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();