From 15f36c3a7e4ed4b6fc2ad519e3f06d65477eabbe Mon Sep 17 00:00:00 2001 From: Deploy Date: Wed, 26 Aug 2026 22:24:14 +0200 Subject: [PATCH] fix(pos-selfpay): use confirmSaleWith to avoid stale state when confirming from modal --- project/apps/pos/src/app/(terminal)/page.tsx | 63 +++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/project/apps/pos/src/app/(terminal)/page.tsx b/project/apps/pos/src/app/(terminal)/page.tsx index 61a64fd..5d3bb39 100644 --- a/project/apps/pos/src/app/(terminal)/page.tsx +++ b/project/apps/pos/src/app/(terminal)/page.tsx @@ -525,6 +525,66 @@ export default function RegisterPage() { } }; + // POS-SELFPAY-FLOW: confirm sale with explicit payment list (avoids stale state in selfpay modal) + const confirmSaleWith = async (allocations: PaymentAllocation[]) => { + if (!config?.session) { + setError('No hay sesión abierta'); + return; + } + if (cart.length === 0) { + setError('Carrito vacío'); + return; + } + const totalAllocated = allocations.reduce((sum, a) => sum + a.amountCents, 0); + if (totalAllocated > totals.total) { + setError('Los pagos asignados superan el total'); + return; + } + setPayments(allocations); + setProcessing(true); + setError(''); + try { + const saleItems = cart.map((item) => { + if (item.kind === 'free' || !item.variantId) { + return { + kind: 'free' as const, + name: item.name, + unitPriceCents: item.unitPriceCents, + quantity: item.quantity, + }; + } + return { + kind: 'stock' as const, + variantId: item.variantId, + quantity: item.quantity, + discountCents: item.discountCents, + }; + }); + const result = await posApi.createSale({ + idempotencyKey: generateIdempotencyKey(), + cashSessionId: config.session.id, + terminalId: config.terminal.id, + items: saleItems, + payments: allocations.map((payment) => ({ + methodCode: payment.methodCode, + amountCents: payment.amountCents, + ...(payment.kind === 'cash' ? { tenderedCents: payment.tenderedCents } : {}), + })), + ...(customer ? { customerId: customer.id } : {}), + }); + setReceipt(result.receipt); + setRestPaymentFor(null); + if (isSelfpayMode && selfpayEmail.trim()) { + void posApi.emailReceipt(result.receipt.orderId, selfpayEmail.trim()).catch(() => {}); + } + void loadPendingSales(); + } catch (err) { + setError(err instanceof Error ? err.message : 'No se pudo confirmar la venta'); + } finally { + setProcessing(false); + } + }; + const parkSale = async () => { if (!config?.session) { setError('No hay sesión abierta'); @@ -1857,8 +1917,7 @@ export default function RegisterPage() { ...(method.kind === 'cash' ? { tenderedCents: remainingCents } : {}), changeCents: 0, }; - setPayments([allocation]); - void confirmSale(); + void confirmSaleWith([allocation]); }} disabled={remainingCents === 0} className={`min-h-16 rounded-xl px-2 font-bold text-white disabled:opacity-40 ${method.kind === 'cash' ? 'bg-green-600' : method.kind === 'card' ? 'bg-blue-600' : 'bg-slate-700'}`}