// VMAX Store Manager — Receiving, Counts, Catalogue (functional bulk entry + correction/revert), People.
(function () {
const DS = window.VMAX365DesignSystem_0f78b2;
const { Button, Badge, SegmentedControl, StockBadge } = DS;
const { Icon, Sheet, Overline } = window.VMAXUI;
const { Avatar, Field, TextInput, NumberInput, Select, Toggle, EmptyState, pushToast } = window.VMAXW;
const { K, reasonCodes } = window.VMAXP;
const { useVMAX, statusOf } = window.VMAXStore;
const { Panel } = window.VMAXMGRUI;
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' };
// ============ Receiving (receive-and-note, not a PO match) ============
function Receiving({ mobile, who }) {
const [s, a] = useVMAX();
const inv = s.ingredients;
const name = (id) => (inv.find((x) => x.id === id) || {}).name;
const [picked, setPicked] = React.useState([]);
const [note, setNote] = React.useState('');
const [addId, setAddId] = React.useState(inv[0] && inv[0].id);
const add = () => setPicked((p) => p.find((x) => x.id === addId) ? p : [...p, { id: addId, qty: 6 }]);
const confirm = () => { a.receiveStock(picked, note, who); setPicked([]); setNote(''); pushToast('Received · added to back store'); };
return (
Back store}>
Enter what physically arrived from the warehouse — this isn't matched against an order. If something's off, note it.
{picked.map((p, idx) => (
{name(p.id)}
{p.qty}
{(inv.find((x) => x.id === p.id) || {}).unit}
))}
{picked.length === 0 &&
Add the items on the dispatch note.
}
{s.receipts.length === 0 &&
No deliveries logged yet.
}
{s.receipts.map((r) => (
{r.items.map((it) => `${it.qty}× ${name(it.id)}`).join(' · ')}
{r.note &&
{r.note}
}
))}
);
}
// ============ Counts (spot + full) ============
function Counts({ mobile, who }) {
const [s, a] = useVMAX();
const [kind, setKind] = React.useState('spot');
const [loc, setLoc] = React.useState('front');
const [picked, setPicked] = React.useState(['coldcup', 'choc']);
const [vals, setVals] = React.useState({});
const [reasons, setReasons] = React.useState({});
const inv = s.ingredients;
const level = (it) => (loc === 'back' ? it.back : it.front);
const items = kind === 'full' ? inv : inv.filter((i) => picked.includes(i.id));
const lines = items.map((it) => { const counted = vals[it.id]; return { id: it.id, expected: level(it), counted: counted === '' || counted == null ? null : Number(counted), reason: reasons[it.id] || '' }; });
const varianced = lines.filter((l) => l.counted != null && l.counted !== l.expected);
const openV = varianced.filter((l) => !l.reason);
const save = () => { a.saveCount(kind, loc, lines.filter((l) => l.counted != null), who); setVals({}); setReasons({}); pushToast((kind === 'spot' ? 'Spot check' : 'Full count') + ' saved · ' + varianced.length + ' variance' + (varianced.length === 1 ? '' : 's')); };
return (
}>
Enter what you count. A gap doesn't overwrite silently — it's raised as a variance with a reason, the same logic as the close.
{kind === 'spot' && (
Items to verify
{inv.map((i) => { const on = picked.includes(i.id); return ; })}
)}
{items.length === 0 &&
Pick items above to verify.
}
{items.map((it) => {
const counted = vals[it.id]; const exp = level(it);
const diff = counted === '' || counted == null ? null : Number(counted) - exp;
return (
{it.name}
System expects {exp} {it.unit}
setVals({ ...vals, [it.id]: v })} suffix={it.unit} width={50} />
{diff != null && (diff === 0 ? ✓ Matches : (
{diff > 0 ? '+' : ''}{diff}
))}
);
})}
{lines.filter((l) => l.counted != null).length} entered · {varianced.length} variance{varianced.length === 1 ? '' : 's'}{openV.length ? ` · ${openV.length} need a reason` : ''}
{s.counts.length === 0 &&
No counts yet today.
}
{s.counts.map((c) => (
#{c.id} · {c.kind === 'spot' ? 'Spot' : 'Full'} · {c.location}
{c.when} · {c.by} · {c.lines.length} items{c.reverted ? ' · reverted' : ''}
{c.resolved ? 'matched' : 'variance'}
{!c.reverted &&
}
))}
);
}
window.VMAXMGR2 = { Receiving, Counts };
})();