fix(operations-service): close orders after full payment

This commit is contained in:
José René White Enciso 2026-03-31 20:21:41 -06:00
parent 1e86d2f05b
commit 4bd088e2b1
3 changed files with 24 additions and 1 deletions

View File

@ -29,13 +29,14 @@ That means:
- customer detail and history no longer need to be projected from the status summary payload
- POS summary reads only served checks that remain payable
- POS detail can resolve a specific check directly from the shared lifecycle store
- payment capture updates persisted check state and appends lifecycle events
- payment capture updates persisted check state, appends lifecycle events, and closes the customer-facing order lifecycle on full payment
## Contract Intent
- waiter assignments surface floor-facing table attention derived from shared order/check state
- customer status, detail, and history all reflect the same lifecycle that waiter and POS flows observe
- POS payment only opens for served orders with outstanding balance
- a fully paid check now advances customer-facing order state beyond `served`, which keeps order detail aligned with successful POS capture
- POS detail stays lifecycle-backed even when the check is not yet payable, which keeps downstream error handling honest
- restaurant-admin configuration remains control-plane oriented and intentionally separate from order persistence

View File

@ -265,10 +265,12 @@ public sealed class DefaultOperationsWorkflowPort : IOperationsWorkflowPort
var nowUtc = DateTime.UtcNow;
var remainingBalance = decimal.Max(record.OutstandingBalance - request.Amount, 0m);
var nextCheckState = remainingBalance == 0m ? "Paid" : "AwaitingPayment";
var nextOrderState = remainingBalance == 0m ? "Paid" : record.OrderState;
// Partial captures keep the check open for the remaining balance; full captures close the check cleanly.
var updated = record with
{
OrderState = nextOrderState,
CheckState = nextCheckState,
OutstandingBalance = remainingBalance,
UpdatedAtUtc = nowUtc

View File

@ -128,10 +128,30 @@ public class OperationsWorkflowUseCasesTests
Assert.True(response.Succeeded);
Assert.Equal("captured", response.Status);
Assert.NotNull(updated);
Assert.Equal("Paid", updated!.OrderState);
Assert.Equal("Paid", updated!.CheckState);
Assert.Equal(0m, updated.OutstandingBalance);
}
[Fact]
public async Task CapturePosPaymentUseCase_WhenPaymentIsPartial_KeepsOrderServed()
{
var useCase = new CapturePosPaymentUseCase(workflowPort);
var response = await useCase.HandleAsync(
new CapturePosPaymentRequest("demo-context", "CHK-1002", 12.50m, "USD", "card"),
CancellationToken.None);
var updated = await lifecycleStore.GetOrderAsync("demo-context", "ORD-1002", CancellationToken.None);
Assert.True(response.Succeeded);
Assert.Equal("partial", response.Status);
Assert.NotNull(updated);
Assert.Equal("Served", updated!.OrderState);
Assert.Equal("AwaitingPayment", updated.CheckState);
Assert.Equal(25.00m, updated.OutstandingBalance);
}
[Fact]
public async Task CapturePosPaymentUseCase_WhenOrderNotServed_ReturnsBlockedStatus()
{