diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/OrderCancel.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/OrderCancel.razor new file mode 100644 index 00000000..baf4486e --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/OrderCancel.razor @@ -0,0 +1,136 @@ +@* + EN: Cancel Order Confirmation — Order summary, reason dropdown, notes, refund warning, cancel/keep buttons. + VI: Xác nhận hủy đơn — Tổng quan đơn, lý do hủy, ghi chú, cảnh báo hoàn tiền, nút hủy/giữ đơn. +*@ +@page "/pos/dialog/order-cancel" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Hủy đơn hàng
+
Đơn #DH2024-0589
+
+ +
+ +
+ @* ═══ ORDER SUMMARY / TÓM TẮT ĐƠN HÀNG ═══ *@ +
+
+ Đơn #DH2024-0589 + Bàn 8 · 16:45 +
+ @foreach (var item in _orderItems) + { +
+ x@(item.Qty) @item.Name + @FormatPrice(item.Price * item.Qty) +
+ } +
+ Tổng + @FormatPrice(_orderItems.Sum(i => i.Price * i.Qty)) +
+
+ + @* ═══ REFUND WARNING / CẢNH BÁO HOÀN TIỀN ═══ *@ + @if (_isPaid) + { +
+ +
+
Đơn hàng đã thanh toán
+
+ Hủy đơn này sẽ phát sinh hoàn tiền @FormatPrice(_orderItems.Sum(i => i.Price * i.Qty)) cho khách hàng. + Vui lòng xác nhận với quản lý trước khi tiếp tục. +
+
+
+ } + + @* ═══ CANCELLATION REASON / LÝ DO HỦY ═══ *@ +
+
+ Lý do hủy * +
+ +
+ + @* ═══ NOTES / GHI CHÚ ═══ *@ +
+
Ghi chú thêm
+ +
+
+ + @* ═══ ACTIONS / HÀNH ĐỘNG ═══ *@ +
+ + +
+
+
+ +@code { + // EN: Cancel state / VI: Trạng thái hủy + private string _selectedReason = ""; + private string _note = ""; + private bool _isPaid = true; + + // EN: Cancellation reasons / VI: Lý do hủy + private readonly string[] _reasons = + { + "Khách hủy", "Hết nguyên liệu", "Sai đơn", "Quá lâu", "Khác" + }; + + // EN: Demo order items / VI: Danh sách món mẫu + private readonly List _orderItems = new() + { + new("Cơm tấm sườn bì chả", 65_000, 2), + new("Canh chua cá lóc", 85_000, 1), + new("Chả giò", 40_000, 1), + new("Nước mía", 20_000, 3), + }; + + private void CancelOrder() + { + NavigateTo(""); + } + + private record CancelItem(string Name, decimal Price, int Qty); +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/OrderEdit.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/OrderEdit.razor new file mode 100644 index 00000000..20b2a206 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/OrderEdit.razor @@ -0,0 +1,205 @@ +@* + EN: Edit Existing Order — Editable item list, quantity controls, add items, discount, notes, recalculated total. + VI: Chỉnh sửa đơn hàng — Danh sách món chỉnh sửa, điều khiển số lượng, thêm món, giảm giá, ghi chú, tổng tính lại. +*@ +@page "/pos/dialog/order-edit" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Chỉnh sửa đơn hàng
+
Bàn 5 · 15:20 · Đang phục vụ
+
+ +
+ +
+ @* ═══ ORDER INFO / THÔNG TIN ĐƠN HÀNG ═══ *@ +
+
+
Khách hàng
+
Bàn 5 — 4 khách
+
+
+
Nhân viên
+
Trần Thị B
+
+
+
Trạng thái
+
Đang phục vụ
+
+
+ + @* ═══ EDITABLE ITEM LIST / DANH SÁCH MÓN CHỈNH SỬA ═══ *@ +
Danh sách món
+
+ @foreach (var item in _items) + { +
+
+
@item.Name
+
@FormatPrice(item.Price)
+
+
+ + @item.Qty + +
+ @FormatPrice(item.Price * item.Qty) + +
+ } +
+ + @* ═══ ADD ITEM / THÊM MÓN ═══ *@ +
+
Thêm món
+
+ +
+
+ @foreach (var quick in _quickAddItems) + { + + } +
+
+ + @* ═══ DISCOUNT / GIẢM GIÁ ═══ *@ +
+
Giảm giá
+
+
+ + +
+ +
+
+ + @* ═══ SPECIAL NOTE / GHI CHÚ ĐẶC BIỆT ═══ *@ +
+
Ghi chú đặc biệt
+ +
+
+ + @* ═══ FOOTER — TOTAL & SAVE / CUỐI — TỔNG & LƯU ═══ *@ +
+
+ Tạm tính@FormatPrice(Subtotal) +
+ @if (DiscountAmount > 0) + { +
+ Giảm giá-@FormatPrice(DiscountAmount) +
+ } +
+ Tổng cộng + @FormatPrice(Total) +
+
+ + +
+
+
+
+ +@code { + // EN: Edit state / VI: Trạng thái chỉnh sửa + private string _searchTerm = ""; + private string _discountType = "percent"; + private decimal _discountValue = 10; + private string _specialNote = "Không hành cho món Phở"; + + // EN: Demo order items for "Bàn 5" / VI: Danh sách món mẫu "Bàn 5" + private readonly List _items = new() + { + new("Phở bò tái", 75_000, 2), + new("Cơm tấm sườn bì chả", 65_000, 1), + new("Gỏi cuốn tôm thịt", 45_000, 1), + new("Trà đá", 10_000, 4), + }; + + // EN: Quick-add suggestions / VI: Gợi ý thêm nhanh + private readonly List _quickAddItems = new() + { + new("Chả giò", 40_000), + new("Bánh flan", 25_000), + new("Nước suối", 15_000), + new("Bia Sài Gòn", 25_000), + }; + + private decimal Subtotal => _items.Where(i => i.Qty > 0).Sum(i => i.Price * i.Qty); + + private decimal DiscountAmount => _discountType == "percent" + ? Math.Round(Subtotal * _discountValue / 100) + : Math.Min(_discountValue, Subtotal); + + private decimal Total => Math.Max(0, Subtotal - DiscountAmount); + + private void AddItem(QuickItem quick) + { + var existing = _items.FirstOrDefault(i => i.Name == quick.Name); + if (existing != null) + existing.Qty++; + else + _items.Add(new EditableItem(quick.Name, quick.Price, 1)); + } + + private void SaveChanges() => NavigateTo(""); + + private class EditableItem(string name, decimal price, int qty) + { + public string Name { get; set; } = name; + public decimal Price { get; set; } = price; + public int Qty { get; set; } = qty; + } + + private record QuickItem(string Name, decimal Price); +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/PriceCheck.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/PriceCheck.razor new file mode 100644 index 00000000..9e55db9c --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/PriceCheck.razor @@ -0,0 +1,177 @@ +@* + EN: Price Check — Barcode/SKU input, large price display, product details, promotions, price history. + VI: Kiểm tra giá — Nhập mã vạch/SKU, hiển thị giá lớn, chi tiết sản phẩm, khuyến mãi, lịch sử giá. +*@ +@page "/pos/dialog/price-check" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Kiểm tra giá
+
Tra cứu giá sản phẩm
+
+ +
+ +
+ @* ═══ SEARCH INPUT / Ô TÌM KIẾM ═══ *@ +
+ + + +
+ + @if (_productFound) + { + @* ═══ HERO PRICE / GIÁ NỔI BẬT ═══ *@ +
+
@_productName
+ @if (_hasPromotion) + { +
+ @FormatPrice(_originalPrice) +
+ } +
+ @FormatPrice(_currentPrice) +
+ @if (_hasPromotion) + { +
+ + Giảm 10% +
+ } +
+ + @* ═══ PRODUCT DETAILS / CHI TIẾT SẢN PHẨM ═══ *@ +
+
Thông tin sản phẩm
+
+ @foreach (var detail in _productDetails) + { +
+
@detail.Label
+
@detail.Value
+
+ } +
+
+ + @* ═══ ACTIVE PROMOTIONS / KHUYẾN MÃI ĐANG ÁP DỤNG ═══ *@ + @if (_hasPromotion) + { +
+
Khuyến mãi đang áp dụng
+ @foreach (var promo in _promotions) + { +
+ +
+
@promo.Name
+
@promo.Period
+
+ -@promo.Discount +
+ } +
+ } + + @* ═══ PRICE HISTORY / LỊCH SỬ GIÁ ═══ *@ +
+
Lịch sử giá (3 lần thay đổi gần nhất)
+ @foreach (var history in _priceHistory) + { +
+ +
+ @FormatPrice(history.OldPrice) + + @FormatPrice(history.NewPrice) +
+ @history.Date +
+ } +
+ } +
+ + @* ═══ FOOTER / CUỐI TRANG ═══ *@ +
+ +
+
+
+ +@code { + // EN: Price check state / VI: Trạng thái kiểm tra giá + private string _searchInput = "APL-001"; + private bool _productFound = true; + private string _productName = "Áo polo nam"; + private decimal _originalPrice = 450_000; + private decimal _currentPrice = 405_000; + private bool _hasPromotion = true; + + // EN: Product details / VI: Chi tiết sản phẩm + private readonly List _productDetails = new() + { + new("Tên sản phẩm", "Áo polo nam", "var(--pos-text-primary)"), + new("SKU", "APL-001", "var(--pos-text-primary)"), + new("Danh mục", "Thời trang nam", "var(--pos-text-primary)"), + new("Tồn kho", "156 cái", "var(--pos-success)"), + new("Giá gốc", "450,000₫", "var(--pos-text-primary)"), + new("Giá hiện tại", "405,000₫", "var(--pos-orange-primary)"), + }; + + // EN: Active promotions / VI: Khuyến mãi đang áp dụng + private readonly List _promotions = new() + { + new("Giảm giá mùa hè 2026", "01/02/2026 — 31/03/2026", "10%"), + }; + + // EN: Price history / VI: Lịch sử giá + private readonly List _priceHistory = new() + { + new(500_000, 450_000, "01/01/2026", "trending-down", "var(--pos-success)"), + new(420_000, 500_000, "15/11/2025", "trending-up", "var(--pos-danger)"), + new(450_000, 420_000, "01/09/2025", "trending-down", "var(--pos-success)"), + }; + + private void SearchProduct() + { + _productFound = true; + } + + private record DetailItem(string Label, string Value, string Color); + private record PromoInfo(string Name, string Period, string Discount); + private record PriceHistoryItem(decimal OldPrice, decimal NewPrice, string Date, string Icon, string Color); +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/SplitBill.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/SplitBill.razor new file mode 100644 index 00000000..d066ed0d --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/SplitBill.razor @@ -0,0 +1,235 @@ +@* + EN: Split Bill — Equal split, by-item split, custom split modes for shared bills. + VI: Tách hóa đơn — Chia đều, chia theo món, chia tùy chỉnh cho hóa đơn chung. +*@ +@page "/pos/dialog/split-bill" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Tách hóa đơn
+
Tổng: @FormatPrice(_billTotal) · Bàn 3 · 6 khách
+
+ +
+ + @* ═══ SPLIT MODE TABS / TAB CHẾ ĐỘ CHIA ═══ *@ +
+ @foreach (var mode in _modes) + { + + } +
+ +
+ @if (_activeMode == "equal") + { + @* ═══ EQUAL SPLIT / CHIA ĐỀU ═══ *@ +
+
Số người chia
+
+ @for (var i = 2; i <= 10; i++) + { + var count = i; + + } +
+
+ @FormatPrice(Math.Round(_billTotal / _splitCount)) +
+
mỗi người
+
+ + @* EN: Per-person breakdown / VI: Chi tiết từng người *@ +
+ @for (var i = 1; i <= _splitCount; i++) + { + var personNum = i; +
+
Người @personNum
+
@FormatPrice(Math.Round(_billTotal / _splitCount))
+
+ } +
+ } + else if (_activeMode == "byitem") + { + @* ═══ BY-ITEM SPLIT / CHIA THEO MÓN ═══ *@ +
+ @for (var p = 0; p < 3; p++) + { + var personIdx = p; +
+
+ Người @(personIdx + 1) +
+ @foreach (var item in _billItems.Where(i => i.AssignedTo == personIdx)) + { +
+ @item.Name + @FormatPrice(item.Price) +
+ } +
+ Tổng + @FormatPrice(_billItems.Where(i => i.AssignedTo == personIdx).Sum(i => i.Price)) +
+
+ } +
+ + @* EN: Unassigned items / VI: Món chưa gán *@ + @if (UnassignedItems.Any()) + { +
+
Chưa phân
+ @foreach (var item in UnassignedItems) + { +
+ @item.Name — @FormatPrice(item.Price) +
+ @for (var p = 0; p < 3; p++) + { + var targetPerson = p; + + } +
+
+ } +
+ } + } + else + { + @* ═══ CUSTOM SPLIT / CHIA TÙY CHỈNH ═══ *@ +
+ @for (var i = 0; i < 3; i++) + { + var idx = i; +
+
+ @(idx + 1) +
+
+
Người @(idx + 1)
+ +
+
+ @FormatPrice(_customAmounts[idx]) +
+
+ } +
+ + @* EN: Remaining amount / VI: Số tiền còn lại *@ +
+ + @(CustomRemaining == 0 ? "Đã chia hết!" : "Còn thiếu") + + + @FormatPrice(Math.Abs(CustomRemaining)) + +
+ } +
+ + @* ═══ FOOTER / CUỐI TRANG ═══ *@ +
+ + +
+
+
+ +@code { + // EN: Split state / VI: Trạng thái tách + private string _activeMode = "equal"; + private int _splitCount = 3; + private decimal _billTotal = 850_000; + + // EN: Split mode definitions / VI: Định nghĩa chế độ chia + private readonly List _modes = new() + { + new("equal", "Chia đều"), + new("byitem", "Chia theo món"), + new("custom", "Chia tùy chỉnh"), + }; + + // EN: Bill items for by-item split / VI: Danh sách món để chia theo món + private readonly List _billItems = new() + { + new("Lẩu thái", 250_000, 0), + new("Phở bò tái", 75_000, 0), + new("Cơm tấm sườn", 65_000, 1), + new("Cá kho tộ", 120_000, 1), + new("Gỏi cuốn", 45_000, 2), + new("Chả giò", 40_000, 2), + new("Bia Sài Gòn", 75_000, -1), + new("Trà đá", 40_000, -1), + new("Bánh flan", 50_000, -1), + new("Nước mía", 90_000, -1), + }; + + // EN: Custom amounts / VI: Số tiền tùy chỉnh + private decimal[] _customAmounts = { 300_000, 300_000, 250_000 }; + + // EN: Computed properties for template / VI: Thuộc tính tính toán cho template + private List UnassignedItems => _billItems.Where(i => i.AssignedTo < 0).ToList(); + private decimal CustomRemaining => _billTotal - _customAmounts.Sum(); + + private void GenerateSplitBills() => NavigateTo(""); + + private record SplitMode(string Key, string Label); + + private class SplitItem(string name, decimal price, int assignedTo) + { + public string Name { get; set; } = name; + public decimal Price { get; set; } = price; + public int AssignedTo { get; set; } = assignedTo; + } +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockIn.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockIn.razor new file mode 100644 index 00000000..323a3d66 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockIn.razor @@ -0,0 +1,188 @@ +@* + EN: Quick Stock-In Dialog — Product search, qty, supplier, unit cost, lot, expiry, notes, recent log. + VI: Nhập kho nhanh — Tìm sản phẩm, SL, nhà cung cấp, giá nhập, lô, hạn dùng, ghi chú, lịch sử gần đây. +*@ +@page "/pos/dialog/stock-in" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Nhập kho
+
Nhập hàng hóa vào kho
+
+ +
+ +
+ @* ═══ PRODUCT SEARCH / TÌM SẢN PHẨM ═══ *@ +
+
Sản phẩm
+
+ + +
+ @if (!string.IsNullOrEmpty(_selectedProduct)) + { +
+ +
+
@_selectedProduct
+
SKU: @_selectedSku · Tồn kho: @_currentStock
+
+
+ } +
+ +
+ @* ═══ QUANTITY / SỐ LƯỢNG ═══ *@ +
+
Số lượng nhập
+ +
+ + @* ═══ UNIT COST / GIÁ NHẬP ═══ *@ +
+
Giá nhập (₫/đơn vị)
+ +
+
+ + @* ═══ SUPPLIER / NHÀ CUNG CẤP ═══ *@ +
+
Nhà cung cấp
+ +
+ +
+ @* ═══ LOT / LÔ HÀNG ═══ *@ +
+
Số lô / Batch
+ +
+ + @* ═══ EXPIRY / HẠN DÙNG ═══ *@ +
+
Hạn sử dụng
+ +
+
+ + @* ═══ NOTES / GHI CHÚ ═══ *@ +
+
Ghi chú
+ +
+ + @* ═══ TOTAL COST / TỔNG CHI PHÍ ═══ *@ +
+ Tổng chi phí nhập + @FormatPrice(_quantity * _unitCost) +
+ + @* ═══ RECENT LOG / NHẬT KÝ GẦN ĐÂY ═══ *@ +
+
Nhập kho gần đây
+ @foreach (var log in _recentLogs) + { +
+ +
+ @log.Product + · @log.Qty @log.Unit +
+ @log.Time +
+ } +
+
+ + @* ═══ FOOTER / CUỐI TRANG ═══ *@ +
+ + +
+
+
+ +@code { + // EN: Stock-in state / VI: Trạng thái nhập kho + private string _productSearch = "Cà phê hạt Arabica"; + private string _selectedProduct = "Cà phê hạt Arabica"; + private string _selectedSku = "CF-ARA-500"; + private int _currentStock = 120; + private int _quantity = 50; + private decimal _unitCost = 185_000; + private string _supplier = "Công ty TNHH Cà phê Đà Lạt"; + private string _lotNumber = "LOT-2026-0226"; + private DateTime _expiryDate = new(2027, 2, 26); + private string _notes = ""; + + // EN: Suppliers / VI: Nhà cung cấp + private readonly string[] _suppliers = + { + "Công ty TNHH Cà phê Đà Lạt", + "Nhà phân phối Sài Gòn Food", + "Đại lý nông sản Tây Nguyên", + "Công ty CP Thực phẩm Miền Nam", + }; + + // EN: Recent stock-in log / VI: Nhật ký nhập gần đây + private readonly List _recentLogs = new() + { + new("Cà phê hạt Robusta", 30, "kg", "Hôm nay 09:15"), + new("Sữa tươi TH", 100, "hộp", "Hôm nay 08:30"), + new("Đường trắng", 20, "kg", "Hôm qua 16:00"), + new("Trà ô long", 15, "kg", "Hôm qua 14:20"), + new("Ly giấy 16oz", 500, "cái", "25/02/2026"), + }; + + private void ConfirmStockIn() => NavigateTo(""); + + private record StockLog(string Product, int Qty, string Unit, string Time); +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockOut.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockOut.razor new file mode 100644 index 00000000..d8df99ed --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockOut.razor @@ -0,0 +1,158 @@ +@* + EN: Stock-Out Dialog — Product search, current stock, remove qty, reason, authorization, notes. + VI: Xuất kho — Tìm sản phẩm, tồn kho hiện tại, SL xuất, lý do, xác nhận nhân viên, ghi chú. +*@ +@page "/pos/dialog/stock-out" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Xuất kho
+
Xuất hàng hóa khỏi kho
+
+ +
+ +
+ @* ═══ PRODUCT SEARCH / TÌM SẢN PHẨM ═══ *@ +
+
Sản phẩm
+ +
+ + @* ═══ SELECTED PRODUCT / SẢN PHẨM ĐÃ CHỌN ═══ *@ +
+
+
+
@_selectedProduct
+
SKU: @_selectedSku
+
+
+ @* EN: Current stock level / VI: Mức tồn kho hiện tại *@ +
+
+
Tồn kho
+
@_currentStock
+
@_unit
+
+
+
Sau xuất
+
@(_currentStock - _removeQty)
+
@_unit
+
+
+
+ + @* ═══ QUANTITY TO REMOVE / SỐ LƯỢNG XUẤT ═══ *@ +
+
Số lượng xuất
+
+ + + + @_unit +
+
+ + @* ═══ REASON / LÝ DO ═══ *@ +
+
Lý do xuất kho
+
+ @foreach (var reason in _reasons) + { + + } +
+
+ + @* ═══ AUTHORIZED BY / NHÂN VIÊN XÁC NHẬN ═══ *@ +
+
Nhân viên xác nhận
+ +
+ + @* ═══ NOTES / GHI CHÚ ═══ *@ +
+
Ghi chú
+ +
+
+ + @* ═══ FOOTER / CUỐI TRANG ═══ *@ +
+ + +
+
+
+ +@code { + // EN: Stock-out state / VI: Trạng thái xuất kho + private string _productSearch = "Sữa tươi"; + private string _selectedProduct = "Sữa tươi TH True Milk"; + private string _selectedSku = "STM-1L"; + private int _currentStock = 48; + private string _unit = "hộp"; + private int _removeQty = 5; + private string _selectedReason = "Hư hỏng"; + private string _authorizedBy = "Trần Văn C"; + private string _notes = "5 hộp bị phồng, hạn còn 2 ngày"; + + // EN: Reasons / VI: Lý do + private readonly string[] _reasons = + { + "Hư hỏng", "Hết hạn", "Chuyển kho", "Tiêu thụ nội bộ", "Khác" + }; + + // EN: Staff list / VI: Danh sách nhân viên + private readonly string[] _staffList = + { + "Nguyễn Văn A", "Trần Văn C", "Lê Thị D", "Phạm Minh E" + }; + + private void ConfirmStockOut() => NavigateTo(""); +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockTransfer.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockTransfer.razor new file mode 100644 index 00000000..01a72097 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/StockTransfer.razor @@ -0,0 +1,197 @@ +@* + EN: Stock Transfer — Transfer products between branches with product list, qty, delivery note. + VI: Chuyển kho — Chuyển sản phẩm giữa chi nhánh với danh sách, SL, phiếu giao hàng. +*@ +@page "/pos/dialog/stock-transfer" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Chuyển kho
+
Chuyển hàng giữa các chi nhánh
+
+ +
+ +
+ @* ═══ BRANCH SELECTION / CHỌN CHI NHÁNH ═══ *@ +
+
+
Từ chi nhánh
+ +
+
+ +
+
+
Đến chi nhánh
+ +
+
+ + @* ═══ ADD PRODUCT ROW / THÊM SẢN PHẨM ═══ *@ +
+
Thêm sản phẩm
+
+ + + +
+
+ + @* ═══ TRANSFER LIST / DANH SÁCH CHUYỂN ═══ *@ +
+
Danh sách sản phẩm chuyển
+
+ @* EN: Table header / VI: Tiêu đề bảng *@ +
+ Sản phẩm + Tồn kho + SL chuyển + Giá trị + +
+ @foreach (var item in _transferItems) + { +
+
+
@item.Name
+
@item.Sku
+
+ @item.Stock +
+ +
+ @FormatPrice(item.UnitPrice * item.Qty) + +
+ } +
+
+ + @* ═══ TRANSFER SUMMARY / TÓM TẮT CHUYỂN ═══ *@ +
+
+
Tổng sản phẩm
+
@_transferItems.Count mặt hàng
+
+
+
Tổng số lượng
+
@_transferItems.Sum(i => i.Qty)
+
+
+
Tổng giá trị
+
@FormatPrice(_transferItems.Sum(i => i.UnitPrice * i.Qty))
+
+
+ + @* ═══ DELIVERY NOTE / GHI CHÚ GIAO HÀNG ═══ *@ +
+
Ghi chú giao hàng
+ +
+
+ + @* ═══ FOOTER / CUỐI TRANG ═══ *@ +
+ + +
+
+
+ +@code { + // EN: Transfer state / VI: Trạng thái chuyển kho + private string _fromBranch = "Chi nhánh Q1"; + private string _toBranch = "Chi nhánh Q7"; + private string _addProductSearch = ""; + private int _addQty = 10; + private string _deliveryNote = ""; + + // EN: Branches / VI: Chi nhánh + private readonly string[] _branches = + { + "Chi nhánh Q1", "Chi nhánh Q3", "Chi nhánh Q7", "Chi nhánh Thủ Đức", "Chi nhánh Bình Thạnh" + }; + + // EN: Demo transfer items / VI: Danh sách mẫu + private readonly List _transferItems = new() + { + new("Cà phê hạt Arabica", "CF-ARA-500", 120, 185_000, 20), + new("Trà ô long", "TO-OL-250", 85, 95_000, 15), + new("Sữa tươi TH 1L", "STM-1L", 200, 32_000, 50), + new("Ly giấy 16oz", "LG-16", 1200, 2_500, 300), + }; + + private void AddTransferItem() + { + if (!string.IsNullOrWhiteSpace(_addProductSearch)) + { + _transferItems.Add(new TransferItem(_addProductSearch, "NEW-SKU", 100, 50_000, _addQty)); + _addProductSearch = ""; + _addQty = 10; + } + } + + private void CreateTransfer() => NavigateTo(""); + + private class TransferItem(string name, string sku, int stock, decimal unitPrice, int qty) + { + public string Name { get; set; } = name; + public string Sku { get; set; } = sku; + public int Stock { get; set; } = stock; + public decimal UnitPrice { get; set; } = unitPrice; + public int Qty { get; set; } = qty; + } +} diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/VoidRefund.razor b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/VoidRefund.razor new file mode 100644 index 00000000..01c53945 --- /dev/null +++ b/apps/web-client-tpos-net/src/WebClientTpos.Client/Pages/Pos/Shared/Dialogs/VoidRefund.razor @@ -0,0 +1,198 @@ +@* + EN: Void / Refund Dialog — Order lookup, void vs refund selection, reason, manager PIN, confirm. + VI: Hủy / Hoàn tiền — Tra cứu đơn hàng, chọn hủy hay hoàn tiền, lý do, mã PIN quản lý, xác nhận. +*@ +@page "/pos/dialog/void-refund" +@layout PosLayout +@inherits PosBase + +
+
+ + @* ═══ HEADER / TIÊU ĐỀ ═══ *@ +
+
+ +
+
+
Hủy / Hoàn tiền
+
Void / Refund đơn hàng
+
+ +
+ +
+ @if (!_orderFound) + { + @* ═══ ORDER LOOKUP / TRA CỨU ĐƠN HÀNG ═══ *@ +
+
Nhập mã đơn hàng
+
+ + +
+
+ } + else + { + @* ═══ ORDER DETAILS / CHI TIẾT ĐƠN HÀNG ═══ *@ +
+
+
+ Đơn #@_orderNumber + 14:32 — 26/02/2026 +
+ + Đã thanh toán + +
+ @foreach (var item in _orderItems) + { +
+ x@(item.Qty) @item.Name + @FormatPrice(item.Price * item.Qty) +
+ } +
+ Tổng cộng + @FormatPrice(_orderTotal) +
+
+ Thanh toán: Tiền mặt · PV: Nguyễn Văn A +
+
+ + @* ═══ TYPE SELECTION / CHỌN LOẠI ═══ *@ +
+
Loại xử lý
+
+ + +
+
+ + @* ═══ REASON SELECTION / CHỌN LÝ DO ═══ *@ +
+
Lý do
+
+ @foreach (var reason in _reasons) + { + + } +
+
+ + @if (_type == "refund") + { + @* ═══ REFUND AMOUNT / SỐ TIỀN HOÀN TRẢ ═══ *@ +
+
Số tiền hoàn trả
+
+ + / @FormatPrice(_orderTotal) +
+
+ } + + @* ═══ MANAGER PIN / MÃ PIN QUẢN LÝ ═══ *@ +
+
Mã PIN quản lý
+
+ @for (var i = 0; i < 4; i++) + { + var idx = i; + + } +
+
Yêu cầu xác nhận từ quản lý
+
+ } +
+ + @if (_orderFound) + { + @* ═══ ACTIONS / HÀNH ĐỘNG ═══ *@ +
+ + +
+ } +
+
+ +@code { + // EN: Order lookup state / VI: Trạng thái tra cứu đơn + private string _orderNumber = "DH2024-0567"; + private bool _orderFound = false; + private string _type = "void"; + private string _selectedReason = ""; + private decimal _refundAmount = 285_000; + private decimal _orderTotal = 285_000; + + // EN: Reason options / VI: Các lý do + private readonly string[] _reasons = + { + "Khách yêu cầu", "Sai đơn hàng", "Vấn đề chất lượng", "Khác" + }; + + // EN: Demo order items / VI: Mục đơn hàng mẫu + private readonly List _orderItems = new() + { + new("Phở bò tái", 75_000, 2), + new("Gỏi cuốn tôm thịt", 45_000, 1), + new("Trà đá", 10_000, 3), + new("Bánh flan", 25_000, 2), + }; + + private void LookupOrder() + { + _orderFound = true; + } + + private void ConfirmAction() + { + NavigateTo(""); + } + + private record OrderItem(string Name, decimal Price, int Qty); +}