"use client"; /* Reorder draft — what fell below par, at quantities that bring each line back up. Adjust and raise. This raises a draft on Ordering; nothing reaches a supplier until someone approves it there. */ import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { useDerived, useSnap } from "@/lib/client"; import { flaggedNeeds, label, onhand, reorderAt, touched, variantName } from "@/lib/compute"; import { INK, MBar, MBody, MEmpty, MError, MRule, MStepper, MTop } from "@/components/m"; export default function MReorder() { const { s, mutate, busy } = useSnap(); const { L, byId, variants } = useDerived(); const router = useRouter(); const [qty, setQty] = useState>({}); const [err, setErr] = useState(""); const [done, setDone] = useState(null); const needs = useMemo(() => flaggedNeeds(s, L, byId).map((n) => { const k = `${n.itemId}:${n.si}`; return { ...n, key: k, name: `${variantName(byId[n.itemId], n.size)}`, oh: onhand(s, L, k), par: reorderAt(s, k) }; }), [s, L, byId]); // /m/stock's "N below par" counts every line at or under its reorder point. flaggedNeeds nets off // what is already on an open order and drops a line once that covers it, so the two figures // legitimately differ — and a counter who taps "Reorder 8 lines" and is told nothing needs // ordering stops believing the screen. Report both, and never call a short shelf healthy: // stock on order is not stock on the shelf until somebody receipts it. const belowPar = useMemo( () => variants.filter((v) => touched(s, L, v.key) && onhand(s, L, v.key) <= reorderAt(s, v.key)).length, [s, L, variants], ); const covered = belowPar - needs.length; const q = (k: string, fallback: number) => qty[k] ?? fallback; const total = needs.reduce((t, n) => t + q(n.key, n.qty), 0); const suppliers = [...new Set(needs.map((n) => n.supplier))]; const raise = async () => { const bySup: Record = {}; for (const n of needs) if (q(n.key, n.qty) > 0) (bySup[n.supplier] ||= []).push(n); const codes: string[] = []; for (const sup of Object.keys(bySup)) { const r = await mutate<{ code: string }>("order.create", { orderFor: "Stock", supplier: sup, replenish: false, notes: "Raised from a stocktake on the app", lines: bySup[sup].map((n) => ({ itemId: n.itemId, size: n.size, qty: q(n.key, n.qty) })), }); if (!r.ok) { setErr(r.error); return; } codes.push(r.result.code); } setDone(codes.join(" · ")); }; if (done) return ( <> ); return ( <> setErr("")} />
Below par

Draft order

{needs.length === 0 ? belowPar === 0 ? "Every line is at or above its par level. Nothing needs ordering." : `${belowPar} line${belowPar === 1 ? "" : "s"} ${belowPar === 1 ? "is" : "are"} at or below par, and open orders already cover ${belowPar === 1 ? "it" : "them"}. There’s nothing more to raise — but the shelf stays short until the delivery is receipted.` : covered > 0 ? `${belowPar} lines are at or below par. Open orders cover ${covered}; the other ${needs.length} still need${needs.length === 1 ? "s" : ""} ordering, at quantities that bring ${needs.length === 1 ? "it" : "each one"} back up.` : `${needs.length} line${needs.length === 1 ? "" : "s"} ${needs.length === 1 ? "is" : "are"} at or below par. Quantities bring each one back up.`}

{needs.length === 0 ? ( ) : needs.map((n) => (
{n.name}
{n.oh} on hand · par {n.par} · {n.supplier}
setQty((x) => ({ ...x, [n.key]: v }))} />
))} {needs.length > 0 && (

Goes to {suppliers.length === 1 ? suppliers[0] : `${suppliers.length} suppliers`} as a draft. Nothing is sent until you approve it on Ordering.

)}
{needs.length > 0 && } ); }