fix(security,pos): close payment/push/PII gaps from app review
CI/CD / CI · API (dotnet build + test) (push) Successful in 59s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 48s
CI/CD / CI · Koja (tsc) (push) Successful in 52s
CI/CD / Deploy · all services (push) Successful in 2m13s
CI/CD / CI · API (dotnet build + test) (push) Successful in 59s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 48s
CI/CD / CI · Koja (tsc) (push) Successful in 52s
CI/CD / Deploy · all services (push) Successful in 2m13s
- Payments: reject RecordPaymentsAsync when the order is already Delivered/
Cancelled (ORDER_ALREADY_CLOSED) — prevents duplicate payments, double loyalty
earn, and overstated cash drawer from a double-tap or paying a reopened order.
- Push broadcast: POST /api/push/broadcast was [Authorize]-only (any user → any
topic, platform-wide). Now requires SendSms + café context and is forced to the
caller's own topic (cafe-{slug}); arbitrary/cross-café topics rejected.
- HR reads: GetEmployees/GetAttendance/GetShifts now require ViewStaff/
ViewAttendance/ViewSchedules (were café-access-only, leaking roster PII the UI
already hid). Expenses list now requires ViewExpenses.
- Receipt: removed the auto-print on full payment so the POS success sheet is the
single print path (no more double receipt).
Local build blocked by NU1301 (NuGet network unreachable); CI builds via mirror.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1039,6 +1039,12 @@ public class OrderService : IOrderService
|
||||
if (order is null)
|
||||
return new OrderServiceResult<IReadOnlyList<PaymentDto>>(false, null, "ORDER_NOT_FOUND");
|
||||
|
||||
// Never take payment on an already-closed order — a double-tap on Pay, or
|
||||
// paying a closed order reopened from the board, would otherwise record
|
||||
// duplicate payments, re-earn loyalty, reprint, and overstate the drawer.
|
||||
if (order.Status is OrderStatus.Delivered or OrderStatus.Cancelled)
|
||||
return new OrderServiceResult<IReadOnlyList<PaymentDto>>(false, null, "ORDER_ALREADY_CLOSED");
|
||||
|
||||
var branchId = await ResolveOrderBranchIdAsync(order, cafeId, cancellationToken);
|
||||
if (string.IsNullOrEmpty(branchId))
|
||||
return new OrderServiceResult<IReadOnlyList<PaymentDto>>(false, null, "NO_OPEN_SHIFT", "branchId");
|
||||
@@ -1125,7 +1131,8 @@ public class OrderService : IOrderService
|
||||
|
||||
if (paidTotal >= order.Total)
|
||||
{
|
||||
PrinterBackgroundJobs.QueueReceiptPrint(_scopeFactory, cafeId, orderId);
|
||||
// Receipt is printed explicitly from the POS success sheet (single
|
||||
// print path) — no auto-print here, to avoid a duplicate receipt.
|
||||
await _loyalty.ApplyEarnOnOrderPaidAsync(cafeId, order.CustomerId, paidTotal, cancellationToken);
|
||||
await _deliverySync.SyncInternalStatusAsync(cafeId, orderId, OrderStatus.Delivered, cancellationToken);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user