/* 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. */ // K2B DMS — GL Posting Map (full, editable). // Every transaction in every department, fully mapped to the chart of accounts: // Vehicle Sales & F&I · Inventory & Floorplan · Service · Parts · Core Bank · // Accounting. Each editable line's account number can be changed inline — it // writes through DMS.setAcctMap so EVERY future posting uses the new account // (one source of truth, shared with Settings → GL Flow). const pmAcctName = (acct) => { const a = String(acct); const coa = window.GM_COA || []; const hit = coa.find(e => String(Array.isArray(e) ? e[0] : (e && (e.acct || e.num))) === a); if (hit) return Array.isArray(hit) ? (hit[1] || "") : (hit.name || hit.desc || ""); const fb = { "300": "Accounts payable — trade", "325": "Payroll taxes withheld", "246": "Core inventory", "999": "Labor applied / clearing" }; return fb[a] || ""; }; const PM_DEPT_ICON = { "Vehicle Sales & F&I": "🚗", "Inventory & Floorplan": "🏷", "Service": "🔧", "Parts": "📦", "Core Bank": "🔋", "Accounting": "📒", }; // An account cell — editable input when in edit mode AND the line is // account-mapped. HOISTED to module scope (was previously defined inside // GLPostingMap, which gave it a new function identity every render and // remounted the on each keystroke — the "single character at a time" // bug). Props carry the per-render closures it needs. const PMAcctCell = ({ line, editing, draft, setDraft }) => { if (editing && line.editable) { const val = line.key in draft ? draft[line.key] : line.acct; return ( setDraft(d => ({ ...d, [line.key]: e.target.value }))} style={{ width: 70, padding: "2px 5px", border: "1px solid #C8CCD2", fontFamily: "var(--jb-mono, monospace)", fontSize: 12.5, fontWeight: 700, color: "var(--jb-navy)", textAlign: "center", background: (line.key in draft && draft[line.key] !== line.acct) ? "#FFFBEA" : "#FFF" }} /> ); } return ( {line.acct} ); }; const GLPostingMap = ({ onClose, go }) => { const [, force] = React.useReducer(n => n + 1, 0); React.useEffect(() => (window.DMS && window.DMS.subscribe) ? window.DMS.subscribe(force) : undefined, []); const D = window.DMS; const map = D && D.getPostingMap ? D.getPostingMap() : []; const [editing, setEditing] = React.useState(false); const [draft, setDraft] = React.useState({}); // acctKey -> new number const [filter, setFilter] = React.useState("ALL"); const dirty = Object.keys(draft).length > 0; const save = () => { const r = D.setAcctMap(draft); setDraft({}); window.k2bToast && window.k2bToast(r.msg, r.ok ? "ok" : "warn", 3600); return r; }; // RC35 (brd): a store switch asks first (SPEC 4.4) if (window.useK2BUnsaved) window.useK2BUnsaved("posting-map", { label: "GL Posting Map", isDirty: () => dirty, save: () => { const r = save(); return !!(r && r.ok !== false); }, discard: () => setDraft({}) }); const reset = () => { if (!window.confirm("Reset every GL account mapping back to the standard GM chart?")) return; const r = D.resetAcctMap(); setDraft({}); window.k2bToast && window.k2bToast(r.msg, "ok", 3200); }; const depts = filter === "ALL" ? map : map.filter(d => d.dept === filter); const sideColor = (s) => s.startsWith("DR") ? "var(--jb-navy, #11337A)" : "var(--k2b-maroon, #8a1c1c)"; const filterBtn = (k, label) => ( ); return (
| {l.side} | {pmAcctName(l.key in draft ? draft[l.key] : l.acct) || l.memo} {pmAcctName(l.acct) && · {l.memo}} |