/* 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 — Funding. Tracks every POSTED deal to funded (ONE PATH: Posted →
// Funded = DONE). Financed deals live in Contracts in Transit until the lender
// pays — expected-vs-actual reconcile CODES any short-fund (bank fee, contract
// error, product cancellation, holdback, write-off) before CIT can clear
// (DMS.fundDealAmount). CASH deals were paid at delivery: no CIT, no JE —
// "Fund" simply closes them out through window.jbFundDeal.
const fwMoney = (n) => "$" + (Number(n) || 0).toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 0 });
const fwMoney2 = (n) => (n < 0 ? "-$" : "$") + Math.abs(Number(n) || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
// "Sent for Funding" is what posting a financed / lease deal sets (DMS.postDeal).
// "Not Submitted" stays only for legacy records.
const FW_STATUSES = ["Sent for Funding", "Not Submitted", "Submitted", "Conditioned", "Missing Stips", "Approved to Fund", "Funded Short", "Funded Full", "Chargeback Risk", "Unwound"];
const FW_STIPS = ["Driver license", "Proof of income", "Proof of residence", "Insurance", "Signed contract", "Title app", "Trade documents", "Payoff authorization", "Bank approval", "Menu signed", "Products remitted"];
const fwDays = (iso) => iso ? Math.floor((Date.now() - new Date(iso)) / 86400000) : 0;
// Legacy posted financed deals with no fundingStatus left the store at posting too.
const fwStatus = (d) => d.fundingStatus || "Sent for Funding";
const fwSentDays = (d) => fwDays(d.sentForFundingAt || d.delivered);
const fwDate = (iso) => iso ? new Date(iso).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "—";
const fwIsCash = (d) => (+d.amtFinanced || 0) <= 0 || /cash/i.test(String(d.dealType || ""));
const fwIsFunded = (d) => d.status === "Funded" || d.stage === "Funded" || ((window.DMS && window.DMS.dealStage) ? window.DMS.dealStage(d) === "Funded" : false);
const FW_TONE = (d) => {
if (/funded full/i.test(d.fundingStatus || "") || fwIsFunded(d)) return { bg: "#E6F7EC", fg: "#0E7A37" };
if (/funded short/i.test(d.fundingStatus || "")) return { bg: "#FBF3E0", fg: "#9A6700" };
if (/chargeback|unwound|missing/i.test(d.fundingStatus || "")) return { bg: "#FBE9E7", fg: "#B23A2E" };
if (/sent for funding/i.test(fwStatus(d))) return { bg: "var(--jb-navy, #11337A)", fg: "#FFF" };
return { bg: "var(--jb-blue-tint,#E5EFFA)", fg: "var(--jb-blue,#1B66A8)" };
};
// RC19 search chips on the posted-deals list (DealSearch.jsx.txt). "Aged" = the same rule as the "Aged > 14 days" tile.
const fwThisMonth = (d) => { const t = Date.parse(d.delivered || d.created || ""); if (!isFinite(t)) return false; const a = new Date(t), n = new Date(); return a.getFullYear() === n.getFullYear() && a.getMonth() === n.getMonth(); };
const FW_SEARCH_CHIPS = [
{ key: "unfunded", group: "fund", label: "Unfunded", test: (d) => !fwIsFunded(d) },
{ key: "funded", group: "fund", label: "Funded", test: (d) => fwIsFunded(d) },
{ key: "cash", group: "pay", label: "Cash", test: (d) => fwIsCash(d) },
{ key: "financed", group: "pay", label: "Financed", test: (d) => !fwIsCash(d) },
{ key: "aged", group: "aged", label: "Aged > 14 days", title: "Financed, not funded, delivered 14+ days ago (same as the tile above)", test: (d) => !fwIsFunded(d) && !fwIsCash(d) && fwDays(d.delivered) >= 14 },
{ key: "month", group: "month", label: "This month", title: "Delivered this calendar month", test: fwThisMonth },
];
const FundingWorkflow = ({ onClose, go }) => {
const D = window.DMS;
const [, force] = React.useReducer(n => n + 1, 0);
React.useEffect(() => (D && D.subscribe) ? D.subscribe(force) : undefined, []);
const act = (r) => Promise.resolve(r).then(x => { window.k2bToast && window.k2bToast(x.msg, x.ok ? "ok" : "warn", 3600); return x; }); // RC33 (mpx): the deal's checks are SERVER commands (promises)
// Every posted deal (cash AND financed); funded ones sort to the bottom.
const deals = ((D && D.deals) ? D.deals().filter(d => d.postedToGL && d.status !== "Dead") : [])
.slice().sort((a, b) => (fwIsFunded(a) ? 1 : 0) - (fwIsFunded(b) ? 1 : 0));
const pipeline = deals.filter(d => !fwIsFunded(d));
const [selId, setSelId] = React.useState(null);
const [modal, setModal] = React.useState(null);
React.useEffect(() => { if (selId && deals.find(d => d.id === selId)) return; if (pipeline[0]) setSelId(pipeline[0].id); else if (deals[0]) setSelId(deals[0].id); }, [deals.length]);
const sel = deals.find(d => d.id === selId) || null;
// RC19: one search box over the list (empty box = the list below exactly as before)
const ds = dsUseDealSearch({ deals, chips: FW_SEARCH_CHIPS, selectedId: selId, onSelect: setSelId, onOpen: (d) => setSelId(d.id) });
// "In CIT" = financed deals only; cash deals have nothing in transit.
const citTotal = pipeline.filter(d => !fwIsCash(d)).reduce((s, d) => s + (+d.amtFinanced || 0), 0);
const cashCount = pipeline.filter(fwIsCash).length;
const aged = pipeline.filter(d => !fwIsCash(d) && fwDays(d.delivered) >= 14).length;
return (
{[["In CIT (financed, unfunded)", fwMoney(citTotal), "var(--jb-navy)"], ["Deals in pipeline", pipeline.length, "var(--jb-blue)"],
["Cash — awaiting close", cashCount, cashCount ? "var(--jb-green)" : "var(--jb-text-muted)"],
["Aged > 14 days", aged, aged ? "var(--k2b-maroon)" : "var(--jb-green)"]].map(([l, v, c], i) => (
))}
{deals.length > 0 &&
}
{deals.length === 0 &&
No posted deals. Post a deal from the Cash Deal or Finance Deal screen to see it here.
}
{ds.list.map(d => {
const on = d.id === selId; const t = FW_TONE(d); const dd = fwDays(d.delivered);
const funded = fwIsFunded(d); const cash = fwIsCash(d);
return (
setSelId(d.id)} style={{ padding: "10px 14px", borderBottom: "1px solid var(--jb-border)", cursor: "pointer", background: on ? "var(--jb-blue-tint, #E5EFFA)" : "transparent", borderLeft: on ? "3px solid var(--jb-blue)" : "3px solid transparent", opacity: funded ? 0.7 : 1 }}>
{d.id}
{cash &&
CASH }
{funded
?
DONE ✓
:
{cash ? "Paid at delivery" : `${fwStatus(d).toUpperCase()} · ${fwSentDays(d)}d`} }
{d.buyer}
{cash ? "Cash · " + fwMoney(d.cashDown) : (d.lender || "—") + " · " + fwMoney(d.amtFinanced)}
{!funded &&
= 14 ? "var(--k2b-maroon)" : "#999", marginTop: 2 }}>{d.delivered ? `delivered ${dd}d ago` : "not delivered"}{!cash && d.sentForFundingAt ? ` · sent ${fwDate(d.sentForFundingAt)}` : ""}{(d.adjustments || []).length ? ` · ${(d.adjustments || []).length} adj.` : ""}
}
);
})}
{sel ?
fwIsCash(sel) ? (window.jbFundDeal ? window.jbFundDeal(sel.id) : act({ ok: false, msg: "Ledger offline." })) : setModal({ type: "fund", deal: sel })} /> : Select a deal.
}
ESC-Close
go && go("deal")}>Deal Pipeline
go && go("cashcommand")}>Cash Command
go && go("accounting")}>General Ledger
{modal && modal.type === "fund" &&
setModal(null)} />}
);
};
const FwDetail = ({ deal, act, go, onFund }) => {
const D = window.DMS;
const funded = fwIsFunded(deal);
const cash = fwIsCash(deal);
const check = deal.fundingCheck || {};
const doneCount = FW_STIPS.filter(s => check[s]).length;
const stage = (D && D.dealStage) ? D.dealStage(deal) : (deal.stage || "Posted");
return (
{deal.id}
{cash && CASH }
{stage}
{deal.buyer}
{deal.vehicle} · {cash ? "no lender — paid in full" : (deal.lender || "no lender")}
{cash ? "Expected Funding" : "Expected Funding (CIT)"}
{cash
?
Cash — paid at delivery {fwMoney(deal.cashDown)}
:
{fwMoney(deal.amtFinanced)}
}
{!cash && Array.isArray(deal.fundings) && (window.dcxLiveFundings ? window.dcxLiveFundings(deal).length > 0 : deal.fundings.some(f => f && f.kind === "lender")) && !funded && ( /* RC23 (corr): a reversed funding is not "partly funded" */
Partly funded — {fwMoney2(window.DMS.dealCitOpen ? window.DMS.dealCitOpen(deal) / 100 : 0)} still in transit
)}
{/* Big status line — financed / lease: where the contract is; cash: paid at delivery */}
{!funded && (
{cash ? "CASH — paid at delivery" : (fwStatus(deal) === "Sent for Funding" ? "SENT OFF FOR FUNDING" : fwStatus(deal).toUpperCase())}
{cash
? `No lender — ${fwMoney(deal.cashDown)} collected${deal.delivered ? " on " + fwDate(deal.delivered) : ""}. Mark Funded to close it out.`
: <>Sent off for funding to {deal.lender || "the lender"} on {fwDate(deal.sentForFundingAt || deal.delivered)} — expected {fwMoney2(deal.amtFinanced)}
· {fwSentDays(deal)}d in transit >}
{window.DealAdjustButton
?
: null}
)}
{/* RC23 (corr): Reverse funding (the office) — a funded deal is unwound only after its funding is reversed */}
{window.DealCorrectButtons && window.dcxLiveFundings && window.dcxLiveFundings(deal).length > 0 && (
)}
{funded && window.DealAdjustButton && (
)}
{/* status pipeline — lender-facing; not applicable to a cash deal */}
{!cash && (
Funding Status
{FW_STATUSES.filter(s => !/funded (short|full)/i.test(s) && (s !== "Not Submitted" || deal.fundingStatus === "Not Submitted")).map(s => (
act(D.setFundingStatus(deal.id, s))}
style={{ cursor: funded ? "default" : "pointer", fontSize: 11.5, padding: "3px 10px", borderRadius: 999,
border: "1px solid " + (fwStatus(deal) === s ? "var(--jb-blue)" : "var(--jb-border)"),
background: fwStatus(deal) === s ? "var(--jb-blue)" : "#fff",
color: fwStatus(deal) === s ? "#fff" : "var(--jb-text-dim)", fontWeight: fwStatus(deal) === s ? 800 : 600 }}>{s}
))}
)}
{/* stip checklist */}
{Array.isArray(deal.adjustments) && deal.adjustments.length > 0 && window.DealAdjustHistory && (
)}
{deal.postedToGL && !deal.imported &&
}
{funded ? (
DONE ✓ · {cash ? "Cash deal closed — paid in full" : (deal.fundingStatus || "Funded")}
{!cash && (
Expected {fwMoney2(deal.amtFinanced)}
Received {fwMoney2(deal.fundedActual != null ? deal.fundedActual : deal.amtFinanced)}
{Math.abs(deal.fundedDiff || 0) > 0.01 && <>Difference · {deal.fundedDiffReason} {fwMoney2(-(deal.fundedDiff || 0))} >}
)}
) : (
{cash ? "✓ Mark Funded (cash — paid at delivery)" : "💰 Reconcile & Fund →"}
)}
);
};
// RC33 (mpx): the deal's own checks — the trade payoff (DR 303), the F&I product remittances (DR 302 / 302G) and a lender's over-funding paid
// back (DR 331) — SERVER commands that write the check in the same step and keep the deal in step (a voided check reopens the amount).
const FwDealChecks = ({ deal, act }) => {
const D = window.DMS;
if (!D || !D.dealPayoffCheck || !(D.can && D.can("acct"))) return null;
const sumC = (l) => (Array.isArray(l) ? l : []).filter(x => x && !x.voided).reduce((t, x) => t + (+x.amountCents || 0), 0);
const overLeft = Math.round((+deal.lenderOverpaid || 0) * 100) - sumC(deal.lenderRefunds);
const payoffLeft = Math.round((+deal.tradePayoff || 0) * 100) - sumC(deal.payoffChecks);
const ask = (q, d) => { const v = window.prompt(q, d || ""); return v === null ? null : String(v).trim(); };
const c2 = (c) => (c / 100).toFixed(2);
return (
{payoffLeft > 0 && { const payee = ask("Trade payoff — pay to (the lien holder):", deal.tradeLienholder || deal.lienholder || ""); if (!payee) return; const a = ask("Amount:", c2(payoffLeft)); if (!a) return; act(D.dealPayoffCheck(deal.id, { payee, amount: a })); }}>Write trade payoff check ({fwMoney2(payoffLeft / 100)}) }
{(+deal.vscSale || 0) + (+deal.gapSale || 0) > 0 && { const which = ask("Remit which product payable? 302 (service contract) or 302G (GAP)", "302"); if (!which) return; const payee = ask("Pay to (the product provider):"); if (!payee) return; const a = ask("Amount:"); if (!a) return; act(D.dealRemit(deal.id, { payable: which.toUpperCase(), payee, amount: a })); }}>Remit F&I product (check) }
{overLeft > 0 && { const ref = ask(`Refund ${fwMoney2(overLeft / 100)} the lender over-funded.\n\nPaid back by ACH / wire: type the confirmation number. Leave blank to write a check.`); if (ref === null) return; const why = ask("Reason:", "Lender over-funded the contract"); if (!why) return; act(D.lenderRefund(deal.id, { amount: c2(overLeft), method: ref ? "ach" : "check", ref: ref || undefined, reason: why })); }}>Refund lender over-funding ({fwMoney2(overLeft / 100)}) }
);
};
// RC21 (deals): the funding is recorded by the SERVER (POST /api/deals/fund): "Expected" = what is still in contracts in transit for the
// deal; a short amount is either CODED (the CIT clears, the gap goes to the chosen account) or PARTIAL (the rest stays in transit —
// the deal stays unfunded until the rest arrives). More than is in transit is refused (an overpayment is not booked here).
const FW_PARTIAL = "__partial__";
const FwFundModal = ({ deal, act, onClose }) => {
const expected = (window.DMS.dealCitOpen ? window.DMS.dealCitOpen(deal) / 100 : (+deal.amtFinanced || 0));
const [actual, setActual] = React.useState(String(expected.toFixed(2)));
const [reason, setReason] = React.useState("Bank fee");
const [ref, setRef] = React.useState("");
const [busy, setBusy] = React.useState(false);
const amt = parseFloat(String(actual).replace(/[^0-9.]/g, "")) || 0;
const diff = +(expected - amt).toFixed(2);
const needsReason = Math.abs(diff) > 0.01;
const reasons = window.DMS.FUND_DIFF_ACCTS ? Object.keys(window.DMS.FUND_DIFF_ACCTS) : ["Bank fee", "Contract error", "Product cancellation", "Holdback", "Short fund"];
const submit = async () => {
if (busy) return;
setBusy(true);
const over = diff < -0.01; // RC33 (mpx): the lender paid MORE than is in transit — the extra is held (331) for a refund to the lender
const r = await window.DMS.fundDealAmount(deal.id, amt, needsReason && !over && reason !== FW_PARTIAL ? reason : null, { ref: ref.trim() || undefined, overpay: over ? "hold" : undefined });
setBusy(false); act(r); if (r.ok) onClose();
};
const inp = { width: "100%", boxSizing: "border-box", padding: "8px 10px", border: "1px solid #C8CCD2", borderRadius: 4, fontSize: 14, fontFamily: "var(--jb-mono)" };
return (
Cancel {busy ? "Posting…" : (needsReason && reason === FW_PARTIAL ? "Post Partial Funding" : "Post Funding (clear CIT)")} >}>
Expected (CIT)
{fwMoney2(expected)}
{diff > 0 ? "Funded SHORT by" : diff < 0 ? "Funded OVER by" : "Funds match"}
{fwMoney2(Math.abs(diff))}
Lender's funding reference (optional — the same reference is never booked twice)
setRef(e.target.value)} data-fw="ref" style={{ ...inp, background: "#FFFEF4", fontFamily: "var(--jb-font)" }} />
{diff < -0.01 && (
The lender paid {fwMoney2(-diff)} more than is in transit. Posting HOLDS the extra on 331 (owed back to the lender) — pay it back with “Refund lender over-funding” on this deal.
)}
{needsReason && diff > 0.01 && (
Code the difference to:
setReason(e.target.value)} style={{ ...inp, fontFamily: "var(--jb-font)" }}>
{reasons.map(rn => {rn} → acct {(window.DMS.FUND_DIFF_ACCTS[rn] || {}).acct} )}
{diff > 0 && Partial — the rest is still coming from the lender }
{reason === FW_PARTIAL ? `Only ${fwMoney2(amt)} clears; ${fwMoney2(diff)} stays in contracts in transit and the deal stays unfunded until the rest arrives.` : `CIT clears in full; the gap posts to ${(window.DMS.FUND_DIFF_ACCTS[reason] || {}).label}.`}
)}
);
};
Object.assign(window, { FundingWorkflow });