// VMAX POS — extras: stock-request sheet (front → back) and the cashier cash-up. (function () { const DS = window.VMAX365DesignSystem_0f78b2; const { Button, Badge } = DS; const { Icon, Sheet, Overline } = window.VMAXUI; const { NumberInput, Field, Toggle, pushToast, EmptyState } = window.VMAXW; const { K, mobileChannels } = window.VMAXP; const { useVMAX, lowStock, statusOf } = window.VMAXStore; const qbtn = { width: 28, height: 28, borderRadius: 7, border: '1.5px solid var(--vmax-line)', background: 'var(--surface-card)', cursor: 'pointer', fontWeight: 700, color: 'var(--vmax-ink-soft)', display: 'grid', placeItems: 'center' }; // ---------- Stock request (front staff ask the back store) ---------- function RequestStockSheet({ open, onClose, who, preset }) { const [s, a] = useVMAX(); const inv = s.ingredients; const name = (id) => (inv.find((x) => x.id === id) || {}).name; const [picked, setPicked] = React.useState([]); const [urgent, setUrgent] = React.useState(false); React.useEffect(() => { if (open) { setPicked(preset ? [{ id: preset, qty: 1 }] : []); setUrgent(false); } }, [open, preset]); // quick-add: the low or out front items first, then common ones const low = lowStock(s).map((i) => i.id); const quick = [...new Set([...low, 'choc', 'coldcup', 'syrup', 'coconut', 'beans'])].filter((id) => inv.find((x) => x.id === id)).slice(0, 6); const submit = () => { a.submitRequest(picked, urgent, who); pushToast('Request sent to back store'); onClose(); }; return (
Request stock
front → back

Ask the back store to top up the counter. Fewest taps — it replaces a WhatsApp message.

Quick add
{quick.map((id) => { const it = inv.find((x) => x.id === id); const st = statusOf(it); return ; })}
Requesting
{picked.map((p, idx) => (
{name(p.id)} {p.qty}
))} {picked.length === 0 &&
Tap an item above to add it.
}
); } // ---------- Cashier cash-up (shift handover record; not the manager's accountable close) ---------- function CashierClose({ open, onClose, who }) { const [s, a] = useVMAX(); const exp = s.tally.cash; const expMobile = s.tally.mobile || {}; const [counted, setCounted] = React.useState(''); const [cm, setCm] = React.useState({}); const [note, setNote] = React.useState(''); const [done, setDone] = React.useState(false); React.useEffect(() => { if (open) { setCounted(''); setCm(mobileChannels.reduce((o, ch) => ({ ...o, [ch]: (s.tally.mobile || {})[ch] || 0 }), {})); setNote(''); setDone(false); } }, [open]); const diff = counted === '' ? null : Number(counted) - exp; const mobileTotal = mobileChannels.reduce((acc, ch) => acc + (Number(cm[ch]) || 0), 0); if (!open) return null; if (done) return (
Cash-up recorded

Counted {K(Number(counted) || 0)} cash · {K(mobileTotal)} mobile. Attributed to {who} — the manager runs the accountable end-of-day close.

); return (
Cash up · your shift

Count the drawer at handover. This is a record, not the day's close — the manager reconciles the full day.

System expects (cash sales)
{K(exp)}
{diff != null &&
{diff === 0 ? '✓ Matches the recorded sales' : `${diff < 0 ? '−' : '+'}${K(Math.abs(diff))} vs recorded — the close will ask for a reason`}
}
Mobile money
{mobileChannels.map((ch) => { const e = expMobile[ch] || 0; const cv = cm[ch]; const d = cv == null || cv === '' ? null : Number(cv) - e; return (
{ch}
expects {K(e)}
{d === 0 && } {d != null && d !== 0 && {d < 0 ? '−' : '+'}{K(Math.abs(d))}} setCm({ ...cm, [ch]: v })} prefix="K" width={68} />
); })}
setNote(e.target.value)} placeholder="Anything to flag for the manager" style={{ width: '100%', border: '1.5px solid var(--vmax-line)', borderRadius: 'var(--radius-md)', padding: '11px 13px', fontFamily: 'var(--font-body)', fontSize: 14.5, outline: 'none', background: 'var(--surface-card)' }} />
); } window.VMAXEXTRA = { RequestStockSheet, CashierClose }; })();