/* 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 — Accounting Office. // Ties together the receivables & payables the integration engine generates: // A/R Aging · A/P Aging · Bank Reconciliation · Month-End Close. // Reads live from DMSCore; payments and reconciliation post to the GL. const AcctOffice = ({ onClose, go }) => { const [, force] = React.useReducer(n => n + 1, 0); React.useEffect(() => DMS.subscribe(force), []); const [tab, setTab] = React.useState(() => { const hint = (typeof window !== "undefined" && window.__aoTab) || "ar"; if (typeof window !== "undefined") window.__aoTab = null; return hint; }); const [modal, setModal] = React.useState(null); const money = (n) => "$" + Number(n || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); const act = (r) => Promise.resolve(r).then(x => { window.k2bToast && window.k2bToast(x.msg, x.ok ? "ok" : "warn", 3200); return x; }); // RC33 (mpx): bills / payments are SERVER commands (promises) const daysSince = (iso) => iso ? Math.floor((Date.now() - new Date(iso)) / 86400000) : 0; const bucket = (d) => d <= 30 ? "0-30" : d <= 60 ? "31-60" : d <= 90 ? "61-90" : "90+"; // ── Build A/R from the engine ── const deals = DMS.deals(), wr = DMS.warrantyReceivables(), fr = DMS.factoryReceivables(); const stageOf = (d) => (DMS.dealStage ? DMS.dealStage(d) : (d.stage || "Posted")); const arRows = [ // Contracts in transit = posted, unfunded, FINANCED deals. Cash deals were // paid at delivery — nothing in transit (ONE PATH). ...deals.filter(d => d.postedToGL && stageOf(d) !== "Funded" && (+d.amtFinanced || 0) > 0).map(d => ({ type: "Contract in Transit", who: d.buyer, ref: d.id, acct: "205", age: daysSince(d.delivered || d.created), amount: d.amtFinanced || 0, stage: stageOf(d), dealId: d.id, lender: d.lender || "", deal: d, // Posting sets "Sent for Funding"; legacy posted deals without one read the same. funding: (d.fundingStatus || "Sent for Funding") + " · " + daysSince(d.sentForFundingAt || d.delivered || d.created) + "d", })), ...wr.filter(w => !w.paid && w.status !== "Debited" && !w.voided).map(w => ({ // RC33 fix round: what is still OPEN on the claim type: "Warranty Claim", who: w.ro, ref: w.claim, acct: "263", age: daysSince(w.submittedDate || w.closed), amount: Math.max(0, Math.round((+w.amount || 0) * 100) - Math.round((+w.received || 0) * 100) - Math.round((+w.debitedOpen || 0) * 100)) / 100, })), ...fr.filter(f => !f.received).map(f => ({ type: "Factory Incentive", who: f.program, ref: f.deal, acct: "261", age: f.age || 0, amount: f.claimed, })), ]; const arTotal = arRows.reduce((s, r) => s + r.amount, 0); // ── A/P from the engine ── const payables = DMS.payables(); const apOpen = payables.filter(p => p.status === "Open"); const apRows = apOpen.map(p => ({ ...p, age: daysSince(p.date) })); const apTotal = apRows.reduce((s, p) => s + p.amount, 0); // aging summaries const agingSummary = (rows) => { const b = { "0-30": 0, "31-60": 0, "61-90": 0, "90+": 0 }; rows.forEach(r => b[bucket(r.age)] += r.amount); return b; }; const arAging = agingSummary(arRows); const apAging = agingSummary(apRows); const br = DMS.bankRec(); const checklist = DMS.monthEndChecklist(); const meReady = checklist.every(c => c.done); const TABS = [ { k: "ar", label: `A/R Aging (${money(arTotal)})` }, { k: "ap", label: `A/P Aging (${money(apTotal)})` }, { k: "bank", label: "Bank Rec" }, { k: "monthend",label: "Month-End Close" }, ]; return (
{/* RC20 (fops): office only — ROs / parts sales since RC16 with no journal entry, "Post it now" per item */} {window.FopsNotInBooks && }
{TABS.map(t => ( ))}
{/* ── A/R AGING ── */} {tab === "ar" && ( <> {arRows.length === 0 && } {arRows.sort((a, b) => b.age - a.age).map((r, i) => ( ))}
TypeCustomer / SourceRef AcctAgeBucketAmountStage / Action
No open receivables. ✓
{r.type} {r.who}{r.lender ? · {r.lender} : null} {r.ref} {r.acct} {r.age}d {money(r.amount)} {r.dealId ? ( {r.funding} {window.DealAdjustButton && } ) : ""}
A/R is built live from posted-unfunded financed contracts (205 — cash deals are paid at delivery and never in transit), open warranty claims (263), and factory receivables (261). Mark Funded here or in Funding; other items resolve from the Dashboard or the Service/Sales screens.
)} {/* ── A/P AGING ── */} {tab === "ap" && ( <>
setModal({ type: "addAP" })} style={{ background: "var(--jb-yellow)", fontWeight: 700 }}>+ Enter Vendor Bill
{apRows.length === 0 && } {apRows.sort((a, b) => b.age - a.age).map((p, i) => ( ))}
VendorInvoiceAcct DateAgeBucketAmountAction
No open payables. ✓
{p.vendor} {p.inv} {p.acct} {new Date(p.date).toLocaleDateString()} {p.age}d {money(p.amount)}
)} {/* ── BANK REC ── */} {tab === "bank" && (
{br.account}
Statement date {br.statementDate ? new Date(br.statementDate).toLocaleDateString() : ""}
{(() => { const oc = (br.outstandingChecks || []).reduce((s, c) => s + c.amount, 0); const dit = (br.depositsInTransit || []).reduce((s, d) => s + d.amount, 0); const adjBank = (br.bankBalance || 0) - oc + dit; const diff = +(adjBank - (br.bookBalance || 0)).toFixed(2); return ( <>
Per Bank Statement
Less: Outstanding Checks
{(br.outstandingChecks || []).map(c => )}
Plus: Deposits in Transit
{(br.depositsInTransit || []).map(d => )}
Per Books
{Math.abs(diff) < 0.01 ? "✓ In balance" : `⚠ Out of balance by ${money(Math.abs(diff))}`}
act(DMS.clearBankRec())} style={{ background: br.cleared ? undefined : "var(--jb-yellow)", fontWeight: 700 }} disabled={br.cleared}> {br.cleared ? "✓ Reconciled" : "Clear Reconciliation"} {br.cleared && Reconciled {br.clearedDate ? new Date(br.clearedDate).toLocaleDateString() : ""}}
); })()}
)} {/* ── MONTH-END CLOSE ── */} {tab === "monthend" && (
Month-End Close Checklist
Live readiness driven by the integration engine. Each item turns green when its underlying exceptions clear.
{checklist.map(c => (
{c.done ? "✅" : "⬜"}
{c.label}
{c.detail}
{!c.done && c.key === "bank" && setTab("bank")}>Go to Bank Rec} {!c.done && c.key !== "bank" && go && go("home")}>Resolve on Dashboard}
))}
{meReady ? "✓ Ready to close the period" : "Not ready to close"}
{meReady ? "All schedules reconcile and exceptions are clear. You may post the financial statement and submit to GM." : `${checklist.filter(c => !c.done).length} item(s) outstanding. Resolve them before closing.`}
{ go && go("gmacct"); window.k2bToast && window.k2bToast("Opening GM Accounting to post & submit the statement.", "ok"); }}> Post Financial Statement & Submit to GM →
)}
ESC-Close go && go("accounting")}>F2-GL Inquiry go && go("docentry")}>F3-Doc Entry go && go("gmacct")}>F4-GM Statement go && go("home")}>F5-Dashboard
{modal && modal.type === "addAP" && setModal(null)} act={act} />}
); }; const AddAPDialog = ({ onClose, act }) => { const [f, setF] = React.useState({ vendor: "", inv: "", amount: "", acct: "0699" }); const set = (k, v) => setF(s => ({ ...s, [k]: v })); return ( Cancel { if (!f.vendor || !f.amount || !f.inv) return act({ ok: false, msg: "Vendor, invoice # and amount are required." }); const r = await act(DMS.addPayable(f)); if (r && r.ok) onClose(); }}>Enter Bill }>
Vendor: set("vendor", e.target.value)} style={aoInp} /> Invoice #: set("inv", e.target.value)} style={aoInp} /> Amount: set("amount", e.target.value.replace(/[^0-9.]/g, ""))} style={{ ...aoInp, width: 120 }} /> Expense Acct: set("acct", e.target.value)} style={{ ...aoInp, width: 100 }} />
); }; const AgingBar = ({ buckets, money, total, label }) => (
{["0-30", "31-60", "61-90", "90+"].map(b => { const tone = b === "0-30" ? "var(--jb-green, #16A34A)" : b === "31-60" ? "#B26A00" : "#C03546"; return (
{b} days
{money(buckets[b])}
); })}
Total {label}
{money(total)}
); const AgeTag = ({ b }) => { const tone = b === "0-30" ? { bg: "var(--jb-green-tint, #E6F7EC)", fg: "var(--jb-green, #16A34A)" } : b === "31-60" ? { bg: "var(--jb-amber-tint, #FBF3E0)", fg: "#9A6700" } : { bg: "#FBE4E6", fg: "#C03546" }; return {b}; }; const RecLine = ({ label, val, small, bold }) => (
{label} {val}
); const aoInp = { background: "var(--k2b-yellow, #FFFFE1)", border: "1px solid #808080", padding: "3px 6px", fontFamily: "var(--font-ui)", fontSize: 13, width: "100%", boxSizing: "border-box" }; const aoTable = { width: "100%", borderCollapse: "collapse", fontFamily: "'Courier Prime', monospace", fontSize: 12 }; const aoHead = { background: "#C0C0C0", color: "var(--k2b-maroon, #8a1c1c)", borderBottom: "1px solid #808080", position: "sticky", top: 0 }; const aTh = { padding: "3px 6px", textAlign: "left", whiteSpace: "nowrap" }; const aThL = { ...aTh }; const aThR = { ...aTh, textAlign: "right" }; const aThC = { ...aTh, textAlign: "center" }; const aTd = { padding: "3px 6px", color: "var(--k2b-navy, #11337A)" }; const aTdMono = { ...aTd, fontWeight: 700, color: "var(--k2b-blue-data-dk, #11337A)" }; const aTdR = { ...aTd, textAlign: "right" }; const aTdC = { ...aTd, textAlign: "center" }; const aoEmpty = { padding: 14, textAlign: "center", color: "var(--k2b-green, #16A34A)" }; const aoPanel = { background: "#FFF", border: "1px solid var(--jb-border, #C0C0C0)", borderRadius: 6, padding: "12px 14px" }; const aoPanelHd = { fontSize: 12, fontWeight: 700, color: "var(--k2b-maroon, #8a1c1c)", textTransform: "uppercase", letterSpacing: "0.04em", marginBottom: 8, borderBottom: "1px solid #DDD", paddingBottom: 4 }; Object.assign(window, { AcctOffice });