Per-recipient project codes

Each charge-to entry in config.yml can now have its own project-codes
list. Selecting a recipient swaps the project code dropdown to that
recipient's codes; switching back to Select or Other restores the
global list. Falls back to the global list if a recipient has no
project-codes defined.
This commit is contained in:
Claude 2026-05-24 16:25:32 +00:00
parent 3baf821fbd
commit f07b1e9ae0
No known key found for this signature in database
2 changed files with 27 additions and 0 deletions

View file

@ -119,6 +119,10 @@ charge-to:
vat-id: "US-EIN-12-3456789"
reg-no: ""
currency: USD
project-codes:
- AC-100
- AC-110
- AC-200
- display: Example NGO
name: Example Non-Profit Organisation
address1: 45 Charity Lane
@ -131,6 +135,10 @@ charge-to:
vat-id: "GB123456789"
reg-no: "01234567"
currency: GBP
project-codes:
- NGO-2026-01
- NGO-2026-02
- NGO-2026-03
# ── Project codes ──────────────────────────────────────────────────────────────
project-codes:

View file

@ -693,6 +693,20 @@ function buildForm() {
calcPayBy(); // compute initial pay-by from default 7-day term
}
// ── Project-code dropdown ─────────────────────────────────────────────────────
function updateProjectCodes(codes) {
const arr = (codes && codes.length) ? codes : (cfg["project-codes"] || []);
const sel = document.getElementById("pcode");
if (!sel) return;
const prev = sel.value;
sel.innerHTML = `<option value="">${t("select")}</option>`
+ arr.map(pc => `<option value="${h(pc)}">${h(pc)}</option>`).join("")
+ `<option value="__other__">${t("other")}</option>`;
sel.value = arr.includes(prev) ? prev : "";
const wrap = document.getElementById("pcode-other-wrap");
if (wrap) wrap.style.display = sel.value === "__other__" ? "block" : "none";
}
// ── Fill charge-to ────────────────────────────────────────────────────────────
function fillChargeTo(v) {
const f = (id, val) => { const el = document.getElementById(id); if (el) el.value = val ?? ""; };
@ -704,10 +718,12 @@ function fillChargeTo(v) {
if (v === "") {
["ctn","ca1","ca2","ca3","ca4","cc","cph","cem","cvat","creg"].forEach(id => f(id, ""));
fields?.classList.add("locked");
updateProjectCodes(null); // restore global project codes
return;
}
if (v === "__other__") {
fields?.classList.remove("locked");
updateProjectCodes(null); // restore global project codes
return;
}
const ct = (cfg["charge-to"] || [])[+v];
@ -719,6 +735,9 @@ function fillChargeTo(v) {
f("cvat", ct["vat-id"]); f("creg", ct["reg-no"]);
fields?.classList.add("locked");
// Swap project codes to this recipient's list (falls back to global if none defined)
updateProjectCodes(ct["project-codes"] || null);
// Auto-set invoice currency from recipient config
if (ct.currency) {
const icurEl = document.getElementById("icur");