feat(pos): implement order payment flow and update order aggregate status handling.
This commit is contained in:
@@ -435,6 +435,7 @@
|
||||
|
||||
// ═══ SENT ITEMS (per table — items sent to kitchen, not yet paid) ═══
|
||||
private readonly Dictionary<string, List<SentItem>> _tableOrders = new();
|
||||
private readonly Dictionary<string, List<Guid>> _tableOrderIds = new();
|
||||
private bool _sendingToKitchen;
|
||||
private string? _kitchenMessage;
|
||||
private bool _kitchenSuccess;
|
||||
@@ -684,10 +685,17 @@
|
||||
_lastReceiptItems = GetSentItemsForTable(SelectedTable).ToList();
|
||||
_lastTransactionId = $"POS-{DateTime.Now:yyyyMMdd}-{DateTime.Now:HHmmss}";
|
||||
|
||||
// Pay all orders for this table in the backend
|
||||
if (_tableOrderIds.TryGetValue(SelectedTable.Id, out var orderIds))
|
||||
{
|
||||
foreach (var orderId in orderIds)
|
||||
await DataService.PayOrderAsync(orderId, ShopId);
|
||||
}
|
||||
|
||||
_paymentProcessing = false;
|
||||
_paymentStep = PayStep.Success;
|
||||
|
||||
// Reload table orders from DB (the paid order will no longer be Validated)
|
||||
// Reload table orders from DB (paid orders no longer Validated → removed)
|
||||
await LoadTableOrdersFromDb();
|
||||
await SaveStateToLocalStorage();
|
||||
StateHasChanged();
|
||||
@@ -745,12 +753,16 @@
|
||||
{
|
||||
var activeOrders = await DataService.GetActiveTableOrdersAsync(ShopId);
|
||||
_tableOrders.Clear();
|
||||
_tableOrderIds.Clear();
|
||||
foreach (var order in activeOrders)
|
||||
{
|
||||
if (order.TableId == null) continue;
|
||||
var tableKey = order.TableId.Value.ToString();
|
||||
if (!_tableOrders.ContainsKey(tableKey))
|
||||
_tableOrders[tableKey] = new();
|
||||
if (!_tableOrderIds.ContainsKey(tableKey))
|
||||
_tableOrderIds[tableKey] = new();
|
||||
_tableOrderIds[tableKey].Add(order.OrderId);
|
||||
foreach (var item in order.Items)
|
||||
{
|
||||
var existing = _tableOrders[tableKey].FirstOrDefault(s => s.ProductId == item.ProductId);
|
||||
|
||||
@@ -627,6 +627,15 @@ public class PosDataService
|
||||
return null;
|
||||
}
|
||||
|
||||
// ═══ PAY ORDER ═══
|
||||
|
||||
public async Task<bool> PayOrderAsync(Guid orderId, Guid shopId)
|
||||
{
|
||||
AttachToken();
|
||||
var resp = await _http.PostAsJsonAsync($"api/bff/orders/{orderId}/pay?shopId={shopId}", new { }, _writeOptions);
|
||||
return resp.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
// ═══ ACTIVE TABLE ORDERS ═══
|
||||
|
||||
// EN: DTOs for active table orders (orders with table_id, status=Validated)
|
||||
|
||||
@@ -113,6 +113,17 @@ public class OrderController : ControllerBase
|
||||
new { reason = "Cancelled from POS" }).ProxyAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: Pay an order.
|
||||
/// VI: Thanh toán đơn hàng.
|
||||
/// </summary>
|
||||
[HttpPost("orders/{orderId:guid}/pay")]
|
||||
public Task<IActionResult> PayOrder(Guid orderId, [FromQuery] Guid? shopId = null)
|
||||
{
|
||||
var qs = shopId.HasValue ? $"?shopId={shopId}" : "";
|
||||
return _order.PostAsJsonAsync($"/api/v1/orders/{orderId}/pay{qs}", new { }).ProxyAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EN: Create a POS order. Enriches items with correct productType based on shop category.
|
||||
/// VI: Tạo đơn POS. Bổ sung productType chính xác cho items dựa trên loại shop.
|
||||
|
||||
@@ -46,10 +46,10 @@ public class Order : Entity, IAggregateRoot
|
||||
public Guid? TableId => _tableId;
|
||||
|
||||
/// <summary>
|
||||
/// EN: Order status.
|
||||
/// VI: Trạng thái đơn hàng.
|
||||
/// EN: Order status (resolved from StatusId when loaded from DB).
|
||||
/// VI: Trạng thái đơn hàng (resolve từ StatusId khi load từ DB).
|
||||
/// </summary>
|
||||
public OrderStatus Status => _status;
|
||||
public OrderStatus Status => _status ?? Enumeration.FromValue<OrderStatus>(StatusId);
|
||||
|
||||
/// <summary>
|
||||
/// EN: Status ID for EF Core mapping.
|
||||
@@ -134,8 +134,8 @@ public class Order : Entity, IAggregateRoot
|
||||
/// </summary>
|
||||
public void MarkAsValidated()
|
||||
{
|
||||
if (_status != OrderStatus.Draft)
|
||||
throw new DomainException($"Cannot validate order with status {_status.Name}");
|
||||
if (Status != OrderStatus.Draft)
|
||||
throw new DomainException($"Cannot validate order with status {Status.Name}");
|
||||
if (!_items.Any())
|
||||
throw new DomainException("Cannot validate order with no items");
|
||||
|
||||
@@ -150,8 +150,8 @@ public class Order : Entity, IAggregateRoot
|
||||
/// </summary>
|
||||
public void MarkAsPaid()
|
||||
{
|
||||
if (_status != OrderStatus.Validated)
|
||||
throw new DomainException($"Cannot mark as paid order with status {_status.Name}");
|
||||
if (Status != OrderStatus.Validated)
|
||||
throw new DomainException($"Cannot mark as paid order with status {Status.Name}");
|
||||
|
||||
_status = OrderStatus.Paid;
|
||||
StatusId = OrderStatus.Paid.Id;
|
||||
@@ -166,8 +166,8 @@ public class Order : Entity, IAggregateRoot
|
||||
/// </summary>
|
||||
public void MarkAsProcessing()
|
||||
{
|
||||
if (_status != OrderStatus.Paid)
|
||||
throw new DomainException($"Cannot process order with status {_status.Name}");
|
||||
if (Status != OrderStatus.Paid)
|
||||
throw new DomainException($"Cannot process order with status {Status.Name}");
|
||||
|
||||
_status = OrderStatus.Processing;
|
||||
StatusId = OrderStatus.Processing.Id;
|
||||
@@ -180,8 +180,8 @@ public class Order : Entity, IAggregateRoot
|
||||
/// </summary>
|
||||
public void MarkAsCompleted()
|
||||
{
|
||||
if (_status != OrderStatus.Processing)
|
||||
throw new DomainException($"Cannot complete order with status {_status.Name}");
|
||||
if (Status != OrderStatus.Processing)
|
||||
throw new DomainException($"Cannot complete order with status {Status.Name}");
|
||||
|
||||
_status = OrderStatus.Completed;
|
||||
StatusId = OrderStatus.Completed.Id;
|
||||
@@ -196,9 +196,9 @@ public class Order : Entity, IAggregateRoot
|
||||
/// </summary>
|
||||
public void Cancel(string reason)
|
||||
{
|
||||
if (_status == OrderStatus.Completed)
|
||||
if (Status == OrderStatus.Completed)
|
||||
throw new DomainException("Cannot cancel completed order");
|
||||
if (_status == OrderStatus.Cancelled)
|
||||
if (Status == OrderStatus.Cancelled)
|
||||
throw new DomainException("Order is already cancelled");
|
||||
|
||||
_status = OrderStatus.Cancelled;
|
||||
|
||||
Reference in New Issue
Block a user