/* 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 Network Solutions (W26) — Payroll Process (PREdit). // Full payroll workflow: time entry by employee, generate from time cards, // review/edit gross, print checks (preview), post to employee record, // post to GL, EFTPS, and the "special" un-post / register / journal queries. const EMPLOYEES = [ { id: "E-101", name: "BREWER, JARED", dept: "SAL", type: "Salary", rate: 4_166.66, comm: 8_420.00 }, { id: "E-102", name: "MORALES, ELENA P.", dept: "SAL", type: "Salary", rate: 3_750.00, comm: 7_185.50, deductions: [{ label: "Child Support (IWO)", amount: 175.00, payee: "MO Family Support Payment Center" }] }, { id: "E-103", name: "WALSH, KATHRYN", dept: "SAL", type: "Salary", rate: 3_500.00, comm: 6_280.00 }, { id: "E-104", name: "HOFFMAN-NGUYEN, A.", dept: "F&I", type: "Salary", rate: 4_500.00, comm: 9_402.45 }, { id: "E-201", name: "WILSON, RICK", dept: "SVC", type: "FlatRate", rate: 32.50, hrs: 86 }, { id: "E-202", name: "ALVAREZ, JOEL", dept: "SVC", type: "FlatRate", rate: 36.00, hrs: 82 }, { id: "E-203", name: "THOMAS, DARRYL", dept: "SVC", type: "FlatRate", rate: 34.00, hrs: 78 }, { id: "E-204", name: "CASTRO, MIGUEL", dept: "SVC", type: "FlatRate", rate: 30.00, hrs: 88 }, { id: "E-301", name: "TANNER, BRYCE", dept: "SVC", type: "Hourly", rate: 24.00, hrs: 80, ot: 4 }, { id: "E-401", name: "RILEY, DELANEY", dept: "PRT", type: "Hourly", rate: 22.50, hrs: 80, ot: 0 }, { id: "E-402", name: "FITZGERALD, AUDREY", dept: "PRT", type: "Hourly", rate: 21.00, hrs: 78, ot: 2 }, { id: "E-501", name: "KEYES, SHANNON", dept: "SVC", type: "Hourly", rate: 26.00, hrs: 80, ot: 4 }, { id: "E-601", name: "BAILEY, MEGAN", dept: "ADM", type: "Hourly", rate: 23.00, hrs: 80, ot: 0 }, ]; const pr$ = (n) => (n || 0).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); // Post a REAL journal entry to the shared GL — same path the DMS cascades use // (DMS journal collection + the Accounting JEStore), so payroll postings move // the chart-of-accounts balances like every other module. const prPostJE = (entry) => { if (typeof window === "undefined") return; if (window.DMS && typeof window.DMS.upsertJournalEntry === "function") window.DMS.upsertJournalEntry(entry); if (window.__k2bJEStore) window.__k2bJEStore.add(entry, { posted: true }); }; const prToday = () => new Date().toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "2-digit" }); // Compute gross/withholdings/net for one employee record const computePay = (e) => { let gross = 0; if (e.type === "Salary") gross = e.rate + (e.comm || 0); else if (e.type === "FlatRate") gross = (e.hrs || 0) * e.rate; else gross = (e.hrs || 0) * e.rate + (e.ot || 0) * e.rate * 1.5; const fed = gross * 0.16; const fica = gross * 0.0765; const state = gross * 0.0425; const ret = gross * 0.04; // Custom per-employee deductions (child support / garnishments / insurance // / union dues) — editable in PREdit, paid out to payees as vendor checks. const ded = (e.deductions || []).reduce((s, d) => s + (+d.amount || 0), 0); const net = gross - fed - fica - state - ret - ded; return { gross, fed, fica, state, ret, ded, net }; }; // Today as MM/DD/YYYY (audit P2-4 — no hard-coded demo dates). const prTodayMDY = () => new Date().toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }); const PayrollProcess = ({ onClose, go }) => { const [payDate, setPayDate] = React.useState(prTodayMDY); // Employees live in the shared DMS store (persisted, wiped with the store, // editable in Settings → Staff & Techs). Demo list is only the lazy seed. const [employees, _setEmployees] = React.useState(() => (window.DMS && DMS.employees) ? DMS.employees().map(e => ({ ...e })) : EMPLOYEES); const setEmployees = (es) => { _setEmployees(es); if (window.DMS && DMS.setEmployees) DMS.setEmployees(es); }; const [modal, setModal] = React.useState(null); // 'time'|'gen'|'edit'|'checks'|'postEmp'|'postGL'|'postEFTPS'|'unpost'|'stubs'|'register'|'journal'|'setup'|'help' const [stage, setStage] = React.useState("draft"); // draft | generated | printed | postedEmp | postedGL | done const [flash, setFlash] = React.useState(""); const toast = (m) => { setFlash(m); setTimeout(() => setFlash(""), 2800); }; // Users → payroll: any login without a payroll record is added on open. React.useEffect(() => { // round-2 regress finding: only a role that may change pay plans (the office) auto-adds logins — Parts / Service stations no longer fire 21 refused writes if (window.DMS && DMS.syncUsersToPayroll && (typeof DMS.can !== 'function' || DMS.can('acct'))) { const r = DMS.syncUsersToPayroll(); if (r.added) { _setEmployees(DMS.employees().map(e => ({ ...e }))); toast(r.msg); } } }, []); const needSetup = employees.filter(e => e.needsSetup && !(+e.rate) && !(+e.comm)); const totals = employees.reduce((s, e) => { const p = computePay(e); return { gross: s.gross + p.gross, net: s.net + p.net, tax: s.tax + (p.fed + p.fica + p.state), fed: s.fed + p.fed, fica: s.fica + p.fica, state: s.state + p.state, ret: s.ret + (p.ret || 0), ded: s.ded + (p.ded || 0), }; }, { gross: 0, net: 0, tax: 0, fed: 0, fica: 0, state: 0, ret: 0, ded: 0 }); const isStage = (...ss) => ss.includes(stage); const requires = (need) => { const order = ["draft","generated","printed","postedEmp","postedGL","done"]; if (order.indexOf(stage) < order.indexOf(need)) { toast(`Must ${need === "generated" ? "Generate Payroll" : need === "printed" ? "Print Checks" : need === "postedEmp" ? "Post to Employee" : "Post Checks to GL"} first.`); return false; } return true; }; const StageBadge = ({ on, label }) => ( {on ? "✓ " : ""}{label} ); const stageDone = { generated: ["generated","printed","postedEmp","postedGL","done"].includes(stage), printed: ["printed","postedEmp","postedGL","done"].includes(stage), postedEmp: ["postedEmp","postedGL","done"].includes(stage), postedGL: ["postedGL","done"].includes(stage), done: stage === "done", }; return (
{}} onMax={() => {}} /> {/* Run flow — where you are in the payroll run */}
{["1 · Employees & hours", "2 · Deductions", "3 · Review checks", "4 · Print & post to GL"].map((s, i) => ( {s} {i < 3 && →} ))} Staff list: Settings → Staff & Techs · child support & garnishments: Edit → Deductions
{flash && (
✓ {flash}
)}
{needSetup.length > 0 && (
⚠ {needSetup.length} new employee{needSetup.length !== 1 ? "s" : ""} from Users need pay setup: {needSetup.map(e => e.name).join(", ")}
)} {/* Header: pay date + stage pills + KPIs */}
Payroll Date setPayDate(e.target.value)} style={{ background: "#FFFEF4", border: "1px solid #C8CCD2", borderRadius: 6, padding: "4px 8px", width: 110, color: "var(--jb-text, #1C2530)", fontFamily: "var(--jb-mono, monospace)", fontSize: 14, fontWeight: 700 }} />
setModal("setup")}>🔧 Setup Esc-Close
{[["Employees", employees.length, "var(--jb-navy, #11337A)"], ["Gross Pay", "$" + pr$(totals.gross), "var(--jb-blue, #1B66A8)"], ["Taxes + Deductions", "$" + pr$(totals.tax + totals.ret + totals.ded), "#9A6700"], ["Net Pay", "$" + pr$(totals.net), "var(--jb-green, #16A34A)"]].map(([lab, val, c]) => (
{lab}
{val}
))}
{/* Standard Functions */}
Run Payroll
setModal("gen")}>1 · Generate Payroll requires("generated") && setModal("edit")}>2 · Edit Payroll requires("generated") && setModal("checks")}>3 · Print Checks requires("printed") && setModal("postEmp")}>4 · Post To Employee requires("postedEmp") && setModal("postGL")}>5 · Post Checks to GL requires("postedEmp") && setModal("postEFTPS")}>6 · Post EFTPS to GL requires("postedGL") && setModal("payded")}>7 · Pay Deductions… go && go("accounting")}>Go to GL →
Special
stageDone.postedGL ? setModal("unpost") : toast("Nothing posted to un-post yet.")}> Unpost Payroll setModal("stubs")}>Check Stubs setModal("register")}>Check Register setModal("journal")}>Payroll Journal
{/* Employee payroll preview list */}
Employee Payroll Preview · {employees.length} {stage === "draft" ? "DRAFT — not yet generated" : stage.toUpperCase()}
Emp #
Name
Dept
Type
Hrs
Gross
Net Pay
{employees.map((e, i) => { const p = computePay(e); const ns = e.needsSetup && !(+e.rate) && !(+e.comm); return (
{e.id}
{e.name}{ns && · needs pay setup}
{e.dept}
{e.type}
{e.type === "Salary" ? "—" : (e.hrs || 0) + (e.ot ? "+" + e.ot : "")}
{pr$(p.gross)}
{pr$(p.net)}
); })}
—
{employees.length} employees
{pr$(totals.gross)}
{pr$(totals.net)}
{/* Quick menu (right rail) */}
Quick Menu
{[ ["👥 Employees & hours", () => setModal("time")], ["▶ Generate payroll", () => setModal("gen")], ["📄 Reports", () => setModal("journal")], ["🏦 Post to GL", () => setModal("postGL")], ["🔧 Setup", () => setModal("setup")], ["← Back", () => onClose()], ].map(([it, fn]) => ( ))}
New logins appear here automatically. Pay setup, child support & garnishments: Edit Payroll → Deductions.
{modal === "help" && setModal(null)} />} {modal === "time" && setModal(null)} onSave={(es) => { setEmployees(es); setModal(null); toast("Time entries saved."); }} />} {modal === "gen" && setModal(null)} onRun={() => { setStage("generated"); setModal(null); toast(`Payroll generated for ${employees.length} employees.`); }} />} {modal === "edit" && setModal(null)} onSave={(es) => { setEmployees(es); setModal(null); toast("Edits saved."); }} />} {modal === "checks" && setModal(null)} onPrint={(start) => { setStage("printed"); setModal(null); toast(`${employees.length} checks printed starting at #${start}.`); }} />} {modal === "postEmp" && setModal(null)} onPost={() => { setStage("postedEmp"); setModal(null); toast("Posted to employee YTD records."); }} />} {/* RC33 (mpx): Post Checks to GL / EFTPS / Un-post are SERVER commands — the payroll run (PRL-) marks the commissions it pays PAID and the chargebacks it takes off DEDUCTED in the same step; the tax deposit carries the real EFTPS / state confirmation (typed); Un-post reverses THIS run (a linked entry) and puts its commissions back to owed. Nothing here writes a journal entry itself any more. */} {modal === "postGL" && setModal(null)} onPost={async () => { const D = window.DMS; if (!D || !D.postPayroll) { toast("Reload the DMS (Ctrl+F5) — payroll posts on the server."); return; } const all = (D.commissions && D.commissions()) || []; const comm = all.filter(c => c && c.kind !== "chargeback" && !c.paid && !c.reversed); const cbs = all.filter(c => c && c.kind === "chargeback" && c.dueNextPayroll && !c.deducted); const r = await D.postPayroll({ payDate, period: payDate, commissionIds: comm.map(c => c.id), chargebackIds: cbs.map(c => c.id), employees: employees.map(e => { const p = computePay(e); return { id: e.id, name: e.name, gross: p.gross.toFixed(2), fed: p.fed.toFixed(2), fica: p.fica.toFixed(2), state: p.state.toFixed(2), ret: (p.ret || 0).toFixed(2), ded: (p.ded || 0).toFixed(2) }; }) }); if (r.ok) { setStage("postedGL"); setModal(null); try { window.__k2bLastPayrollRun = r.runId; } catch (e) {} } toast(r.msg); }} />} {modal === "postEFTPS" && setModal(null)} onPost={async (conf, stateConf) => { const D = window.DMS; const fedAmt = +(totals.fed + totals.fica).toFixed(2), stAmt = +(+totals.state || 0).toFixed(2); const r1 = await D.payTaxDeposit({ kind: "federal", amount: fedAmt.toFixed(2), period: payDate, method: conf ? "eft" : "check", ref: conf || undefined, breakdown: { fit: totals.fed.toFixed(2), fica: totals.fica.toFixed(2) } }); if (!r1.ok) { toast(r1.msg); return; } const r2 = stAmt > 0.004 ? await D.payTaxDeposit({ kind: "state", amount: stAmt.toFixed(2), period: payDate, method: stateConf ? "eft" : "check", ref: stateConf || undefined }) : { ok: true, msg: "" }; if (r2.ok) { setStage("done"); setModal(null); } toast([r1.msg, r2.msg].filter(Boolean).join(" ")); }} />} {modal === "payded" && setModal(null)} onDone={(n, amt) => { setModal(null); toast(`${n} deduction check(s) written — $${pr$(amt)} — Dr 300A · Cr 202. See Check Writing register.`); }} />} {modal === "unpost" && setModal(null)} onUnpost={async () => { const D = window.DMS; const runs = D.payrollRuns ? await D.payrollRuns() : []; const last = runs.find(x => !x.reversed); if (!last) { setModal(null); toast("No payroll run on the server to un-post."); return; } const why = window.prompt(`Un-post payroll ${last.runId}? A reversing entry is posted and its commissions are owed again.\n\nReason:`, ""); if (!why || !why.trim()) return; const r = await D.reversePayroll(last.runId, why.trim()); if (r.ok) { setStage("postedEmp"); setModal(null); } toast(r.msg); }} />} {modal === "stubs" && setModal(null)} />} {modal === "register" && setModal(null)} />} {modal === "journal" && setModal(null)} />} {modal === "setup" && setModal(null)} onSave={() => { setModal(null); toast("Payroll setup saved."); }} />}
); }; // ─── Modal shell ─────────────────────────────────────────────────────────── const PrModal = ({ title, width = 620, children, onClose }) => (
{ e.currentTarget.__k2bDownOnBackdrop = (e.target === e.currentTarget); }} onClick={(e) => { const d = e.currentTarget.__k2bDownOnBackdrop; e.currentTarget.__k2bDownOnBackdrop = false; if (d && e.target === e.currentTarget) onClose && onClose(); }}>
e.stopPropagation()} className="k2b-window" style={{ width, background: "#C0C0C0" }}> {}} onMax={() => {}} /> {children}
); // ─── Time entry ─────────────────────────────────────────────────────────── const PrTimeEntry = ({ employees, onClose, onSave }) => { const [es, setEs] = React.useState(employees); const updHrs = (id, field, v) => setEs(prev => prev.map(e => e.id === id ? { ...e, [field]: parseFloat(v) || 0 } : e)); return (
Hourly & Flat-Rate Employee Time — period ending current pay date
Emp #
Name
Dept
Type
Rate
Hrs
O.T. Hrs
{es.map(e => (
{e.id}
{e.name}
{e.dept}
{e.type}
{pr$(e.rate)}
{e.type === "Salary" ? — : updHrs(e.id, "hrs", ev.target.value)} style={{ width: "100%", textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 2px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)" }} />}
{e.type === "Hourly" ? ( updHrs(e.id, "ot", ev.target.value)} style={{ width: "100%", textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 2px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)" }} /> ) : —}
))}
ESC-Cancel onSave(es)}>F10-Save Time
); }; // ─── Generate ────────────────────────────────────────────────────────────── const PrGenerate = ({ payDate, totals, count, onClose, onRun }) => { const [step, setStep] = React.useState(0); const steps = [ "Load employee master file......", "Apply time cards & flat-rate hrs.", "Calculate gross / commissions...", "Apply Fed / FICA / State / Ret..", "Build pre-post payroll register..", ]; return (
Generating payroll for {payDate} — {count} employees
{steps.map((s, i) => (
{i < step ? "✓" : i === step ? "▶" : "·"} {s}
))}
Gross total: ${pr$(totals.gross)} · Withholdings: ${pr$(totals.tax)} · Deductions: ${pr$(totals.ded)} · Net to checks: ${pr$(totals.net)}
ESC-Cancel {step < steps.length ? setStep(step + 1)}>F8-Run Next Step : F10-Generate}
); }; // ─── Edit ───────────────────────────────────────────────────────────────── const PrEditList = ({ employees, onClose, onSave }) => { const [es, setEs] = React.useState(employees); const [sel, setSel] = React.useState(0); const e = es[sel]; const upd = (k, v) => setEs(prev => prev.map((x, i) => i === sel ? { ...x, [k]: v } : x)); const addEmp = () => { const n = { id: "E-" + String(101 + es.length), name: "NEW, EMPLOYEE", dept: "SAL", type: "Hourly", rate: 0, hrs: 0, comm: 0, deductions: [] }; setEs(prev => [...prev, n]); setSel(es.length); }; const delEmp = () => { if (!e || !window.confirm(`Remove ${e.name} from payroll?`)) return; setEs(prev => prev.filter((_, i) => i !== sel)); setSel(0); }; return (
{es.map((x, i) => { const p = computePay(x); return (
setSel(i)} style={{ display: "grid", gridTemplateColumns: "70px 1fr 90px", background: i === sel ? "var(--k2b-navy)" : (i % 2 ? "#F4F4F4" : "#FFFFFF"), color: i === sel ? "#FFFFFF" : "var(--k2b-blue-data-dk)" }}>
{x.id}
{x.name}
{pr$(p.net)}
); })}
{e && (
{e.name} · {e.id}
Type: {e.type}
Rate: upd("rate", parseFloat(ev.target.value) || 0)} style={{ width: 100, textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)" }} />
{e.type !== "Salary" && (
Hours: upd("hrs", parseFloat(ev.target.value) || 0)} style={{ width: 80, textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)" }} />
)} {e.type === "Hourly" && (
O.T.: upd("ot", parseFloat(ev.target.value) || 0)} style={{ width: 80, textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)" }} />
)} {e.type === "Salary" && (
Comm $: upd("comm", parseFloat(ev.target.value) || 0)} style={{ width: 100, textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)" }} />
)} {/* Editable deductions — child support / garnishments / insurance */}
DEDUCTIONS (per pay period)
{(e.deductions || []).map((d, di) => (
upd("deductions", e.deductions.map((x, j) => j === di ? { ...x, label: ev.target.value } : x))} style={{ flex: 1, minWidth: 0, background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)", fontSize: 11 }} /> upd("deductions", e.deductions.map((x, j) => j === di ? { ...x, amount: parseFloat(ev.target.value) || 0 } : x))} style={{ width: 56, textAlign: "right", background: "var(--k2b-yellow)", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)", fontSize: 11 }} />
))} {(e.deductions || []).map((d, di) => (
pay to upd("deductions", e.deductions.map((x, j) => j === di ? { ...x, payee: ev.target.value } : x))} style={{ flex: 1, minWidth: 0, background: "#FFFEF4", border: "1px solid #808080", padding: "0 4px", fontFamily: "'Courier Prime', monospace", color: "var(--k2b-navy)", fontSize: 11 }} />
))}
{(() => { const p = computePay(e); return ( <>
Gross:{pr$(p.gross)}
Federal:({pr$(p.fed)})
FICA:({pr$(p.fica)})
State:({pr$(p.state)})
401k:({pr$(p.ret)})
{p.ded > 0 &&
Deductions:({pr$(p.ded)})
}
Net:{pr$(p.net)}
); })()}
)}
+ Add Employee Delete ESC-Cancel onSave(es)}>F10-Save Edits
); }; // ─── Print Checks ────────────────────────────────────────────────────────── const PrPrintChecks = ({ employees, payDate, onClose, onPrint }) => { const [start, setStart] = React.useState(48211); const [printer, setPrinter] = React.useState("LP1 — MICR Check Printer"); return (
{employees.length} checks · pay date {payDate}
Starting Check #: {/* Audit A-06: draft while typing, parse on blur (JBLiveInt) — the field can be cleared / retyped without snapping back. A blank or zero entry keeps the previous starting number. */} { if (n > 0) setStart(n); }} width={110} style={{ textAlign: "left", color: "var(--k2b-navy)" }} />
Printer.........:
Check sequence preview
{employees.map((e, i) => { const p = computePay(e); return (
#{start + i}
{e.name}
${pr$(p.net)}
); })}
ESC-Cancel onPrint(start)}>F9-Print {employees.length} Checks
); }; // ─── Post to Employee record ────────────────────────────────────────────── const PrPostEmployee = ({ count, onClose, onPost }) => (
Update YTD records for {count} employees
✓ Increment YTD gross, deductions, and net.
✓ Update YTD 401k, vacation accrual, PTO used.
✓ Stamp last-pay date and check number on each record.
✓ Roll period quarter-to-date totals for 941 reporting.
ESC-Cancel F10-Post to Employees
); // ─── Post to GL ─────────────────────────────────────────────────────────── const PrPostGL = ({ totals, onClose, onPost }) => (
Journal entry preview — payroll posting
Acct
Description
Debit
Credit
{[ // RC33 (mpx): the server's payroll map (routes/money-office.js — the accountant confirms the accounts) ["321", "Salaries / wages / commissions accrual relieved", totals.gross, 0], ["323", "Federal withholding + FICA — payable", 0, totals.fed + totals.fica], ["323B", "State withholding — payable", 0, totals.state], ["300B", "401(k) employee deferrals payable", 0, totals.ret], ["300A", "Other deductions payable", 0, totals.ded], ["202", "Operating cash — net checks issued", 0, totals.gross - totals.tax - totals.ret - totals.ded], ].filter(r => (r[2] || 0) > 0.004 || (r[3] || 0) > 0.004).map(([acct, desc, dr, cr], i) => (
{acct}
{desc}
{dr ? pr$(dr) : ""}
{cr ? pr$(cr) : ""}
))}
—
Totals · BALANCED
{pr$(totals.gross)}
{pr$(totals.gross)}
ESC-Cancel F10-Post to GL
); // ─── EFTPS ──────────────────────────────────────────────────────────────── const PrPayDeductions = ({ employees, onClose, onDone }) => { // Group employee deductions by payee — one check per payee. const byPayee = {}; employees.forEach(e => (e.deductions || []).forEach(d => { if (!(+d.amount)) return; const key = (d.payee || d.label || "Deduction payee").trim() || "Deduction payee"; (byPayee[key] = byPayee[key] || []).push({ emp: e.name, label: d.label, amount: +d.amount }); })); const payees = Object.keys(byPayee); const total = payees.reduce((s, p) => s + byPayee[p].reduce((x, d) => x + d.amount, 0), 0); const payAll = async () => { if (!window.DMS || !(window.DMS.postCheck || window.DMS.writeCheck)) return; let n = 0, amt = 0; for (const p of payees) { // one at a time: each check number is reserved on the server (shared check numbers) const sum = +byPayee[p].reduce((x, d) => x + d.amount, 0).toFixed(2); const memo = byPayee[p].map(d => `${d.label || "Deduction"} — ${d.emp}`).join("; ").slice(0, 120); const r = window.DMS.postCheck ? await window.DMS.postCheck({ payee: p, amount: sum, acct: "300A", memo }) : window.DMS.writeCheck({ payee: p, amount: sum, acct: "300A", memo }); // RC33 (mpx): the payroll run credits deductions to 300A (one server map) if (r.ok) { n++; amt += sum; } else if (window.k2bToast) window.k2bToast(r.msg, "warn", 5000); } onDone(n, amt); }; return (
Remit withheld deductions — one check per payee (Dr 300A · Cr 202)
{payees.length === 0 ? (
No employee deductions set up. Add them in Edit Payroll → Deductions.
) : payees.map(p => (
{p}${pr$(byPayee[p].reduce((x, d) => x + d.amount, 0))}
{byPayee[p].map((d, i) => (
{d.label || "Deduction"} — {d.emp}${pr$(d.amount)}
))}
))}
{payees.length} check(s) · total ${pr$(total)} — checks land in the Check Writing register with the next sequence numbers.
ESC-Cancel F10-Write Checks
); }; const PrPostEFTPS = ({ totals, onClose, onPost }) => { const [conf, setConf] = React.useState(""); // RC33 (mpx): the REAL confirmation numbers (never made up) const [stateConf, setStateConf] = React.useState(""); const taxes = { fed: totals.fed, fica: totals.fica, state: totals.state, }; const liability = taxes.fed + taxes.fica + taxes.state; return (
941 / state tax deposit
Federal income tax withheld: {pr$(taxes.fed)} FICA — employee (6.2% + 1.45%): {pr$(taxes.fica)} State income tax withheld: {pr$(taxes.state)} Total deposit liability: ${pr$(liability)}
DR 323 federal + FICA · DR 323B state · CR 202 Operating cash. Pay on EFTPS / the state site first, then type the confirmation numbers they gave you (blank = pay by check).
setConf(e.target.value)} /> setStateConf(e.target.value)} />
ESC-Cancel onPost(conf.trim(), stateConf.trim())}>F10-File EFTPS
); }; // ─── Unpost ─────────────────────────────────────────────────────────────── const PrUnpost = ({ onClose, onUnpost }) => (
⚠ This will create a reversing journal entry. Posting status drops back to "Posted to Emp".
ESC-Cancel F10-Reverse Post
); // ─── Check Stubs ────────────────────────────────────────────────────────── const PrCheckStubs = ({ employees, payDate, onClose }) => { const [sel, setSel] = React.useState(0); const e = employees[sel]; const p = e && computePay(e); return (
{employees.map((x, i) => (
setSel(i)} style={{ padding: "2px 6px", background: i === sel ? "var(--k2b-navy)" : "transparent", color: i === sel ? "var(--k2b-yellow)" : "var(--k2b-blue-data-dk)" }}> {x.id} {x.name}
))}
{e && (
{window.K2BStore ? window.K2BStore.branding().printName : ""} — PAYROLL EARNINGS STATEMENT
Pay Date: {payDate} · {e.name} · {e.id}
EarningsCurrent DeductionsCurrent Regular / Salary{pr$(e.type === "Salary" ? e.rate : (e.hrs || 0) * e.rate)} Federal W-H({pr$(p.fed)}) O.T. / Commission{pr$(e.type === "Hourly" ? (e.ot || 0) * e.rate * 1.5 : (e.comm || 0))} FICA({pr$(p.fica)}) State W-H({pr$(p.state)}) 401(k)({pr$(p.ret)})
Net Pay ${pr$(p.net)}
)}
ESC-Close { window.k2bPrint && window.k2bPrint(window.dealerHeadHtml() + '
PAY STUB
Bi-Weekly Pay Stub · Direct Deposit

Pay stub detail for the selected employee. Full earnings, taxes, deductions and YTD totals print here.

Employee Acknowledgement
Payroll Officer
', { title: "Pay Stub" }); window.k2bToast && window.k2bToast("Pay stub sent to printer.", "ok"); }}>🖨 F9-Print Stub
); }; // ─── Check Register ─────────────────────────────────────────────────────── const PrCheckRegister = ({ employees, payDate, onClose }) => (
Check Register — pay date {payDate}
Check #
Emp #
Name
Gross
Tax
Net
{employees.map((e, i) => { const p = computePay(e); return (
{48211 + i}
{e.id}
{e.name}
{pr$(p.gross)}
{pr$(p.fed + p.fica + p.state)}
{pr$(p.net)}
); })}
ESC-Close { window.k2bPrint && window.k2bPrint(window.dealerHeadHtml() + '
PAYROLL REGISTER
Bi-Weekly · ' + new Date().toLocaleDateString() + '

Employee-by-employee earnings, taxes, and net pay for the current pay period.

', { title: "Payroll Register" }); window.k2bToast && window.k2bToast("Payroll register sent to printer.", "ok"); }}>🖨 F9-Print Register
); // ─── Payroll Journal ────────────────────────────────────────────────────── const PrPayrollJournal = ({ totals, onClose }) => (
Period summary — distribution by department
Department
Gross
Tax
Net
{[ ["SAL — Sales", totals.gross * 0.45, totals.tax * 0.45, totals.net * 0.45], ["F&I", totals.gross * 0.15, totals.tax * 0.15, totals.net * 0.15], ["SVC — Service", totals.gross * 0.28, totals.tax * 0.28, totals.net * 0.28], ["PRT — Parts", totals.gross * 0.08, totals.tax * 0.08, totals.net * 0.08], ["ADM — Admin", totals.gross * 0.04, totals.tax * 0.04, totals.net * 0.04], ].map(([d, g, t, n]) => (
{d}
{pr$(g)}
{pr$(t)}
{pr$(n)}
))}
ESC-Close { window.k2bPrint && window.k2bPrint(window.dealerHeadHtml() + '
PAYROLL JOURNAL ENTRY
Posting summary to GL accounts 011/021/022/023/025/321/323
', { title: "Payroll Journal" }); window.k2bToast && window.k2bToast("Payroll journal sent to printer.", "ok"); }}>🖨 F9-Print Journal
); // ─── Setup ──────────────────────────────────────────────────────────────── const PrSetup = ({ onClose, onSave }) => { const [pf, setPf] = React.useState({ period: "Semi-monthly", fed: 16.00, fica: 7.65, state: 4.25, ret: 4.00, cashAcct: "41 — Operating Cash", }); const upd = (k, v) => setPf(prev => ({ ...prev, [k]: v })); return (
Payroll Defaults
Pay Period.....:
Cash account...:
Default withholding rates
{[["Fed","fed"],["FICA","fica"],["State","state"],["401(k)","ret"]].map(([lbl,k]) => (
{lbl} %........: {/* Audit A-06: JBLiveDec keeps a text draft while focused so a decimal point can be typed and the box can be cleared. */} upd(k, n)} width={80} decimals={2} style={{ color: "var(--k2b-navy)" }} />
))}
ESC-Cancel F10-Save Setup
); }; // ─── Help ───────────────────────────────────────────────────────────────── const PrHelp = ({ onClose }) => (
Workflow
  1. Enter time cards (🔍 icon next to date).
  2. Generate Payroll — calculates gross/W-H/net.
  3. Edit Payroll — manual adjustments by employee.
  4. Print Checks — MICR printer to LP1.
  5. Post to Employee — updates YTD on the master file.
  6. Post Checks to GL — releases the journal entry.
  7. Post EFTPS — files the 941 deposit.
ESC-Close
); Object.assign(window, { PayrollProcess, EMPLOYEES }); // Lazy-seed source for DMS.employees() on non-blank stores. window.__PR_EMPLOYEES_DEFAULT = EMPLOYEES;