/* Live pricing reports and configuration. Scoped API saves never write a retail price. */ (function () { const {useState,useEffect,useMemo} = React; const money = c => c == null ? "—" : "$" + (c/100).toLocaleString("en-AU",{minimumFractionDigits:2,maximumFractionDigits:2}); const percent = n => n == null ? "—" : Number(n).toFixed(1) + "%"; const signed = c => c == null ? "—" : (c>0 ? "+" : c<0 ? "−" : "") + money(Math.abs(c)); const date = v => v ? new Date(v).toLocaleDateString("en-AU",{day:"numeric",month:"short",year:"numeric"}) : "Never observed"; const clone = v => JSON.parse(JSON.stringify(v)); function useLoad(loader,deps=[]) { const [data,setData]=useState(null),[error,setError]=useState(null),[busy,setBusy]=useState(true),[reload,setReload]=useState(0); useEffect(()=>{const refresh=()=>setReload(k=>k+1);window.addEventListener("office-pricing-refresh",refresh);return()=>window.removeEventListener("office-pricing-refresh",refresh);},[]); useEffect(()=>{let alive=true;setBusy(true);setError(null);loader().then(d=>{if(alive)setData(d);}).catch(e=>{if(alive)setError(e.message||"Couldn't load pricing data.");}).finally(()=>{if(alive)setBusy(false);});return()=>{alive=false;};},[reload,...deps]); return {data,setData,error,setError,busy,refresh:()=>setReload(k=>k+1)}; } function Message({error,children}) {return
{children}
;} function Frame({children,actions}) {return
{actions}
{children}
;} function LoadState({r}) {return <>{r.error&&{r.error}}{r.busy&&Loading live pricing data…};} function useOpen(from) {const {setScreen,setScreenState}=window.useOffice();return productId=>{setScreenState(st=>({...st,priceProductId:productId,priceFrom:from,priceMethod:undefined,priceStrategy:undefined}));setScreen("priceDetail");};} function Pager({page,setPage,total,size=50}) {return total>size?
{page*size+1}–{Math.min((page+1)*size,total)} of {total}
:null;} function RuleList({config}) { const rules=config ? [{id:"default",name:"Everything",scope:"group",methodId:config.defaultMethodId,enabled:true},...(config.rules||[])] : []; return
{rules.map(r=>
{r.id==="default"?"Group default":r.scope}
{r.name||r.value||"Everything"}{r.reason||(!r.enabled?"Disabled":"")}
{config.methods.find(m=>m.id===r.methodId)?.name||r.methodId}
)}
; } function Breakdown() { const r=useLoad(()=>Promise.all([window.OfficeAPI.pricingBreakdown(),window.OfficeAPI.pricingConfig()]).then(([report,config])=>({report,config}))); const [grouping,setGrouping]=useState("categories"),[selected,setSelected]=useState(null),[page,setPage]=useState(0); const {setScreen,setScreenState}=window.useOffice(),open=useOpen("pricingBreakdown"),Kpi=window.KpiCard; const d=r.data?.report,cfg=r.data?.config,s=d?.summary,groups=d?.[grouping]||[]; const toggle=id=>{setSelected(current=>current===id?null:id);setPage(0);}; const addRule=()=>{setScreenState(st=>({...st,priceEditRule:true}));setScreen("pricingMethods");}; const columns='minmax(180px,1.5fr) 118px 76px 110px 96px 104px 110px minmax(160px,1fr)'; return Recompute}> {d&&<>
r.enabled).length+1} delta={{text:"Most specific assignment wins"}}/>
A rule set at group level covers everything. Anything set at category, brand or line level overrides it, and the most specific assignment always wins. The detail screen names which one governed the price and which ones lost.
{d.basisNote}

Sales: {date(d.period?.from)} to {date(d.period?.to)}. Margin comparisons use the same costed unit basket.

{[["categories","By category"],["brands","By brand"]].map(([key,label])=>)}
Click a row to see the lines inside it
{[grouping==="categories"?"Category":"Brand","KVIs","Lines","Sales ex-GST","Margin","If repriced","Moves","Method in force"].map((label,i)=>
=2&&i<=6?{justifyContent:"flex-end"}:undefined}>{label}
)} {groups.map(g=>{ const expanded=selected===g.id; return
toggle(g.id)} style={{cursor:"pointer"}}>
{g.kviCount?g.kviCount+" of "+g.lines:none}
{g.lines}
{money(g.salesExGstCents)}
{percent(g.currentMarginPct)}
g.currentMarginPct?"pc-down":g.recommendedMarginPct{percent(g.recommendedMarginPct)}
{g.rises+g.falls}
{g.rises} rise · {g.falls} fall
{g.methods.map(m=>{m.name})}
{expanded&&<>
{["Product","Rule from","Cost","Now inc-GST","Margin","Recommended","Move","Method"].map((label,i)=>
=2&&i<=6?{textAlign:"right"}:undefined}>{label}
)}
{g.products.slice(page*50,(page+1)*50).map(p=>{ const rule=cfg.rules.find(rule=>rule.id===p.ruleId); const cost=p.currentNetCents!=null&&p.currentGpCents!=null?p.currentNetCents-p.currentGpCents:p.recommendedNetCents!=null&&p.recommendedGpCents!=null?p.recommendedNetCents-p.recommendedGpCents:null; const scope=rule?.scope||"group",labels={group:"Group default",category:"Category",brand:"Brand",product:"This line"},tones={group:"flat",category:"brand",brand:"sched",product:"hold"}; return
open(p.productId)} style={{cursor:"pointer"}}>
{p.sku||"No SKU"}{p.kvi?" · KVI":""}
{labels[scope]}{p.locked&&
Locked
}
{money(cost)}
{money(p.currentCents)}
{percent(p.marginNowPct)}
{money(p.recommendedCents)}
{p.locked? held:0?"pc-up":p.deltaCents<0?"pc-down":"off-muted"}>{signed(p.deltaCents)}}
{p.methodName}{p.warn&&
{p.warn}
}
; })}
}
; })}
{!groups.length&&
No eligible front-of-shop lines in this scope.
}

Rules in force

Ordered from general to specific. The working names the rule that wins.
} ; } function Checks() { const r=useLoad(()=>window.OfficeAPI.pricingChecks()),d=r.data,s=d?.summary,open=useOpen("pricingChecks"),{setScreen}=window.useOffice(),Kpi=window.KpiCard; return Recheck}>{d&&<>
Pack ladders
A bigger pack should cost less per unit. Confirmed pack units establish the comparison; a price ladder also needs a recorded selling price for each pack.
{d.packLadders.length?
{d.packLadders.map(p=>
{p.broken?"Broken":"Confirmed units"}
{p.levels.map((l,i)=>
{l.name}
{l.unitsInBase==null?"—":l.unitsInBase}
base units{l.isSell?" · sold at till":""}
{l.priceCents!=null&&
{money(l.priceCents)}
}
)}
{p.note}
)}
:
No confirmed pack group has enough comparable selling-price evidence to assess a price ladder.
}
Only confirmed levels within the same product are grouped. Missing pack prices remain unassessed.
You may be chasing a promotion
Where the cheapest observed price is inferred to be on sale, matching it may follow a temporary price. No end date or return price is assumed.
{d.promos.length?
{d.promos.map((p,i)=>
Inferred sale
Observed{money(p.priceCents)}
Retailer{p.retailerName}
Last seen{date(p.observedAt)}
Return price
{p.evidence}
)}
:
No cheapest-observed prices currently meet the sale-history threshold.
}
Same line, different price by store
Independent shelf prices at each store are needed to distinguish deliberate local pricing from drift.
{d.crossStore.reason}
Pricing assignments can vary by site; recorded shelf prices currently apply across the organisation.
{d.costIssues.length>0&&
{d.costIssues.length} unit-cost checks
{d.costIssues.map(p=>
{p.note}
)}
} }; } function Register() { const [search,setSearch]=useState(""),[query,setQuery]=useState(""),[page,setPage]=useState(0),[changes,setChanges]=useState({}),[editRevision,setEditRevision]=useState(null),[saving,setSaving]=useState(false),[notice,setNotice]=useState(null); useEffect(()=>{const t=setTimeout(()=>{setQuery(search.trim());setPage(0);},250);return()=>clearTimeout(t);},[search]); const r=useLoad(()=>window.OfficeAPI.pricingRegister({search:query,limit:50,offset:page*50}),[query,page]),d=r.data,Kpi=window.KpiCard; const change=(row,key,value)=>{setNotice(null);if(!Object.keys(changes).length)setEditRevision(d.revision);setChanges(prev=>({...prev,[row.productId]:{productId:row.productId,enabled:row.enabled,reason:row.reason||"Manually selected",cadenceDays:row.cadenceDays||14,...prev[row.productId],[key]:value}}));}; const save=async()=>{setSaving(true);r.setError(null);try{await window.OfficeAPI.savePricingRegister({revision:editRevision,kviOverrides:Object.values(changes)});setChanges({});setEditRevision(null);setNotice("Register saved. Future recommendations use these KVI selections and recheck dates.");r.refresh();}catch(e){r.setError(e.message||"Couldn't save the register.");}finally{setSaving(false);}}; return 0} onClick={r.refresh}> Refresh}>{notice&&{notice}}{d&&<>
Registered lines get the KVI second pass on every recommendation. Their cadence identifies when fresh competitor evidence is due.
{d.scopeNote}

Last observed is a captured competitor price, not a record of staff approval. The cadence tracks due dates; it does not add a new collection service.

Known value items

setSearch(e.target.value)}/>
{d.rows.map(row=>{const p={...row,...changes[row.productId]};return ;})}
KVIProductCategoryWhy it's on the listRecheckLast observed
{p.name}{p.sku||"No SKU"}{p.category||"Unassigned"} change(row,"reason",e.target.value)}/> {date(p.lastObservedAt)}{p.enabled&&p.due&&Due for recheck}
{!d.rows.length&&
No eligible products match this search.
}
Turning a line on enables its KVI second pass after saving. {Object.keys(changes).length} unsaved changes.
}; } function Methods() { const r=useLoad(()=>window.OfficeAPI.pricingConfig()); const reports=useLoad(()=>Promise.all([window.OfficeAPI.pricingBreakdown(),window.OfficeAPI.pricingChecks()]).then(([breakdown,checks])=>({breakdown,checks}))); const [draft,setDraft]=useState(null),[methodId,setMethodId]=useState("hlprice"),[dirty,setDirty]=useState(false),[saving,setSaving]=useState(false),[notice,setNotice]=useState(null); const {screenState={},setScreenState}=window.useOffice(); const [showRule,setShowRule]=useState(!!screenState.priceEditRule),[ruleScope,setRuleScope]=useState("category"),[ruleValue,setRuleValue]=useState(""),[ruleName,setRuleName]=useState(""),[ruleMethod,setRuleMethod]=useState("hlprice"),[picked,setPicked]=useState(null),[lock,setLock]=useState(false),[lockPrice,setLockPrice]=useState(""),[ruleError,setRuleError]=useState(null); useEffect(()=>{if(r.data&&!dirty){setDraft(clone(r.data));}},[r.data,dirty]); useEffect(()=>{if(screenState.priceEditRule)setScreenState(st=>({...st,priceEditRule:false}));},[]); const edit=fn=>{setDraft(prev=>{const next=clone(prev);fn(next);return next;});setDirty(true);setNotice(null);}; const method=draft?.methods.find(m=>m.id===methodId); const editMethod=(key,value)=>edit(d=>{d.methods.find(m=>m.id===methodId)[key]=value;}); const editBand=(i,key,value)=>edit(d=>{d.methods.find(m=>m.id===methodId).bands[i][key]=Math.round(Number(value)*100);}); const save=async()=>{setSaving(true);r.setError(null);try{const result=await window.OfficeAPI.savePricingConfig(draft);setNotice("Configuration saved. Recommendations will use these rules on the next read. Shelf prices are unchanged.");if(result.methods){r.setData(result);}else{r.refresh();}reports.refresh();setDirty(false);}catch(e){r.setError(e.message||"Couldn't save pricing configuration.");}finally{setSaving(false);}}; const addRule=()=>{ const value=ruleScope==="group"?null:ruleScope==="product"?picked?.id:ruleValue.trim(); if(ruleScope!=="group"&&!value){setRuleError("Choose a target for this rule.");return;} if(lock&&ruleScope==="product"&&(!Number.isFinite(Number(lockPrice))||Number(lockPrice)<=0||!ruleName.trim())){setRuleError("A lock needs a positive price and a reason.");return;} if(draft.rules.some(r=>r.enabled&&r.scope===ruleScope&&r.value===value)){setRuleError("An enabled rule already targets this level and value. Remove it before adding a replacement.");return;} edit(d=>{d.rules.push({id:"rule-"+Date.now().toString(36),name:ruleName.trim()||picked?.name||value||"Everything",scope:ruleScope,value,methodId:ruleMethod,enabled:true});if(lock&&ruleScope==="product"){d.productLocks=(d.productLocks||[]).filter(l=>l.productId!==picked.id).concat({productId:picked.id,priceCents:Math.round(Number(lockPrice)*100),reason:ruleName.trim()});}}); setShowRule(false);setRuleError(null);setRuleName("");setRuleValue("");setPicked(null);setLock(false); }; return {saving?"Saving…":"Save changes"}}>{notice&&{notice}}{draft&&method&&
Three methods run side by side: anchored to one retailer, capped against the cheapest, or driven by category margin.
{draft.methods.map(m=>)}

{method.name} · configuration

{method.blurb}
{method.kind==="reference"&&
{method.bands.map((b,i)=>{["add","normal","sale"].map(k=>)})}
Reference price bandAddNormal floorSale floor
{money(i?method.bands[i-1].max+1:0)}–{b.max==null?"up":money(b.max)}editBand(i,k,e.target.value)}/>{k==="add"&&b.addPct!=null&&or {b.addPct}%}
} {method.kind==="competitor"&&
} {method.kind==="margin"&&<>
{Object.entries(method.targets||{}).map(([category,target])=>)}
}
{method.kind==="reference"?<>Rule 1Reference price plus the band’s add.Cost floorCost plus the normal or inferred-sale floor, grossed up for taxable products.Band recomputeA floor above its band is recomputed in the band it reaches.Higher winsThe higher of the anchored price and cost floor.KVI second passThe cheapest watched KVI price plus {money(method.kviAdd)}, checked against its floor.:method.kind==="competitor"?<>Cheapest observedThe cheapest comparable price across the watched retailers.Cost floorThe cost-plus floor takes priority when the gap cap would breach it.:<>Margin targetCategory target on the ex-GST selling price. Unassigned categories use the method’s fallback.Ceiling checkPrices beyond the competitor tolerance are flagged for review.} RoundingNearest of {(method.rounding||[]).map(n=>"."+String(n).padStart(2,"0")).join(" or ")}, ties up. KVI stance overrides use exact cents.
{draft.updatedAt?"Last saved "+date(draft.updatedAt):"Using the initial method configuration"}

Rules in force

{showRule&&

New rule

{ruleError&&{ruleError}}
{ruleScope==="category"&&} {ruleScope==="brand"&&} {ruleScope==="product"&&
{setPicked(p);if(p)setLockPrice(String((p.sellPriceCents||0)/100));}}/>
}
{ruleScope==="product"&&
{lock&&}
}
Save changes to put this rule into effect.
}
{draft.rules.map(rule=>
{rule.scope}
{rule.name||rule.value||"Everything"}{rule.value}
{draft.methods.find(m=>m.id===rule.methodId)?.name}
)}
{!!draft.productLocks?.length&&

Manual locks

{draft.productLocks.map(l=>
Locked
{l.reason}{l.productId}
{money(l.priceCents)}
)}
}
{dirty?"Unsaved changes":"Configuration is up to date"}
{draft.scopeNote}
}; } window.OFFICE_SCREENS=Object.assign(window.OFFICE_SCREENS||{},{pricingBreakdown:Breakdown,pricingChecks:Checks,pricingRegister:Register,pricingMethods:Methods}); })();