/* eslint-disable */ /* © 2026 K2B NETWORK SOLUTIONS LLC. All rights reserved. Proprietary and confidential. Unauthorized copying, distribution, or use of this file, via any medium, is strictly prohibited. Original work product. */ // JBREWS DMS — Dead Money Dashboard. One screen for every dollar trapped in the // store: aged CIT, unpaid warranty & factory receivables, trade payoffs not // sent, aged units, cores not returned, sublet not billed, obsolete parts. // Reads DMS.deadMoney() (live ledger aggregation). Click a category to jump to // where the money gets freed. // // RC36 (lane fpd36) — the owner (10/29): "I need to have the option for the cars that aren't floored as an option on Dead Money". // • "Not floored (cash in inventory)": a toggle (on by default, this PC remembers) — every unit in stock with no floorplan amount, at // cost, in age buckets (0–30 / 31–60 / 61–90 / 91–120 / over 120 days), each with a "floor it" hint (the lender's statement lists // it → take the amount on the Floorplan screen; otherwise: ask the lender to floor it). // • Aged units: the age threshold is the STORE's setting (GET / POST /api/floorplan/settings, the office or the owner changes it; // default 60 days); the category is split floored / not floored. // • Money is added in integer cents here; a unit that is both aged and not floored is counted ONCE in the total. // DMSCore's deadMoney() is unchanged (its 60-day aged list is replaced on this screen by the store-threshold one). const dmMoney = (n) => "$" + (Number(n) || 0).toLocaleString("en-US", { maximumFractionDigits: 0 }); const DM_NAV = { cit: "funding", warranty: "warranty", factory: "acctoffice", payoffs: "deal", agedUnits: "inventory", notFloored: "floorplan", cores: "partsdept", sublet: "bodyshop", obsolete: "partsdept" }; const DM_ICON = { cit: "📄", warranty: "🛡", factory: "🏭", payoffs: "🔁", agedUnits: "🚗", notFloored: "💵", cores: "♻", sublet: "🔧", obsolete: "📦" }; const DM_BUCKETS = [["0–30 days", 0, 30], ["31–60", 31, 60], ["61–90", 61, 90], ["91–120", 91, 120], ["Over 120", 121, Infinity]]; const DM_NF_KEY = "k2b-deadmoney-notfloored"; const dmCents = (v) => { const n = Number(String(v === undefined || v === null ? "" : v).replace(/[$,\s]/g, "")); return isFinite(n) ? Math.round(n * 100) : 0; }; const dmDays = (u) => { const a = +u.age; if (a > 0) return Math.round(a); const d = u.stockedDate || u.invoiceDate || (u.stockIn && u.stockIn.at); return d ? Math.max(0, Math.floor((Date.now() - new Date(d)) / 86400000)) : 0; }; const dmFloored = (u) => dmCents(u.floorAmt) > 0; const dmDesc = (u) => [u.yr, u.mk, u.mdl].filter(Boolean).join(" "); function dmReadNf() { try { const v = localStorage.getItem(DM_NF_KEY); return v === null ? true : v === "1"; } catch (e) { return true; } } const DeadMoney = ({ onClose, go }) => { const D = window.DMS; const [, force] = React.useReducer(n => n + 1, 0); React.useEffect(() => (D && D.subscribe) ? D.subscribe(force) : undefined, []); const [openKey, setOpenKey] = React.useState(null); const [showNf, setShowNf] = React.useState(dmReadNf); const [cfg, setCfg] = React.useState({ days: 60, edit: false, loaded: false }); const [daysIn, setDaysIn] = React.useState(""); const [msg, setMsg] = React.useState(null); const [stmtStocks, setStmtStocks] = React.useState(null); // stocks the latest lender statement lists (the "floor it" hint) const [narrow, setNarrow] = React.useState(typeof window !== "undefined" && window.innerWidth < 720); React.useEffect(() => { const f = () => setNarrow(window.innerWidth < 720); window.addEventListener("resize", f); return () => window.removeEventListener("resize", f); }, []); React.useEffect(() => { let live = true; (async () => { try { const s = await D._apiGet("/floorplan/settings"); if (live && s && s.ok) { setCfg({ days: s.deadMoneyDays, edit: !!(s.may && s.may.edit), loaded: true }); setDaysIn(String(s.deadMoneyDays)); } } catch (e) { if (live) setCfg(c => Object.assign({}, c, { loaded: true })); } try { const r = await D._apiGet("/floorplan/reconcile"); if (live && r && r.ok && r.compare) setStmtStocks(new Set((r.compare.flooredFromStatement || []).map(x => String(x.stock).toUpperCase()))); } catch (e) { /* the hint stays the general one (a role without the floorplan screen, or offline) */ } })(); return () => { live = false; }; }, []); if (!D || !D.deadMoney) return null; const toggleNf = () => { const v = !showNf; setShowNf(v); try { localStorage.setItem(DM_NF_KEY, v ? "1" : "0"); } catch (e) {} }; const saveDays = async () => { setMsg(null); try { const r = await D._apiPost("/floorplan/settings", { deadMoneyDays: daysIn }); setCfg(c => Object.assign({}, c, { days: r.deadMoneyDays })); setMsg({ ok: true, text: r.message }); } catch (e) { setMsg({ ok: false, text: e.message || "Not saved." }); } }; // ── the categories: DMSCore's, with the aged-units category on the store's threshold (split floored / not floored) + Not floored ── const base = D.deadMoney(); const days = cfg.days || 60; const onLot = (D.inventory ? (D.inventory() || []) : []).filter(u => u && window.DealCalc && window.DealCalc.unitOnLot(u)); const agedList = onLot.filter(u => !/new/i.test(u.type || "") && dmDays(u) >= days) .map(u => ({ ref: u.stock, label: dmDesc(u) + (dmFloored(u) ? " · floored" + (u.floorLender ? " with " + u.floorLender : "") : " · NOT floored"), amountC: dmCents(u.cost), age: dmDays(u), floored: dmFloored(u) })); const nfList = onLot.filter(u => !dmFloored(u)) .map(u => ({ ref: u.stock, label: dmDesc(u) + (/new/i.test(u.type || "") ? " · new" : " · used"), amountC: dmCents(u.cost), age: dmDays(u), floored: false, hint: stmtStocks && stmtStocks.has(String(u.stock).toUpperCase()) ? "the lender's statement lists it — take the floored amount on the Floorplan screen" : "floor it: ask your floorplan lender to advance on it" })) .sort((a, b) => b.age - a.age); const sumC = (a) => a.reduce((s, x) => s + x.amountC, 0); const toCat = (key, label, list, extra) => Object.assign({ key, label, count: list.length, total: sumC(list) / 100, items: list.map(x => Object.assign({}, x, { amount: x.amountC / 100 })) }, extra || {}); const agedF = agedList.filter(x => x.floored), agedN = agedList.filter(x => !x.floored); const cats = []; base.categories.forEach(c => { if (c.key === "agedUnits") { cats.push(toCat("agedUnits", `Aged units (${days}+ days)`, agedList, { split: { floored: { n: agedF.length, c: sumC(agedF) }, notFloored: { n: agedN.length, c: sumC(agedN) } } })); if (showNf) cats.push(toCat("notFloored", "Not floored — cash in inventory", nfList, { buckets: DM_BUCKETS.map(([lbl, lo, hi]) => { const b = nfList.filter(x => x.age >= lo && x.age <= hi); return { lbl, n: b.length, c: sumC(b) }; }) })); } else cats.push(c); }); if (!cats.some(c => c.key === "agedUnits")) cats.push(toCat("agedUnits", `Aged units (${days}+ days)`, agedList)); // the total in cents; a unit both aged and not floored counts once const overlapC = showNf ? sumC(agedN) : 0; const totalC = cats.reduce((s, c) => s + Math.round((+c.total || 0) * 100), 0) - overlapC; const active = cats.filter(c => c.count > 0); const maxTotal = Math.max(1, ...cats.map(c => c.total)); return (
{/* grand total banner */}
Total cash trapped
{dmMoney(totalC / 100)}
{overlapC > 0 &&
{dmMoney(overlapC / 100)} of aged units that are also not floored is counted once
}
Money sitting in receivables, payoffs, aged units, cores and obsolete parts — work each category to free it back into cash.
{/* options: the not-floored toggle + the store's aged-unit threshold */}
Aged after {cfg.edit ? <> setDaysIn(e.target.value.replace(/[^0-9]/g, "").slice(0, 3))} inputMode="numeric" autoComplete="off" style={{ width: 52, padding: "4px 6px", fontFamily: "var(--jb-mono)" }} /> days : {days} days} (a store setting{cfg.edit ? "" : " — the office or the owner changes it"}) {msg && {msg.text}}
{active.length === 0 ? (
✓ No dead money detected — every category is clear.
) : (
{cats.map(c => { const isOpen = openKey === c.key; const empty = c.count === 0; return (
!empty && setOpenKey(isOpen ? null : c.key)} style={{ display: "flex", alignItems: "center", gap: 12, padding: narrow ? "10px 10px" : "12px 16px", cursor: empty ? "default" : "pointer" }}> {DM_ICON[c.key] || "•"}
{c.label}
{c.split && c.count > 0 &&
Floored: {c.split.floored.n} · {dmMoney(c.split.floored.c / 100)}  |  Not floored: {c.split.notFloored.n} · {dmMoney(c.split.notFloored.c / 100)}
}
{dmMoney(c.total)}
{c.count} item{c.count !== 1 ? "s" : ""}
{!empty && {isOpen ? "▴" : "▾"}}
{c.buckets && c.count > 0 && (
{c.buckets.map(b => (
{b.lbl}
{dmMoney(b.c / 100)}
{b.n} unit{b.n !== 1 ? "s" : ""}
))}
)} {isOpen && (
{c.items.map((it, i) => ( ))}
{it.ref} {it.label}{it.hint &&
↳ {it.hint}
}
{it.age ? it.age + "d" : ""} {dmMoney(it.amount)}
)}
); })}
)}
ESC-Close go && go("cashcommand")}>Cash Command go && go("floorplan")}>Floorplan go && go("dashboard")}>Dashboard
); }; Object.assign(window, { DeadMoney });