/* ============================================================= CoreIQ Office — Stock Line Editor Replaces the old "New stock line" form with a comprehensive tabbed editor that doubles as the Edit view for existing lines. Tabs: Details · Pricing · Suppliers (PDEs) · Barcodes · Promotions · Printing · History · Movements · Notes · Multi-store ============================================================= */ const { useState: sleUseState, useMemo: sleUseMemo } = React; const SLE_TABS = [ { id: "details", label: "Details" }, { id: "pricing", label: "Pricing" }, { id: "suppliers", label: "Suppliers (PDEs)" }, { id: "barcodes", label: "Barcodes" }, { id: "promos", label: "Promotions & Clubs" }, { id: "printing", label: "Printing" }, { id: "multistore", label: "Multi-store" }, { id: "movements", label: "Movements" }, { id: "history", label: "History" }, { id: "notes", label: "Notes" }, ]; function StockLineEditor() { const { setScreen, screenState, pushToast } = window.useOffice(); const editing = screenState.activeProductId ? OD.PRODUCTS.find(p => p.id === screenState.activeProductId) : null; const isNew = !editing; // --------------------------------------------------------------- // Form state — seed from an existing product or sensible defaults. // Field names mirror Minfos/FRED conventions where reasonable so // the model maps cleanly to migration. // --------------------------------------------------------------- const [form, setForm] = sleUseState(() => sleSeedForm(editing)); const [tab, setTab] = sleUseState("details"); const [dirty, setDirty] = sleUseState(false); function set(patch) { setForm(f => ({ ...f, ...patch })); setDirty(true); } function setField(k, v) { set({ [k]: v }); } // Derived pricing const costEx = parseFloat(form.costEx) || 0; const supplierEx = parseFloat(form.supplierEx) || 0; const taxRate = form.taxRate || 0; const costInc = costEx * (1 + taxRate); const sellEx = parseFloat(form.sellEx) || 0; const sellInc = sellEx * (1 + taxRate); const gstAmount = sellInc - sellEx; const markup = costEx > 0 ? ((sellEx - costEx) / costEx) * 100 : 0; const gp = sellEx > 0 ? ((sellEx - costEx) / sellEx) * 100 : 0; // Validation const valid = !!form.name && !!form.sku && !!form.dept && !!form.sub && sellEx > 0; function save() { pushToast({ kind: "paid", icon: "check-circle", title: `${isNew ? "Stock line created" : "Stock line saved"} · ${form.name}`, meta: `SKU ${form.sku} · ${oMoney(sellEx)} ex GST`, }); setDirty(false); setScreen(isNew ? "stockOverview" : "productDetail"); } function cancel() { setScreen(isNew ? "stockOverview" : "productDetail"); } return (

{isNew ? "New stock line" : "Edit stock line details"}

{form.name ? {form.name} : Untitled} {form.sku && <>·{form.sku}} {dirty && Unsaved changes}
{!isNew && }
{/* Tab strip */}
{tab === "details" && } {tab === "pricing" && } {tab === "suppliers" && } {tab === "barcodes" && } {tab === "promos" && } {tab === "printing" && } {tab === "multistore" && } {tab === "movements" && } {tab === "history" && } {tab === "notes" && }
); } // ================================================================= // SEED — turn an existing product into a full editor form, or // produce sensible defaults for a new line. // ================================================================= function sleSeedForm(p) { if (!p) { return { // Identity name: "", fullName: "", sku: "", strength: "", pack: "", // Classification dept: "", cat: "", sub: "", manufacturer: "", schedule: "—", stockGroups: [], locations: [], // Stock rules cartonSize: 1, minOrdCartons: 1, minStock: 0, maxStock: 0, // Flags active: true, autoOrder: true, orderOnlyBelowMin: false, ethical: false, discountable: true, authRequired: false, bulkFood: false, custDisplay: true, inRobot: false, online: false, controlled: false, coldChain: false, ndss: false, ageVerify: false, counsel: false, // Pricing costEx: "", supplierEx: "", avgCost: "", prevCost: "", sellEx: "", rrp: "", taxRate: 0.10, gstFlag: "Y - Taxable", pricingPolicy: "Standard", roundingPolicy: "Rounding None", expectedDiscount: 0, // Lists suppliers: [], barcodes: [], clubs: [], promos: [], // Multi-store storeStock: OD.STORES.reduce((a, s) => ({...a, [s.id]: { onHand:0, min:0, max:0, location:"", active:true }}), {}), // Printing labelTemplate: "shelf-50x30", printPrice: true, printBarcode: true, printStrength: true, // Notes internalNote: "", customerNote: "", clinicalNote: "", }; } // From a real product (synthetic but plausible PDEs / barcodes) const seed = (p.id || "").split("").reduce((a, c) => a + c.charCodeAt(0), 0); const taxRate = p.taxRate || 0; const supplierEx = +(p.cost * 1.08).toFixed(2); return { name: p.name, fullName: `${p.name} ${p.strength !== "—" ? p.strength : ""} ${p.pack}`.trim(), sku: p.sku, strength: p.strength === "—" ? "" : p.strength, pack: p.pack, dept: p.dept, cat: p.cat, sub: p.sub, manufacturer: ["GSK","Pfizer","Sanofi","Bayer","AstraZeneca","Mylan","Sandoz","Apotex"][seed % 8], schedule: p.schedule, stockGroups: p.controlled ? ["S8 register"] : p.pbs ? ["PBS"] : [], locations: p.controlled ? ["S8 Safe"] : ["Main shelf"], cartonSize: [1,1,6,12][seed % 4], minOrdCartons: 1, minStock: 0, maxStock: 0, active: true, autoOrder: true, orderOnlyBelowMin: false, ethical: false, discountable: !p.pbs, authRequired: !!p.controlled || !!p.ageVerify, bulkFood: false, custDisplay: true, inRobot: false, online: !p.controlled, controlled: !!p.controlled, coldChain: !!p.coldChain, ndss: !!p.ndss, ageVerify: !!p.ageVerify, counsel: !!p.controlled, costEx: p.cost.toFixed(2), supplierEx: supplierEx.toFixed(2), avgCost: p.cost.toFixed(2), prevCost: (p.cost * 0.96).toFixed(2), sellEx: p.price.toFixed(2), rrp: (p.price * 1.05).toFixed(2), taxRate, gstFlag: taxRate > 0 ? "Y - Taxable" : "N - Free to Customers", pricingPolicy: p.pbs ? "PBS" : "Standard", roundingPolicy: "Rounding None", expectedDiscount: 0, suppliers: sleSeedSuppliers(p, seed), barcodes: [{ code: p.sku, default: true, type: "EAN-13" }], clubs: p.pbs ? [{ name: "Concession", ratio: "1.0", fixed: "" }] : [], promos: [], storeStock: OD.STORES.reduce((a, s) => { const st = OD.STOCK[s.id]?.[p.id]; return { ...a, [s.id]: { onHand: st?.onHand || 0, min: st?.min || 0, max: st?.max || 0, location: st?.location || "", active: !!st, }}; }, {}), labelTemplate: "shelf-50x30", printPrice: true, printBarcode: true, printStrength: p.strength !== "—", internalNote: "", customerNote: "", clinicalNote: "", }; } function sleSeedSuppliers(p, seed) { const sups = OD.SUPPLIERS.slice(0, 4); return sups.map((s, i) => ({ pde: (1000000 + (seed * 31 + i * 117) % 9000000).toString(), supplierId: s.id, description: p.name.toUpperCase().slice(0, 32), moq: i === 0 ? "" : "1", cart: i === 0 ? "" : "1", ws1: (p.cost * (0.95 + i * 0.05)).toFixed(2), default: i === Math.min(1, sups.length - 1), })); } // ================================================================= // DETAILS TAB — identity, classification, stock rules, flags // ================================================================= function DetailsTab({ form, set, setField }) { return (
{/* Basic details */}
Basic details
setField("name", e.target.value)} placeholder="e.g. Panadol Liquid Caps 16s"/> setField("fullName", e.target.value)} placeholder="Long form including strength + pack"/> setField("strength", e.target.value)} placeholder="500mg"/> setField("pack", e.target.value)} placeholder="16 capsules"/> setField("sku", e.target.value)} placeholder="13-digit barcode"/> setField("manufacturer", e.target.value)} placeholder="e.g. GSK"/>
{/* Classification */}
Classification
Where this line sits in the merchandising hierarchy
setField("stockGroups", e.target.value.split(",").map(x => x.trim()).filter(Boolean))} placeholder="e.g. PBS, Webster eligible"/> setField("locations", e.target.value.split(",").map(x => x.trim()).filter(Boolean))} placeholder="e.g. Main shelf, S8 Safe"/>
{form.dept && form.cat && form.sub && (
{OD.DEPT_BY_ID[form.dept]?.name} {OD.CAT_BY_ID[form.cat]?.name} {OD.SUB_BY_ID[form.sub]?.name}
)}
{/* Stock rules */}
Stock rules
Group defaults · override per store in the Multi-store tab
setField("cartonSize", parseInt(e.target.value)||1)}/> setField("minOrdCartons", parseInt(e.target.value)||0)}/> setField("minStock", parseInt(e.target.value)||0)}/> setField("maxStock", parseInt(e.target.value)||0)}/>
1 carton={form.cartonSize || 1} unit{form.cartonSize !== 1 ? "s" : ""} Min order={(form.minOrdCartons||0) * (form.cartonSize||1)} units
{/* Behaviour toggles */}
Behaviour
How this line behaves in POS, ordering, and online
{/* Clinical flags */}
Clinical & compliance
Workflow prompts shown to staff
); } // ================================================================= // PRICING TAB — full cost ladder + sell prices + GST + policies // ================================================================= function PricingTab({ form, set, setField, costEx, costInc, sellEx, sellInc, gstAmount, markup, gp, supplierEx }) { return (
Cost ladder
Tracked from supplier invoices · drives margin reporting
setField("prevCost", e.target.value)}/> setField("supplierEx", e.target.value)}/> setField("avgCost", e.target.value)}/> setField("costEx", e.target.value)} placeholder="0.00"/> setField("expectedDiscount", parseFloat(e.target.value)||0)}/>
Sell prices
setField("sellEx", e.target.value)} placeholder="0.00"/> setField("rrp", e.target.value)}/>
Margin summary
60 ? "good" : null}/> 40 ? "good" : null}/>
PBS / claim
{form.pricingPolicy === "PBS" && ( <> )}
); } // ================================================================= // SUPPLIERS / PDEs TAB // Pharmacy Data Exchange codes — multiple per stock line // ================================================================= function SuppliersTab({ form, set }) { function update(i, patch) { const next = form.suppliers.map((s, j) => j === i ? { ...s, ...patch } : (patch.default ? { ...s, default: false } : s)); set({ suppliers: next }); } function add() { set({ suppliers: [...form.suppliers, { pde: "", supplierId: OD.SUPPLIERS[0]?.id, description: form.name, moq: "", cart: "", ws1: "", default: form.suppliers.length === 0 }] }); } function remove(i) { set({ suppliers: form.suppliers.filter((_, j) => j !== i) }); } return (
Supplier codes (PDEs)
{form.suppliers.length} supplier{form.suppliers.length === 1 ? "" : "s"} · the default is used for auto-orders
PDE #
Supplier
Description
MOQ
Cart
WS1 ($)
Default
{form.suppliers.map((s, i) => (
update(i, { pde: e.target.value })}/>
update(i, { description: e.target.value })}/>
update(i, { moq: e.target.value })}/>
update(i, { cart: e.target.value })}/>
update(i, { ws1: e.target.value })}/>
))} {form.suppliers.length === 0 && (
No supplier codes yet. Add one to enable ordering.
)}
); } // ================================================================= // BARCODES TAB // ================================================================= function BarcodesTab({ form, set }) { function update(i, patch) { const next = form.barcodes.map((b, j) => j === i ? { ...b, ...patch } : (patch.default ? { ...b, default: false } : b)); set({ barcodes: next }); } function add() { set({ barcodes: [...form.barcodes, { code: "", default: form.barcodes.length === 0, type: "EAN-13" }] }); } function remove(i) { set({ barcodes: form.barcodes.filter((_, j) => j !== i) }); } return (
Barcodes
A line can have multiple scannable codes · the default prints on labels
Barcode
Type
Default
{form.barcodes.map((b, i) => (
update(i, { code: e.target.value })} placeholder="Scan or type…"/>
))} {form.barcodes.length === 0 &&
No barcodes registered.
}
); } // ================================================================= // PROMOTIONS & CLUBS TAB // ================================================================= function PromosTab({ form, set }) { function addClub() { set({ clubs: [...form.clubs, { name: "", ratio: "1.0", fixed: "" }] }); } function updateClub(i, patch) { set({ clubs: form.clubs.map((c, j) => j === i ? { ...c, ...patch } : c) }); } function removeClub(i) { set({ clubs: form.clubs.filter((_, j) => j !== i) }); } function addPromo() { const today = "28/05/2026"; set({ promos: [...form.promos, { description: "", type: "% off", start: today, end: "" }] }); } function updatePromo(i, patch) { set({ promos: form.promos.map((p, j) => j === i ? { ...p, ...patch } : p) }); } function removePromo(i) { set({ promos: form.promos.filter((_, j) => j !== i) }); } return (
Loyalty clubs
Override pricing for club / concession members
Club name
Ratio
Fixed $
{form.clubs.map((c, i) => (
updateClub(i, { name: e.target.value })} placeholder="e.g. Seniors"/>
updateClub(i, { ratio: e.target.value })}/>
updateClub(i, { fixed: e.target.value })} placeholder="—"/>
))} {form.clubs.length === 0 &&
No club pricing rules.
}
Active promotions
Scheduled discounts & deal mechanics
Description
Type
Start
End
{form.promos.map((p, i) => (
updatePromo(i, { description: e.target.value })} placeholder="e.g. 2 for $20"/>
updatePromo(i, { start: e.target.value })} placeholder="dd/mm/yyyy"/>
updatePromo(i, { end: e.target.value })} placeholder="dd/mm/yyyy"/>
))} {form.promos.length === 0 &&
No active promotions.
}
); } // ================================================================= // PRINTING TAB // ================================================================= function PrintingTab({ form, setField }) { return (
Label template
Default used when printing from POS or PDE scan
Label preview
{form.name || "Product name"}
{form.printStrength && form.strength &&
{form.strength} · {form.pack}
} {!form.printStrength && form.pack &&
{form.pack}
}
{form.printBarcode &&
} {form.printPrice &&
${(parseFloat(form.sellEx) * (1 + form.taxRate) || 0).toFixed(2)}
}
{form.printBarcode && form.sku &&
{form.sku}
}
); } // ================================================================= // MULTI-STORE TAB // ================================================================= function MultiStoreTab({ form, set }) { function updateStore(id, patch) { set({ storeStock: { ...form.storeStock, [id]: { ...form.storeStock[id], ...patch } } }); } return (
Multi-store stock & overrides
Override the group defaults · uncheck Active to hide from a store
Store
Active
On hand
Min
Max
Location
{OD.STORES.map(s => { const v = form.storeStock[s.id]; return (
{s.name}
{s.code}
updateStore(s.id, { onHand: parseInt(e.target.value)||0 })}/>
updateStore(s.id, { min: parseInt(e.target.value)||0 })}/>
updateStore(s.id, { max: parseInt(e.target.value)||0 })}/>
updateStore(s.id, { location: e.target.value })} placeholder="Shelf code"/>
); })}
); } // ================================================================= // MOVEMENTS TAB — read-only ledger // ================================================================= function MovementsTab({ editing }) { const items = [ { date: "28/05/2026 14:32", kind: "Sale", qty: -1, bal: 8, ref: "Sale 1042831", user: "Sarah K." }, { date: "28/05/2026 09:01", kind: "Receipt", qty: +30, bal: 9, ref: "PO 4421", user: "Auto" }, { date: "27/05/2026 17:48", kind: "Sale", qty: -2, bal: -21, ref: "Rx 80442", user: "Mark T." }, { date: "26/05/2026 11:15", kind: "Transfer", qty: -6, bal: -19, ref: "TX-2026-088", user: "Alex J." }, { date: "24/05/2026 16:30", kind: "Adjustment", qty: -1, bal: -13, ref: "Damaged · disposed", user: "Sarah K." }, { date: "20/05/2026 10:05", kind: "Stocktake", qty: 0, bal: -12, ref: "Cycle count", user: "Alex J." }, ]; return (
Stock movements
All inventory changes for {editing?.name || "this line"}
Date / time
Kind
Qty
Balance
Reference
User
{items.map((m, i) => (
{m.date}
{m.kind}
0 ? 'var(--paid-text)' : 'var(--text-subtle)'}}> {m.qty > 0 ? "+" : ""}{m.qty}
{m.bal}
{m.ref}
{m.user}
))}
); } // ================================================================= // HISTORY TAB — audit log // ================================================================= function HistoryTab({ editing }) { const items = [ { date: "28/05/2026 09:00", user: "Auto", action: "Cost price updated", detail: "$4.20 → $4.42 (from PO 4421)" }, { date: "21/05/2026 14:21", user: "Sarah K.", action: "Retail price changed", detail: "$4.65 → $4.99" }, { date: "18/05/2026 11:10", user: "Mark T.", action: "Min stock raised", detail: "0 → 5 (Boronia store)" }, { date: "10/05/2026 08:42", user: "Alex J.", action: "Barcode added", detail: "9300673893680" }, { date: "02/05/2026 16:30", user: "Sarah K.", action: "Stock line created", detail: editing?.name || "" }, ]; return (
Edit history
Every change to this stock line, with the user who made it
{items.map((h, i) => (
{h.date}
{h.action}
{h.detail}
{h.user}
))}
); } // ================================================================= // NOTES TAB // ================================================================= function NotesTab({ form, setField }) { return (
Internal note
Staff-only · shown in dispense queue and on POS