/* ============================================================= CoreIQ Office — Receiving: worklist, Receive Order, product history Uses shared bare globals: OD, oMoney, findP, findS, OIcon, Screen, KpiCard, StatusPill, useOffice. Registers 3 screens. ============================================================= */ const { useState: rcvUseState, useMemo: rcvUseMemo, useEffect: rcvUseEffect } = React; function rcvSync() { const [, s] = rcvUseState(0); rcvUseEffect(() => window.RCV.subscribe(() => s(n => n + 1)), []); } function rcvCtn(l) { return l.ord; } // cost movement cell: recCost vs current cost function RcvMove({ l }) { const up = l.recCost > l.cost + 0.001; const down = l.recCost < l.cost - 0.001; const cls = up ? "rcv-move--up" : down ? "rcv-move--down" : "rcv-move--flat"; return ( {up && }{down && } {oMoney(l.recCost)} ); } // ================================================================= // RECEIVING WORKLIST // ================================================================= function ReceivingWorklist() { const { setScreen, setScreenState } = window.useOffice(); rcvSync(); const R = window.RCV; const [filter, setFilter] = rcvUseState("active"); const list = R.deliveries(); const FILTERS = [ { k: "active", l: "To receive", pred: d => ["awaiting", "invoice-attached", "part-received"].includes(d.status) }, { k: "awaiting", l: "Awaiting", pred: d => d.status === "awaiting" }, { k: "part-received", l: "Part received", pred: d => d.status === "part-received" }, { k: "received", l: "Received", pred: d => d.status === "received" }, { k: "all", l: "All", pred: () => true }, ]; const shown = list.filter(FILTERS.find(f => f.k === filter).pred); const toReceive = list.filter(d => ["awaiting", "invoice-attached", "part-received"].includes(d.status)); const totalUnits = toReceive.reduce((s, d) => s + d.lines.reduce((a, l) => a + l.ord * l.ctn, 0), 0); const totalValue = toReceive.reduce((s, d) => s + (R.totals(d.id)?.inc || 0), 0); function open(id) { setScreenState(s => ({ ...s, rcvDeliveryId: id })); setScreen("receiveOrder"); } return ( }>
d.status === "awaiting").length + " not yet arrived" }}/> s + d.lines.length, 0)} delta={{ text: "across " + toReceive.length + " orders" }}/>
{FILTERS.map(f => ( ))}
Order
Supplier · invoice
Notes
Store · totes
Lines
Units
Inc GST
Status
{shown.map(d => { const t = R.totals(d.id); const sm = R.statusMeta(d.status); const units = d.lines.reduce((a, l) => a + l.rec * l.ctn, 0); return (
open(d.id)}>
{d.id}
{d.po}
{R.WS[d.ws]}
Inv {d.invoice}
{d.notes}
{findS(d.store)?.code} · {d.totes}T
{d.lines.length}
{units}
{oMoney(t?.inc || 0)}
); })}
); } // ================================================================= // RECEIVE ORDER (interactive goods-in) // ================================================================= function ReceiveOrder() { const { screen, screenState, setScreen, setScreenState, pushToast } = window.useOffice(); rcvSync(); const R = window.RCV; const id = screenState.rcvDeliveryId || R.deliveries()[0].id; const d = R.delivery(id); const [adv, setAdv] = rcvUseState(window.RCV_TWEAKS?.advancedCols ?? false); const [sel, setSel] = rcvUseState(0); rcvUseEffect(() => { setAdv(window.RCV_TWEAKS?.advancedCols ?? false); }, [window.RCV_TWEAKS?.advancedCols]); if (!d) return
Delivery not found
; const t = R.totals(id); const declared = R.invoiceDeclared(id); const variance = Math.round((t.inc - declared) * 100) / 100; const sm = R.statusMeta(d.status); const done = d.status === "received"; // grid template: with/without advanced cost columns const cols = adv ? "30px 130px minmax(160px,1.4fr) 56px 70px 56px 96px 90px 80px 80px 64px 48px 90px 90px" : "30px 130px minmax(180px,1.6fr) 56px 78px 60px 100px 70px 56px 96px 96px"; function recOf(i) { return d.lines[i].rec; } function doReceive(selectedOnly) { R.receive(id); pushToast({ kind: "paid", icon: "check-circle", title: "Order received · " + id, meta: d.lines.length + " lines · " + oMoney(t.inc) + " inc GST" }); } const HeadCells = adv ? ["", "PDE # / barcode", "Name", "Ord", "Rec", "SOH", "Rec Ex", "Exp Cost", "WS1", "Last", "Cost", "M/Up", "GST · Sell", "Total"] : ["", "PDE # / barcode", "Name", "Ord", "Rec", "SOH", "Rec cost", "M/Up", "GST", "Sell", "Total"]; return ( }> {/* header strip */}
Order no.{d.id}
Status
Supplier{R.WS[d.ws]}
Invoice no.{d.invoice}
Store{findS(d.store)?.name}
Totes{d.totes}
Notes
{d.notes}
Created{d.created}
Scan products
Show advanced cost columns
{/* grid */}
Stock lines {d.lines.length} lines · {d.lines.reduce((a, l) => a + l.rec * l.ctn, 0)} units received
{HeadCells.map((h, i) => (
= 3 && h !== "Name" && h !== "PDE # / barcode" ? " rcv-cell--num" : "")} style={ h === "GST" || h === "GST · Sell" ? { textAlign: "center" } : undefined}>{h}
))}
{d.lines.map((l, i) => { const p = findP(l.productId); const short = l.rec < l.ord; const over = l.rec > l.ord; const total = R.lineTotal(l); return (
setSel(i)}>
{l.bonus ? : }
{p.sku}
{p.name} {p.strength !== "—" ? p.strength : ""}{p.pack}
{l.ord}
e.stopPropagation()}>
R.setReceived(id, i, parseInt(e.target.value || "0", 10))}/>
{l.soh.toFixed(2)}
{adv &&
{oMoney(l.expCost)}
} {adv &&
{oMoney(l.ws1)}
} {adv &&
{oMoney(l.last)}
} {adv &&
{oMoney(l.cost)}
}
{l.markup.toFixed(1)}%
e.stopPropagation()}> {l.gst ? "Y" : "N"}
{oMoney(l.sell)}
{oMoney(total)}
); })}
{/* selected line detail */} {d.lines[sel] && (() => { const l = d.lines[sel]; const p = findP(l.productId); return (
Stock name
{p.name}
Current cost → new
{oMoney(l.cost)} → {oMoney(l.recCost)}
Current sell
{oMoney(l.sell)}
New markup
{l.markup.toFixed(1)}%
Ordered (units)
{l.ord * l.ctn}
Received (units)
{l.rec * l.ctn}
SOH after receipt
{(l.soh + l.rec * l.ctn).toFixed(2)}
Line total ex GST
{oMoney(R.lineTotal(l))}
); })()} {/* reconciliation */}
Stock-line order notes
{d.status === "received" ? "Received in full and posted to stock on hand. Cost and sell prices updated on the catalogue." : "Set received quantities against the invoice, then Receive to post stock on hand and update costs. Short-supplied lines stay on backorder."}
Total ex GST{oMoney(t.ex)}
GST{oMoney(t.gst)}
Total inc GST{oMoney(t.inc)}
Invoice declared {oMoney(declared)}
Variance {Math.abs(variance) < 0.01 ? "Matches invoice ✓" : (variance > 0 ? "+" : "") + oMoney(variance)}
); } // ================================================================= // PRODUCT ORDERS/INVOICES HISTORY // ================================================================= function ProductOrderHistory() { const { screenState, setScreen } = window.useOffice(); const R = window.RCV; const pid = screenState.historyProductId || screenState.activeProductId || "p016"; const p = findP(pid); const [q, setQ] = rcvUseState(""); const [supF, setSupF] = rcvUseState("all"); const rows = rcvUseMemo(() => R.productHistory(pid), [pid]); const suppliers = ["all"].concat(Array.from(new Set(rows.map(r => r.supplier)))); const shown = rows.filter(r => (supF === "all" || r.supplier === supF) && (!q || (r.orderNo + r.invoice + r.notes + r.supplier).toLowerCase().includes(q.toLowerCase()))); const cols = "92px 96px 150px 130px 1.3fr 130px 64px 64px 90px 84px 74px 84px"; const heads = ["Order No", "Status", "Created", "Invoice No", "Supplier · notes", "Received", "Ord", "Sup", "Inv cost", "Cost", "M/up%", "Sell"]; // averages const recd = rows.filter(r => r.status === "Received"); const avgCost = recd.length ? recd.reduce((s, r) => s + r.cost, 0) / recd.length : 0; const avgMarkup = recd.length ? recd.reduce((s, r) => s + r.markup, 0) / recd.length : 0; const totalOrdered = recd.reduce((s, r) => s + r.ordU, 0); return ( }>
{suppliers.slice(0, 6).map(s => ( ))}
setQ(e.target.value)}/>
{heads.map((h, i) =>
{h}
)}
{shown.map((r, i) => (
{r.orderNo}
{r.status === "Received" ? : }
{r.created}
{r.invoice || "—"}
{r.supplier}
{r.notes}
{r.received || "—"}
{r.ordU}
{r.supU}{r.split && split}
{oMoney(r.invCost)}
{oMoney(r.cost)}
{r.markup.toFixed(2)}
{oMoney(r.sell)}
))}
{shown.length} lines
); } window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, { receiving: ReceivingWorklist, receiveOrder: ReceiveOrder, productHistory: ProductOrderHistory, });