/* 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 — A/R Defaults (legacy "ARDFT" screen, scan 5). // Left: the dealership's default A/R posting config. Right: Setup Files — // Account Types, Dunning Messages, Price Matrix, Letters, Tax Codes. // Saves through DMSCore so every other A/R screen picks up the new config. // Module-scope row + input (defined OUTSIDE the component so they are not // recreated each keystroke — that recreation remounted the and limited // typing to one character at a time). const ARDRow = ({ label, children, hint }) => (
{label}
{children}{hint ? {hint} : null}
); const ARDInp = (props) => ; // Numeric input (audit A-06): keeps a TEXT draft while focused so a decimal // point can be typed and the box can be cleared; parses and commits on blur // (or Enter). `int` → whole numbers only. const ARDNum = ({ value, onCommit, int, style }) => { const fmt = (v) => (v === undefined || v === null || v === "" ? "" : String(v)); const [text, setText] = React.useState(() => fmt(value)); const [focused, setFocused] = React.useState(false); React.useEffect(() => { if (!focused) setText(fmt(value)); }, [value, focused]); const parse = (s) => { const c = String(s || "").replace(int ? /[^0-9\-]/g : /[^0-9.\-]/g, ""); const n = int ? parseInt(c, 10) : parseFloat(c); return isNaN(n) ? 0 : n; }; return ( { setFocused(true); window.jbFocusSelect && window.jbFocusSelect(e); }} onChange={(e) => setText(e.target.value)} onBlur={() => { setFocused(false); const n = parse(text); setText(fmt(n)); onCommit(n); }} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); e.currentTarget.blur(); } }} style={style} /> ); }; const ARDefaults = ({ onClose, go }) => { const [, force] = React.useReducer(n => n + 1, 0); React.useEffect(() => DMS.subscribe(force), []); const saved = DMS.arDefaults(); const [d, setD] = React.useState(saved); const [dirty, setDirty] = React.useState(false); const [file, setFile] = React.useState(null); // open setup-file dialog const set = (k, v) => { setD(s => ({ ...s, [k]: v })); setDirty(true); }; const taxName = (DMS.arTaxCodes().find(t => t.code === d.defaultTaxCode) || {}).name || ""; const Row = ARDRow; const Inp = ARDInp; const setupFiles = [ { key: "types", label: "Account Types", desc: `${DMS.arAccountTypes().length} types` }, { key: "dunning", label: "Dunning Messages",desc: `${DMS.arDunningMessages().length} messages` }, { key: "matrix", label: "Price Matrix", desc: "5 markup tiers" }, { key: "letters", label: "Letters", desc: `${DMS.arLetterTemplates().length} templates` }, { key: "tax", label: "Tax Codes", desc: `${DMS.arTaxCodes().length} codes` }, ]; return (
{/* Defaults */}
Defaults
set("arAccount", e.target.value)} style={{ width: 70, fontFamily: "var(--jb-mono)" }} /> {d.arAccountName} set("financeChargeAcct", e.target.value)} style={{ width: 70, fontFamily: "var(--jb-mono)" }} /> {d.financeChargeAcctName} set("financeChargeJournal", e.target.value)} style={{ width: 50, fontFamily: "var(--jb-mono)" }} /> {d.financeChargeJournalName} set("monthlyFinanceChargePct", n)} style={{ width: 70, textAlign: "right", fontFamily: "var(--jb-mono)" }} /> % set("graceDays", n)} style={{ width: 50, textAlign: "right", fontFamily: "var(--jb-mono)" }} /> days {taxName} set("depositJournal", e.target.value)} style={{ width: 50, fontFamily: "var(--jb-mono)" }} /> {d.depositJournalName}
Credit Card
set("creditCardFeePct", n)} style={{ width: 70, textAlign: "right", fontFamily: "var(--jb-mono)" }} /> % set("creditCardJournal", e.target.value)} style={{ width: 50, fontFamily: "var(--jb-mono)" }} /> {d.creditCardJournalName} set("creditCardDepositAcct", e.target.value)} style={{ width: 70, fontFamily: "var(--jb-mono)" }} /> set("creditCardFeeAcct", e.target.value)} style={{ width: 70, fontFamily: "var(--jb-mono)" }} />
{/* Setup Files */}
Setup Files
{setupFiles.map(s => ( ))}
ESC-Close { _arToast(DMS.arUpdateDefaults(d)); setDirty(false); }}>F9-Save {dirty ? Unsaved changes : null}
go && go("arproc")}>Processing →
{file === "types" && setFile(null)} />} {file === "tax" && setFile(null)} />} {file === "dunning" && setFile(null)} />} {file === "letters" && setFile(null)} />} {file === "matrix" && setFile(null)} />}
); }; /* Account Types */ const ARTypesDialog = ({ onClose }) => { const [rows, setRows] = React.useState(DMS.arAccountTypes().map(r => ({ ...r }))); const upd = (i, k, v) => setRows(rs => rs.map((r, j) => j === i ? { ...r, [k]: v } : r)); return ( Cancel { _arToast(DMS.arSetTable("arAccountTypes", rows)); onClose(); }}>Save}> {rows.map((r, i) => ( ))}
CodeNameTermsGraceFC Exempt
{r.code} upd(i, "name", e.target.value)} style={{ width: "100%" }} /> upd(i, "terms", e.target.value)} style={{ width: "100%" }} /> upd(i, "graceDays", n)} style={{ width: 50, textAlign: "right" }} /> upd(i, "fcExempt", e.target.checked)} />
); }; /* Tax Codes */ const ARTaxDialog = ({ onClose }) => { const [rows, setRows] = React.useState(DMS.arTaxCodes().map(r => ({ ...r }))); const upd = (i, k, v) => setRows(rs => rs.map((r, j) => j === i ? { ...r, [k]: v } : r)); const add = () => setRows(rs => [...rs, { code: "", name: "", rate: 0, desc: "" }]); return ( + AddCancel { _arToast(DMS.arSetTable("arTaxCodes", rows.filter(r => r.code))); onClose(); }}>Save}> {rows.map((r, i) => ( ))}
CodeNameRate %Description
upd(i, "code", e.target.value)} style={{ width: 70 }} /> upd(i, "name", e.target.value)} style={{ width: 90 }} /> upd(i, "rate", n)} style={{ width: 70, textAlign: "right" }} /> upd(i, "desc", e.target.value)} style={{ width: "100%" }} />
); }; /* Dunning Messages */ const ARDunningDialog = ({ onClose }) => { const [rows, setRows] = React.useState(DMS.arDunningMessages().map(r => ({ ...r }))); const upd = (i, v) => setRows(rs => rs.map((r, j) => j === i ? { ...r, message: v } : r)); return ( Cancel { _arToast(DMS.arSetTable("arDunningMessages", rows)); onClose(); }}>Save}>
{rows.map((r, i) => (
Oldest item {r.bucket} days