diff --git a/app/config.yml b/app/config.yml index 392fa25..e450932 100644 --- a/app/config.yml +++ b/app/config.yml @@ -131,6 +131,12 @@ charge-to: vat-id: "GB123456789" reg-no: "01234567" currency: GBP + pay-account-holder: Example Non-Profit Organisation + pay-account-no: "GB29 NWBK 6016 1331 9268 19" + pay-bic: "NWBKGB2L" + pay-bank-address1: "NatWest, 1 Princes Street" + pay-bank-address2: "London EC2R 8PB" + pay-ref: "" # ── Project codes ────────────────────────────────────────────────────────────── project-codes: diff --git a/app/index.html b/app/index.html index 9ebd3a4..7d9a10e 100644 --- a/app/index.html +++ b/app/index.html @@ -296,9 +296,10 @@ .pay-by-row label { font-size: 12px; color: var(--text-muted); white-space: nowrap; } #paybydisp { font-size: 13px; font-weight: 600; color: var(--text); } - /* ── Locked charge-to fields ────────────────────────────────────────────── */ + /* ── Locked charge-to / bank fields ─────────────────────────────────────── */ #ct-fields.locked input, - #ct-fields.locked select { + #ct-fields.locked select, + #bank-section.locked input { background: #f8f9fa; color: var(--text-muted); border-color: var(--border-light); @@ -562,28 +563,30 @@ function buildForm() {
- @@ -643,6 +646,7 @@ function buildForm() { document.getElementById("paid-inp").addEventListener("input", calcTotals); document.getElementById("the-form").addEventListener("submit", e => { e.preventDefault(); generateInvoice(); }); document.querySelectorAll("[data-ls]").forEach(el => el.addEventListener("change", saveStorage)); + calcPayBy(); // compute initial pay-by from default 7-day term } // ── Fill charge-to ──────────────────────────────────────────────────────────── @@ -651,12 +655,20 @@ function fillChargeTo(v) { const fields = document.getElementById("ct-fields"); const hidePayment = cfg["hide-payment-info"] === true || cfg["hide-payment-info"] === "yes"; - const ppanel = document.getElementById("payment-panel"); - if (ppanel) ppanel.style.display = (!hidePayment && v === "__other__") ? "" : "none"; + const bsec = document.getElementById("bank-section"); if (v === "" || v === "__other__") { if (v === "") ["ctn","ca1","ca2","ca3","ca4","cc","cph","cem","cvat","creg"].forEach(id => f(id, "")); fields?.classList.remove("locked"); + if (bsec) { + bsec.classList.remove("locked"); + if (!hidePayment && v === "__other__") { + bsec.style.display = ""; + } else { + bsec.style.display = "none"; + ["pacct","piban","pbic","pbadr1","pbadr2","pref"].forEach(id => f(id, "")); + } + } return; } const ct = (cfg["charge-to"] || [])[+v]; @@ -668,6 +680,29 @@ function fillChargeTo(v) { f("cvat", ct["vat-id"]); f("creg", ct["reg-no"]); fields?.classList.add("locked"); + // Fill bank section from config if provided + if (bsec && !hidePayment) { + const hasBank = ct["pay-account-holder"] || ct["pay-account-no"] || ct["pay-bic"]; + if (hasBank) { + f("pacct", ct["pay-account-holder"]); + f("piban", ct["pay-account-no"]); + f("pbic", ct["pay-bic"]); + f("pbadr1", ct["pay-bank-address1"]); + f("pbadr2", ct["pay-bank-address2"]); + f("pref", ct["pay-ref"]); + bsec.style.display = ""; + bsec.classList.add("locked"); + } else { + bsec.style.display = "none"; + ["pacct","piban","pbic","pbadr1","pbadr2","pref"].forEach(id => f(id, "")); + bsec.classList.remove("locked"); + } + } else if (bsec) { + bsec.style.display = "none"; + ["pacct","piban","pbic","pbadr1","pbadr2","pref"].forEach(id => f(id, "")); + bsec.classList.remove("locked"); + } + // Auto-set invoice currency from recipient config if (ct.currency) { const icurEl = document.getElementById("icur"); @@ -1050,17 +1085,17 @@ function gatherData(renderLang) { const ctCntry= COUNTRY_MAP[g("cc")] || ""; const ctPh = g("cph"), ctEm = g("cem"), ctVat = g("cvat"), ctReg = g("creg"); - const ppanel = document.getElementById("payment-panel"); - const payVisible = ppanel && ppanel.style.display !== "none"; - const pTerm = payVisible ? (parseInt(document.getElementById("pterm")?.value) || 0) : 0; - const pPayBy = payVisible ? (document.getElementById("paybydisp")?.textContent || "") : ""; - const pAcct = payVisible ? g("pacct") : ""; - const pIban = payVisible ? g("piban") : ""; - const pBic = payVisible ? g("pbic") : ""; - const pBadr1 = payVisible ? g("pbadr1"): ""; - const pBadr2 = payVisible ? g("pbadr2"): ""; - const pRef = payVisible ? g("pref") : ""; - const hasPayment = payVisible && !!(pAcct || pIban || pBic || pBadr1 || pRef || pTerm > 0); + const pTerm = parseInt(document.getElementById("pterm")?.value) || 0; + const pPayBy = document.getElementById("paybydisp")?.textContent || ""; + const bsec = document.getElementById("bank-section"); + const bankVisible = bsec && bsec.style.display !== "none"; + const pAcct = bankVisible ? g("pacct") : ""; + const pIban = bankVisible ? g("piban") : ""; + const pBic = bankVisible ? g("pbic") : ""; + const pBadr1 = bankVisible ? g("pbadr1"): ""; + const pBadr2 = bankVisible ? g("pbadr2"): ""; + const pRef = bankVisible ? g("pref") : ""; + const hasBank = !!(pAcct || pIban || pBic || pBadr1 || pRef); let sub = 0; const rows = []; @@ -1115,7 +1150,7 @@ function gatherData(renderLang) { return { dl, td, sName, sAddr, sCntry, sPh, sEm, iDate, pCode, iNo, iCur, ctName, ctAddr, ctCntry, ctPh, ctEm, ctVat, ctReg, - pTerm, pPayBy, pAcct, pIban, pBic, pBadr1, pBadr2, pRef, hasPayment, + pTerm, pPayBy, pAcct, pIban, pBic, pBadr1, pBadr2, pRef, hasBank, rows, sub, taxes, totalTax, paid, toPay }; } @@ -1198,17 +1233,17 @@ function buildPreviewHTML() { ${fmt(toPay)} - ${hasPayment ? ` + ${(pTerm > 0 || hasBank) ? `
${h(td("payment"))}
${pTerm > 0 ? `
${h(td("payment-terms"))}: ${pTerm} ${h(td("payment-days"))}${pPayBy ? ` — ${h(td("pay-by"))}: ${h(pPayBy)}` : ""}
` : ""} -
+ ${hasBank ? `
${pAcct ? `${h(td("account-holder"))}${h(pAcct)}` : ""} ${pIban ? `${h(td("account-no"))}${h(pIban)}` : ""} ${pBic ? `${h(td("bank-bic"))}${h(pBic)}` : ""} ${pBadr1 || pBadr2 ? `${h(td("bank-address"))}${[pBadr1,pBadr2].filter(Boolean).map(l=>h(l)).join("
")}
` : ""}
- ${pRef ? `
${h(td("payment-ref"))}: ${h(pRef)}
` : ""} + ${pRef ? `
${h(td("payment-ref"))}: ${h(pRef)}
` : ""}` : ""}
` : ""}`; } @@ -1421,7 +1456,7 @@ function buildPDF() { y += 9; // ── PAYMENT DETAILS ─────────────────────────────────────────────────────── - if (hasPayment) { + if (pTerm > 0 || hasBank) { y += 8; if (y + 8 > PH - 15) { doc.addPage(); y = MT; } dc(209,213,219); doc.setLineWidth(0.4); @@ -1434,26 +1469,28 @@ function buildPDF() { fn(8.5); tc(17,24,39); tL(termsStr, ML, y); y += 5; } - const LBL = 50; - const payRows = [ - pAcct ? [td("account-holder"), pAcct] : null, - pIban ? [td("account-no"), pIban] : null, - pBic ? [td("bank-bic"), pBic] : null, - (pBadr1 || pBadr2) ? [td("bank-address"), [pBadr1,pBadr2].filter(Boolean).join(", ")] : null, - ].filter(Boolean); + if (hasBank) { + const LBL = 50; + const payRows = [ + pAcct ? [td("account-holder"), pAcct] : null, + pIban ? [td("account-no"), pIban] : null, + pBic ? [td("bank-bic"), pBic] : null, + (pBadr1 || pBadr2) ? [td("bank-address"), [pBadr1,pBadr2].filter(Boolean).join(", ")] : null, + ].filter(Boolean); - payRows.forEach(([lbl, val]) => { - if (y + 5 > PH - 10) { doc.addPage(); y = MT; } - fn(8); tc(107,114,128); tL(lbl + ":", ML, y); - fn(8.5); tc(17,24,39); tL(val, ML + LBL, y); - y += 4.5; - }); + payRows.forEach(([lbl, val]) => { + if (y + 5 > PH - 10) { doc.addPage(); y = MT; } + fn(8); tc(107,114,128); tL(lbl + ":", ML, y); + fn(8.5); tc(17,24,39); tL(val, ML + LBL, y); + y += 4.5; + }); - if (pRef) { - y += 1; - if (y + 6 > PH - 10) { doc.addPage(); y = MT; } - fn(8); tc(107,114,128); tL(td("payment-ref") + ":", ML, y); - fb(9); tc(17,24,39); tL(pRef, ML + LBL, y); + if (pRef) { + y += 1; + if (y + 6 > PH - 10) { doc.addPage(); y = MT; } + fn(8); tc(107,114,128); tL(td("payment-ref") + ":", ML, y); + fb(9); tc(17,24,39); tL(pRef, ML + LBL, y); + } } }