// VMAX POS — payment, the attribution-at-print flow (the system's core mechanic), // and the separate audited refund + remake flows. (function () { const DS = window.VMAX365DesignSystem_0f78b2; const { Button, Badge, SegmentedControl } = DS; const { Icon, Sheet, Overline } = window.VMAXUI; const { Avatar, PinPad, Dots, pushToast, EmptyState, Select, TempTag } = window.VMAXW; const { K, tenders, remakeReasons } = window.VMAXP; const { useVMAX } = window.VMAXStore; const TempTagDS = DS.TempTag; const DENOMS = [10, 20, 50, 100, 200]; // ---------- Payment + attribution at print ---------- function PaymentSheet({ open, total, session, onClose, onDone }) { const [s, a] = useVMAX(); const [tender, setTender] = React.useState('Cash'); const [received, setReceived] = React.useState(0); const [step, setStep] = React.useState('pay'); // pay | names | pin | done const [pinFor, setPinFor] = React.useState(null); const [pin, setPin] = React.useState(''); const [err, setErr] = React.useState(false); const [attrib, setAttrib] = React.useState(''); React.useEffect(() => { if (open) { setTender('Cash'); setReceived(0); setStep('pay'); setPin(''); setErr(false); } }, [open]); const change = Math.max(0, received - total); const noPin = !!(s.settings && s.settings.pinForPrint === false); const finish = (name) => { a.takePayment(tender, name); session.markFresh(); setAttrib(name); setStep('done'); }; const tapName = (p) => { if (noPin) { session.setActive(p); finish(p.name); } else if (session.active && p.id === session.active.id && !session.stale) finish(p.name); else { setPinFor(p); setPin(''); setErr(false); setStep('pin'); } }; React.useEffect(() => { if (step === 'pin' && pin.length === 4) { if (session.verify(pinFor.id, pin)) { session.setActive(pinFor); finish(pinFor.name); } else { setErr(true); setTimeout(() => { setErr(false); setPin(''); }, 480); } } }, [pin]); if (!open) return null; if (step === 'done') return ( {}} width={420}>
Paid
{tender}{tender === 'Cash' && change > 0 ? ` · change ${K(change)}` : ''}
Receipt attributed to {attrib}
); if (step === 'names' || step === 'pin') { const roster = session.people.filter((p) => p.roles.some((r) => r === 'cashier' || r === 'maker' || r === 'owner') && p.pin); const signedIds = session.signedIn.map((p) => p.id); const primary = session.signedIn; const others = roster.filter((p) => !signedIds.includes(p.id)); return ( {step === 'names' ? (
Who's printing this?
Tap your name — this is what the order is attributed to.
{session.stale &&
Till was idle — confirm your PIN to verify it's you.
} Signed in now
{primary.map((p) => { const isActive = session.active && p.id === session.active.id; return ( ); })}
{others.length > 0 && Someone else stepping in
{others.map((p) => )}
}
) : (
{pinFor.name}'s PIN
Confirm the switch — this order will be attributed to {pinFor.name.split(' ')[0]}.
)}
); } // step 'pay' return (
Amount due
{K(total)}
Tender
{tenders.map((t) => ( ))}
{tender === 'Cash' && (
Cash received
{DENOMS.map((d) => )}
Received {K(received)}
Change to give
0 ? 'var(--vmax-red)' : 'var(--vmax-ink)' }}>{K(change)}
)}
); } // ---------- Recent orders → refund / remake ---------- function OrdersSheet({ open, onClose, session }) { const [s] = useVMAX(); const [refund, setRefund] = React.useState(null); const [remake, setRemake] = React.useState(null); const orders = [...s.sales].slice(0, 12); return (
Recent orders
{orders.length === 0 ? : orders.map((o) => (
#{o.id} · {o.at} · {o.by}
{K(o.amount)}
{o.lines.map((l) => `${l.qty}× ${l.name}`).join(' · ')} · {o.tender}
))}
{refund && setRefund(null)} />} {remake && setRemake(null)} />}
); } function RefundFlow({ order, session, onClose }) { const [, a] = useVMAX(); const [picked, setPicked] = React.useState(order.lines.map((_, i) => i)); const [made, setMade] = React.useState(null); // true=made&discarded, false=not made const amount = order.lines.filter((_, i) => picked.includes(i)).reduce((x, l) => x + l.price * l.qty, 0); const toggle = (i) => setPicked((p) => p.includes(i) ? p.filter((x) => x !== i) : [...p, i]); const confirm = () => { a.refund(order.id, picked, made, session.active.name); pushToast('Refund recorded · ' + K(amount), 'ok'); onClose(); }; return (
Refund #{order.id}
Money returns. This writes its own attributed record.
What's being refunded
{order.lines.map((l, i) => ( ))}
Was it made?
setMade(false)} title="Not made" sub="Ingredients return to stock" /> setMade(true)} title="Made & discarded" sub="Recorded as consumed" />
{K(amount)}
); } function RemakeFlow({ order, session, onClose }) { const [, a] = useVMAX(); const [lineIdx, setLineIdx] = React.useState(0); const [reason, setReason] = React.useState(''); const confirm = () => { a.remake(order.id, lineIdx, reason, session.active.name); pushToast('Remake recorded · ' + order.lines[lineIdx].name, 'ok'); onClose(); }; return (
Remake from #{order.id}
No money moves — but stock is consumed again. That's why it's logged separately.
Which drink
{order.lines.map((l, i) => ( ))}
Reason
{remakeReasons.map((r) => )}
); } function ChoiceBtn({ on, onClick, title, sub }) { return ( ); } window.VMAXPAY = { PaymentSheet, OrdersSheet }; })();