Add sender Tax ID field; unify contact info rendering

- New VAT / Tax ID field on the sender form (saved to localStorage)
- Preview: sender phone/email/tax ID now rendered as .bt-meta spans matching
  charge-to style (bold label, muted text, flex wrap with gap)
- PDF: sender contact line includes tax ID alongside phone and email
- .bt-meta CSS selector generalised (removed .d-bill scope) so it works for
  both sender and charge-to panels without duplication

https://claude.ai/code/session_015iyCBgoTXNNqaErR287U1u
This commit is contained in:
Claude 2026-05-19 09:43:05 +00:00
parent a63fcc8a42
commit 7222152664
No known key found for this signature in database

View file

@ -252,9 +252,9 @@
.d-bill .bt-lbl { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 1.2px; color: #6b7280; margin-bottom: 5px; }
.d-bill .bt-name { font-size: 13px; font-weight: 700; color: var(--navy); margin-bottom: 3px; }
.d-bill p { font-size: 10.5px; color: #4b5563; margin-bottom: 2px; }
.d-bill .bt-meta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
.d-bill .bt-meta span { font-size: 10px; color: #4b5563; }
.d-bill .bt-meta strong { color: #111827; }
.bt-meta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
.bt-meta span { font-size: 10px; color: #4b5563; }
.bt-meta strong { color: #111827; }
.d-inv-meta { text-align: right; }
.d-inv-meta h1 { font-size: 28px; font-weight: 800; letter-spacing: 5px; color: var(--navy); margin-bottom: 14px; }
@ -505,6 +505,8 @@ function buildForm() {
<input id="sp" type="tel" data-ls="sp" autocomplete="tel"></div>
<div class="fi"><label id="lbl-se">${t("sender-email")}:</label>
<input id="se" type="email" data-ls="se" autocomplete="email"></div>
<div class="fi"><label id="lbl-stax">${t("vat-id")}:</label>
<input id="stax" type="text" data-ls="stax"></div>
</div>
<div class="card">
<div class="card-title" id="sec-invdet">${t("invoice-details-section")}</div>
@ -944,7 +946,7 @@ function relabel() {
"sec-ct":"charge-to","sec-lines":"invoice-lines",
"lbl-sn":"sender-name","lbl-sa1":"sender-address1","lbl-sa2":"sender-address2",
"lbl-sa3":"sender-address3","lbl-sa4":"sender-address4","lbl-sc":"sender-country",
"lbl-sp":"sender-phone","lbl-se":"sender-email",
"lbl-sp":"sender-phone","lbl-se":"sender-email","lbl-stax":"vat-id",
"lbl-idate":"invoice-date","lbl-icur":"invoice-currency","lbl-pcode":"project-code","lbl-ino":"invoice-no",
"lbl-ctn":"charge-to-name","lbl-ca1":"charge-to-address1","lbl-ca2":"charge-to-address2",
"lbl-ca3":"charge-to-address3","lbl-ca4":"charge-to-address4","lbl-cc":"charge-to-country",
@ -1034,7 +1036,7 @@ function gatherData(renderLang) {
const sName = g("sn");
const sAddr = [g("sa1"),g("sa2"),g("sa3"),g("sa4")].filter(Boolean);
const sCntry = COUNTRY_MAP[g("sc")] || "";
const sPh = g("sp"), sEm = g("se");
const sPh = g("sp"), sEm = g("se"), sTax = g("stax");
const iDate = g("idate");
const pCode = g("pcode") === "__other__" ? g("pcode-other") : g("pcode");
@ -1110,7 +1112,7 @@ function gatherData(renderLang) {
const paid = pn(document.getElementById("paid-inp")?.value);
const toPay = sub + totalTax - paid;
return { dl, td, sName, sAddr, sCntry, sPh, sEm, iDate, pCode, iNo, iCur,
return { dl, td, sName, sAddr, sCntry, sPh, sEm, sTax, iDate, pCode, iNo, iCur,
ctName, ctAddr, ctCntry, ctPh, ctEm, ctVat, ctReg,
pTerm, pPayBy, pAcct, pIban, pBic, pBadr1, pBadr2, pRef, hasBank, hidePaymentOut,
rows, sub, taxes, totalTax, paid, toPay };
@ -1118,7 +1120,7 @@ function gatherData(renderLang) {
// ── Build HTML preview ────────────────────────────────────────────────────────
function buildPreviewHTML() {
const { td, sName, sAddr, sCntry, sPh, sEm, iDate, pCode, iNo, iCur,
const { td, sName, sAddr, sCntry, sPh, sEm, sTax, iDate, pCode, iNo, iCur,
ctName, ctAddr, ctCntry, ctPh, ctEm, ctVat, ctReg,
pTerm, pPayBy, pAcct, pIban, pBic, pBadr1, pBadr2, pRef, hasBank, hidePaymentOut,
rows, sub, taxes, paid, toPay } = gatherData();
@ -1146,8 +1148,11 @@ function buildPreviewHTML() {
${sName ? `<div class="name">${h(sName)}</div>` : ""}
${sAddr.map(a=>`<p>${h(a)}</p>`).join("")}
${sCntry ? `<p>${h(sCntry)}</p>` : ""}
${sPh ? `<p>${h(td("sender-phone"))}: ${h(sPh)}</p>` : ""}
${sEm ? `<p>${h(td("sender-email"))}: ${h(sEm)}</p>` : ""}
<div class="bt-meta">
${sPh ? `<span><strong>${h(td("sender-phone"))}:</strong> ${h(sPh)}</span>` : ""}
${sEm ? `<span><strong>${h(td("sender-email"))}:</strong> ${h(sEm)}</span>` : ""}
${sTax ? `<span><strong>${h(td("vat-id"))}:</strong> ${h(sTax)}</span>` : ""}
</div>
</div>
<div class="d-tr d-inv-meta">
<h1>${h(td("invoice"))}</h1>
@ -1234,7 +1239,7 @@ function buildPDF() {
const tR = (s,x,y) => doc.text(String(s??""), x, y, {align:"right"});
const sp = (s,w) => doc.splitTextToSize(String(s??""), w);
const { dl, td, sName, sAddr, sCntry, sPh, sEm, iDate, pCode, iNo, iCur,
const { dl, td, sName, sAddr, sCntry, sPh, sEm, sTax, iDate, pCode, iNo, iCur,
ctName, ctAddr, ctCntry, ctPh, ctEm, ctVat, ctReg,
pTerm, pPayBy, pAcct, pIban, pBic, pBadr1, pBadr2, pRef, hasBank, hidePaymentOut,
rows, sub, taxes, paid, toPay } = gatherData();
@ -1253,10 +1258,11 @@ function buildPDF() {
if (sName) { fb(13); tc(30,45,69); tL(sName, ML, ly); ly += 6; }
fn(8.5); tc(75,85,99);
[...sAddr, sCntry].filter(Boolean).forEach(l => { tL(l, ML, ly); ly += 4.5; });
if (sPh || sEm) {
if (sPh || sEm || sTax) {
const parts = [];
if (sPh) parts.push(`${td("sender-phone")}: ${sPh}`);
if (sEm) parts.push(`${td("sender-email")}: ${sEm}`);
if (sTax) parts.push(`${td("vat-id")}: ${sTax}`);
fn(8); tc(107,114,128); tL(parts.join(" "), ML, ly); ly += 5;
}