/* ============================================================= CoreIQ Office — Pricing review (LIVE · this pharmacy) The lines that need a price decision, and the working behind each recommendation. Ported from the design's Pricing shell + recommendation detail, wired to /pricing/queue and /pricing/recommendation/:id. ADVISORY ONLY. Nothing on these two screens changes a shelf price. The queue is DERIVED on every read from facts already on file — a cost that moved on a real invoice, a line received with no price, a competitor whose price fell out of its observed range — so there is nothing stored to go stale and nothing to "dismiss". Prices are changed in Manage stock. Registers window.OFFICE_SCREENS.pricing and .priceDetail ============================================================= */ const { useState: prState, useEffect: prEffect, useMemo: prMemo } = React; const prM = (c) => (c == null ? "—" : "$" + (Number(c) / 100).toLocaleString("en-AU", { minimumFractionDigits: 2, maximumFractionDigits: 2 })); const prSigned = (c) => (c == null ? "—" : (c > 0 ? "+" : c < 0 ? "−" : "") + prM(Math.abs(c))); const prPct = (v) => (v == null ? "—" : Number(v).toFixed(1) + "%"); const prDate = (iso) => (iso ? String(iso).slice(0, 10) : "—"); // Why a line is in the queue. Order here is priority order — it matches the API. const PR_REASONS = { new: { label: "No price", bg: "var(--void-bg)", fg: "var(--void-text)", bd: "var(--void-border)" }, cost: { label: "Cost moved", bg: "var(--hold-bg)", fg: "var(--hold-text)", bd: "var(--hold)" }, floor: { label: "Under floor", bg: "var(--void-bg)", fg: "var(--void-text)", bd: "var(--void-border)" }, sale: { label: "Competitor sale", bg: "var(--hold-bg)", fg: "var(--hold-text)", bd: "var(--hold)" }, kvi: { label: "Price-sensitive", bg: "var(--navy-100)", fg: "var(--navy-800)", bd: "var(--navy-200)" }, move: { label: "Room to move", bg: "var(--bg-subtle)", fg: "var(--text-subtle)", bd: "var(--line)" }, }; function PrReasonTag({ reason }) { const r = PR_REASONS[reason] || PR_REASONS.move; return ( {r.label} ); } function PrAdvisoryNote() { return (
Recommendations only — nothing here changes a shelf price. The queue is worked out fresh from the stock card, the latest invoice cost and observed competitor prices each time you open it. Change a price in Manage stock.
); } // ================================================================= // QUEUE // ================================================================= function PricingReview() { const { setScreen, setScreenState } = window.useOffice(); const Kpi = window.KpiCard; const [data, setData] = prState(null); const [err, setErr] = prState(null); const [loading, setLoading] = prState(true); const [filter, setFilter] = prState("all"); const [search, setSearch] = prState(""); const [reloadKey, setReloadKey] = prState(0); prEffect(() => { let alive = true; setLoading(true); setErr(null); window.OfficeAPI.pricingQueue() .then((d) => { if (alive) setData(d); }) .catch((e) => { if (alive) setErr(e && e.message ? e.message : "Couldn't load the pricing queue."); }) .finally(() => { if (alive) setLoading(false); }); return () => { alive = false; }; }, [reloadKey]); const rows = prMemo(() => { const all = (data && data.rows) || []; const q = search.trim().toLowerCase(); return all.filter((r) => (filter === "all" || r.reason === filter) && (!q || (r.name || "").toLowerCase().includes(q) || (r.sku || "").toLowerCase().includes(q) || (r.brand || "").toLowerCase().includes(q))); }, [data, filter, search]); const counts = (data && data.counts) || {}; const total = Object.values(counts).reduce((s, n) => s + Number(n || 0), 0); const open = (r) => { setScreenState((st) => ({ ...st, priceProductId: r.productId, priceFrom: "pricing" })); setScreen("priceDetail"); }; const TABS = [{ k: "all", label: "All", n: total }].concat(Object.keys(PR_REASONS).map((k) => ({ k, label: PR_REASONS[k].label, n: Number(counts[k] || 0) }))); return (
live · this pharmacy · derived from cost + competitor facts

Pricing review

{data && Method · {data.methodName}}
{data && Kpi && (
0 ? "down" : undefined }} /> 0 ? "down" : undefined }} />
)}
{TABS.map((t) => ( ))}
setSearch(e.target.value)} />
{err &&
{err}
} {!err && (
{loading ? "Working it out…" : rows.length + " line" + (rows.length === 1 ? "" : "s")}
{data ? "Recomputed " + new Date(data.generatedAt).toLocaleTimeString("en-AU", { hour: "2-digit", minute: "2-digit" }) + " · sorted by why, then by size of move" : ""}
{!loading && rows.length === 0 && (
{total === 0 ? Nothing needs a price decision right now. A line appears here when its cost moves on an invoice, it arrives with no price, it is selling under its cost floor, or an observed competitor price changes enough to matter. : No lines match this filter.}
)} {rows.length > 0 && (
Product
Why it's here
Now
Recommended
Move
Margin
{rows.map((r) => (
open(r)}>
{r.name} {r.sku || ""}{r.brand ? " · " + r.brand : ""}{r.kvi ? " · KVI" : ""}
{r.costNote && ⚠ check pack} {r.reasonDetail}
{prM(r.currentCents)}
{r.recommendedCents == null ? no anchor : prM(r.recommendedCents)}
0 ? "var(--paid-text)" : r.deltaCents < 0 ? "var(--void-text)" : "var(--text-subtle)" }}>{prSigned(r.deltaCents)}
{prPct(r.marginNowPct)}{r.marginRecPct != null && → {prPct(r.marginRecPct)}}
))}
)}
)}
); } // ================================================================= // DETAIL — the working, shown // ================================================================= function PrTrace({ trace }) { return (
{trace.map((t, i) => (
{t.k}
{t.v}
{t.d}
))}
); } function PriceDetail() { const { screenState, setScreen, setScreenState } = window.useOffice(); const productId = screenState.priceProductId; const backTo = screenState.priceFrom || "pricing"; const [methodId, setMethodId] = prState(screenState.priceMethod || undefined); const [methods, setMethods] = prState([]); const [d, setD] = prState(null); const [err, setErr] = prState(null); const [loading, setLoading] = prState(true); prEffect(() => { let alive = true; window.OfficeAPI.pricingMethods().then((ms) => { if (alive) setMethods(ms || []); }).catch(() => {}); return () => { alive = false; }; }, []); prEffect(() => { if (!productId) { setErr("No product selected."); setLoading(false); return; } let alive = true; setLoading(true); setErr(null); window.OfficeAPI.priceRecommendation(productId, methodId ? { method: methodId } : undefined) .then((x) => { if (alive) setD(x); }) .catch((e) => { if (alive) setErr(e && e.message ? e.message : "Couldn't load the recommendation."); }) .finally(() => { if (alive) setLoading(false); }); return () => { alive = false; }; }, [productId, methodId]); const rec = d && d.recommendation; const p = d && d.product; const openProduct = () => { setScreenState((st) => ({ ...st, activeProductId: productId, productFrom: "priceDetail" })); setScreen("productDetail"); }; return (
live · this pharmacy · price recommendation

{p ? p.name : "Price recommendation"}

{p &&
{p.sku || ""}{p.brand ? " · " + p.brand : ""}{p.category ? " · " + p.category : ""}{p.kvi ? " · KVI" : ""}
}
{methods.length > 1 && (
{methods.map((m) => ( ))}
)}
{err &&
{err}
} {loading && !d &&
Working it out…
} {d && rec && (
{/* the number */}
Recommendation
{d.methodName} · {rec.mode === "standard" ? "anchored calculation" : rec.mode === "fallback" ? "market-average fallback" : "no data to anchor to"}
{[ { l: "Now", v: prM(rec.currentCents), sub: rec.currentGp ? prPct(rec.currentGp.marginPct) + " margin" : "no price set" }, { l: "Recommended", v: rec.recommendedCents == null ? "—" : prM(rec.recommendedCents), sub: rec.gp ? prPct(rec.gp.marginPct) + " margin" : "—", strong: true }, { l: "Move", v: prSigned(rec.deltaCents), sub: rec.deltaCents == null ? "" : rec.deltaCents > 0 ? "up" : rec.deltaCents < 0 ? "down" : "hold", tone: rec.deltaCents > 0 ? "var(--paid-text)" : rec.deltaCents < 0 ? "var(--void-text)" : undefined }, { l: "Cost floor", v: rec.floorCents == null ? "—" : prM(rec.floorCents), sub: rec.taxable ? "inc GST" : "GST-free" }, ].map((x) => (
{x.l}
{x.v}
{x.sub}
))}
{rec.warn && (
{rec.warn}
)} {rec.kvi && (
KVI second pass
{rec.kvi.reason} Priced against the cheapest of {rec.kvi.set.map((o) => o.retailerShort).join(", ")} — {rec.kvi.cheapest.retailerShort} at {prM(rec.kvi.cheapest.priceCents)}, floor {prM(rec.kvi.floorCents)}.
{rec.kvi.missing.length > 0 &&
Not seen on: {rec.kvi.missing.join(", ")} — the evidence is incomplete.
}
)}
{/* the working */}
The working
Every number above, with the step behind it
{/* observations */}
What we observed
{d.observations.length ? "Latest price per retailer, last 90 days" : "No observations in the last 90 days"}
{d.observations.map((o) => (
{o.url ? {o.retailerName} : o.retailerName} {o.onSale && INFERRED SALE}
{prDate(o.observedAt)}{o.saleEvidence ? " · " + o.saleEvidence : ""}
{prM(o.priceCents)}
))} {!d.observations.length &&
Add one from Price position, or wait for the nightly Healthylife watch.
}
{/* cost + gst facts */}
The facts it ran on
{[ ["Cost of goods (ex GST)", prM(p.cogCents), p.costNote ? p.costNote : p.costSource === "invoice" ? "latest supplier invoice line" : p.costSource === "product" ? "stock card cost" : "no cost on file"], ["Previous cost", prM(p.prevCostCents), p.prevCostCents != null && p.cogCents !== p.prevCostCents ? "moved " + prSigned(p.cogCents - p.prevCostCents) : ""], ["GST", p.taxable ? "Taxable" : "GST-free", p.taxable ? "floor grossed up ×1.1" : (p.gstFreeReason || "")], ["Price-sensitive (KVI)", p.kvi ? "Yes" : "No", p.kviReason || "not on the named list"], ].map(([k, v, s]) => (
{k}
{s ?
{s}
: null}
{v}
))}
)}
); } window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, { pricing: PricingReview, priceDetail: PriceDetail });