(function () { /* Comparable KVI baskets and saved advisory strategy. All values come from /pricing/market. */ const {useState: pmState, useEffect: pmEffect} = React; const pmMoney = c => c == null ? "—" : "$" + (c / 100).toLocaleString("en-AU",{minimumFractionDigits:2,maximumFractionDigits:2}); const pmPct = p => p == null ? "—" : (p > 0 ? "+" : "") + p.toFixed(1) + "%"; function PricingMarket() { const {setScreen, setScreenState} = window.useOffice(); const [data,setData] = pmState(null); const [selected,setSelected] = pmState(null); const [loading,setLoading] = pmState(true); const [saving,setSaving] = pmState(false); const [error,setError] = pmState(null); const [notice,setNotice] = pmState(null); const [reload,setReload] = pmState(0); const [showAdd,setShowAdd] = pmState(false); pmEffect(()=>{const refresh=()=>setReload(k=>k+1);window.addEventListener("office-pricing-refresh",refresh);return()=>window.removeEventListener("office-pricing-refresh",refresh);},[]); const [showAll,setShowAll] = pmState(false); const [search,setSearch] = pmState(""); const [page,setPage] = pmState(0); pmEffect(()=>setPage(0),[search]); pmEffect(() => { let alive = true; setLoading(true); setError(null); window.OfficeAPI.pricingMarket(selected ? {strategy:selected} : undefined) .then(d => { if(alive) setData(d); }) .catch(e => { if(alive) setError(e.message || "Couldn't load the market basket."); }) .finally(() => { if(alive) setLoading(false); }); return () => {alive=false;}; },[selected,reload]); const preview = !!data && data.strategy.id !== data.savedStrategyId; const ready = !!data && !loading && (!selected || data.strategy.id === selected); const choose = id => {setSelected(id); setNotice(null);}; const save = async () => { if(!ready || !preview || saving) return; setSaving(true); setError(null); setNotice(null); try { await window.OfficeAPI.savePricingStrategy({id:data.strategy.id}); setNotice("Saved. This stance now guides advisory recommendations for this scope. Shelf prices are unchanged."); setSelected(null); setReload(k=>k+1); } catch(e) {setError(e.message || "Couldn't save this stance.");} finally {setSaving(false);} }; const open = r => { setScreenState(st=>({...st,priceProductId:r.productId,priceFrom:"pricePosition",priceMethod:undefined,priceStrategy:preview ? data.strategy.id : undefined})); setScreen("priceDetail"); }; const s=data && data.summary, c=data && data.coverage; const rows=data ? data.rows.filter(r=>!search.trim() || [r.name,r.sku,r.brand].some(v=>(v||"").toLowerCase().includes(search.trim().toLowerCase()))) : []; const Kpi=window.KpiCard; return
{showAdd && {setShowAdd(false);setReload(k=>k+1);}} onCancel={()=>setShowAdd(false)} />} {error &&
{error}
} {notice &&
{notice}
} {loading &&
{data ? "Recalculating the basket preview…" : "Loading observed prices and the comparable KVI basket…"}
} {data && <>
{c.comparedProducts} of {c.namedKviProducts} KVI products compared. Latest usable price per retailer in the last 90 days.

{c.missingObservations || 0} without comparable observations · {c.missingOwnPrice || 0} without a shelf price · {c.missingCost || 0} without a usable cost · {c.excludedPackComparisons || 0} pack comparisons excluded · {c.excludedBasisComparisons || 0} incompatible price bases excluded.

{data.scopeNote}

Position against each retailer
KVI basket only · each comparison uses the same overlapping products
{data.retailers.length ?
{data.retailers.map(r=>
{r.name} 0 ? "pc-posbar-v off-pricing-up" : "pc-posbar-v off-pricing-down"}>{pmPct(r.gapPct)} {r.aboveCount} of {r.overlapCount} dearer
)}
:
No retailers have comparable KVI observations yet. Add a competitor price to begin.
}
Above zero means your basket is dearer. {c.comparedProducts} products have comparable observations in the last 90 days.

Line by line

{preview ? "Preview" : "Saved stance"} · {data.strategy.name}
{rows.length ?
{rows.slice(page*10,(page+1)*10).map(r=>)}
ProductYouCheapest observedGapRecommendedGap then
{r.sku || "No SKU"}{r.category ? " · " + r.category : ""} {pmMoney(r.ownCents)} {pmMoney(r.cheapestCents)}{typeof r.cheapestRetailer === "string" ? r.cheapestRetailer : r.cheapestRetailer.name} 0 ? "off-pricing-up" : "off-pricing-down")}>{pmPct(r.gapPct)} {loading ? "…" : pmMoney(r.recommendedCents)}{r.recommendedCents == null && Needs cost / evidence} {loading ? "…" : pmPct(r.gapThenPct)}
:
{s.basketCount ? "No KVI lines match this filter." : "No comparable KVI basket yet. Observations must match a named KVI product and its selling unit."}
}
{rows.length > 10 &&
{page*10+1}–{Math.min((page+1)*10,rows.length)} of {rows.length}
}
Pick a stance
Preview the trade-off, then set the stance that guides recommendations.
{data.strategies.map(st=>)}
{preview ? "Preview — this stance is not saved yet." : "Saved stance · " + data.strategy.name} Shelf prices are changed separately in Manage stock.
setShowAll(e.currentTarget.open)}> Browse all competitor observations
Broader monitoring view, including non-KVI and older observations. These rows are separate from the 90-day comparable basket above.
{showAll && window.AllCompetitorObservations && }
}
; } window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, {pricePosition:PricingMarket}); })();