From d440788d0dd7bf0fff1b0b7eacfbbfafa385859f Mon Sep 17 00:00:00 2001 From: odweta Date: Thu, 10 Sep 2026 17:29:19 +0200 Subject: [PATCH] Make invoice due date optional with 14-day default Datum vystaveni defaults to today and Datum splatnosti defaults to 14 days after the issue date. Both fields are now optional date inputs and only override the default when a user enters a custom value. Applies to the invoice editor and generated PDF. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- app/src/app.js | 28 +++++++++++++++++++++++++++- app/src/static/index.html | 12 ++++++------ app/src/static/script.js | 28 +++++++++++++++++----------- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/app/src/app.js b/app/src/app.js index 60693f6..711b57b 100644 --- a/app/src/app.js +++ b/app/src/app.js @@ -445,6 +445,20 @@ app.delete('/api/invoices', requireAuth, async (req, res, next) => { }); const pdfText = (value) => String(value || 'Neuvedeno'); +const formatCzechDate = (value) => { + const date = new Date(`${value}T00:00:00`); + return Number.isNaN(date.getTime()) ? '' : date.toLocaleDateString('cs-CZ'); +}; +const toDateInputValue = (date) => date.toISOString().slice(0, 10); +const resolveIssueDate = (issueDate) => issueDate || toDateInputValue(new Date()); +const resolveDueDate = (dueDate, issueDate) => { + if (dueDate) { + return dueDate; + } + const due = new Date(`${resolveIssueDate(issueDate)}T00:00:00`); + due.setDate(due.getDate() + 14); + return toDateInputValue(due); +}; const formatCzechNumber = (value) => Number(value || 0).toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 @@ -518,13 +532,25 @@ const renderInvoicePdf = async (document, invoice) => { const partyWidth = (pageWidth - 24) / 2; const customerX = document.page.margins.left + partyWidth + 24; + const issueDate = resolveIssueDate(data.issueDate); + const dueDate = resolveDueDate(data.dueDate, data.issueDate); + const headerY = document.y; document.fillColor(navy).font(boldFont).fontSize(25).text('Faktura', document.page.margins.left, headerY); document.fillColor(navy).font(boldFont).fontSize(16).text(invoice.invoice_number, customerX, headerY, { width: partyWidth, align: 'right' }); - document.y = headerY + 40; + document.fillColor(bodyText).font(regularFont).fontSize(9) + .text(`Datum vystavení: ${formatCzechDate(issueDate)}`, customerX, headerY + 22, { + width: partyWidth, + align: 'right' + }) + .text(`Datum splatnosti: ${formatCzechDate(dueDate)}`, customerX, headerY + 35, { + width: partyWidth, + align: 'right' + }); + document.y = headerY + 52; const partyTop = document.y; const drawParty = (x, title, profile, name) => { diff --git a/app/src/static/index.html b/app/src/static/index.html index 509cab9..eeace6f 100644 --- a/app/src/static/index.html +++ b/app/src/static/index.html @@ -308,13 +308,13 @@
- - + + +
+
+ +
-
diff --git a/app/src/static/script.js b/app/src/static/script.js index 53b23a1..d7fdb5a 100644 --- a/app/src/static/script.js +++ b/app/src/static/script.js @@ -40,8 +40,8 @@ const passwordForm = document.getElementById("passwordForm"); const savePasswordButton = document.getElementById("savePasswordButton"); const cancelPasswordButton = document.getElementById("cancelPasswordButton"); const passwordStatus = document.getElementById("passwordStatus"); -const dueDateOption = document.getElementById("dueDateOption"); -const dueDateDisplay = document.getElementById("dueDateDisplay"); +const issueDateInput = document.getElementById("issueDate"); +const dueDateInput = document.getElementById("dueDate"); const paymentPresetSelect = document.getElementById("paymentPresetSelect"); const paymentPresetName = document.getElementById("paymentPresetName"); const savePaymentPresetButton = document.getElementById("savePaymentPresetButton"); @@ -108,17 +108,17 @@ const getClientLabel = (client) => [ const formatCzechDate = (dateValue) => new Date(`${dateValue}T00:00:00`).toLocaleDateString("cs-CZ"); -const getDueDays = () => Number.parseInt(dueDateOption.value, 10) || 14; +const toDateInputValue = (date) => date.toISOString().slice(0, 10); + +const getIssueDate = () => issueDateInput.value || toDateInputValue(new Date()); const getDueDate = () => { - const dueDate = new Date(); - dueDate.setHours(0, 0, 0, 0); - dueDate.setDate(dueDate.getDate() + getDueDays()); - return dueDate.toISOString().slice(0, 10); -}; - -const updateDueDate = () => { - dueDateDisplay.textContent = `Splatnost: ${formatCzechDate(getDueDate())}`; + if (dueDateInput.value) { + return dueDateInput.value; + } + const dueDate = new Date(`${getIssueDate()}T00:00:00`); + dueDate.setDate(dueDate.getDate() + 14); + return toDateInputValue(dueDate); }; const updateClientTypeFields = () => { @@ -234,6 +234,8 @@ deletePaymentPresetButton.addEventListener("click", async () => { }); const readInvoiceData = () => ({ + issueDate: getIssueDate(), + dueDate: getDueDate(), supplier: { ...readProfileFields("dodavatel"), typSubjektu: supplierTypeSelect.value @@ -263,6 +265,8 @@ const clearInvoiceEditor = () => { currentInvoiceId = null; invoiceNumberDisplay.textContent = "Nová faktura"; invoiceSaveStatus.textContent = ""; + issueDateInput.value = ""; + dueDateInput.value = ""; writeProfileFields("odberatel", {}); clientSelect.value = ""; clientTypeSelect.value = "osoba"; @@ -282,6 +286,8 @@ const loadInvoiceIntoEditor = (invoice) => { currentInvoiceId = invoice.id; invoiceNumberDisplay.textContent = invoice.invoiceNumber; invoiceSaveStatus.textContent = ""; + issueDateInput.value = invoice.data.issueDate || ""; + dueDateInput.value = invoice.data.dueDate || ""; writeProfileFields("dodavatel", invoice.data.supplier || {}); supplierTypeSelect.value = invoice.data.supplier?.typSubjektu || "osoba"; updateSupplierTypeFields();