diff --git a/public/app.js b/public/app.js index 4bac4e7..6e9c643 100644 --- a/public/app.js +++ b/public/app.js @@ -817,7 +817,7 @@ async function openInvoiceModal(invoiceId = null) { } } - document.getElementById('invoice-number').value = data.invoice.invoice_number; + document.getElementById('invoice-number').value = data.invoice.invoice_number || ''; document.getElementById('invoice-customer').value = data.invoice.customer_id; const dateOnly = data.invoice.invoice_date.split('T')[0]; document.getElementById('invoice-date').value = dateOnly; @@ -825,6 +825,14 @@ async function openInvoiceModal(invoiceId = null) { document.getElementById('invoice-authorization').value = data.invoice.auth_code || ''; document.getElementById('invoice-tax-exempt').checked = data.invoice.tax_exempt; + // Scheduled Send Date + const sendDateEl = document.getElementById('invoice-send-date'); + if (sendDateEl) { + sendDateEl.value = data.invoice.scheduled_send_date + ? data.invoice.scheduled_send_date.split('T')[0] + : ''; + } + // Load items document.getElementById('invoice-items').innerHTML = ''; itemCounter = 0; @@ -838,11 +846,12 @@ async function openInvoiceModal(invoiceId = null) { document.getElementById('invoice-form').reset(); document.getElementById('invoice-items').innerHTML = ''; document.getElementById('invoice-terms').value = 'Net 30'; + document.getElementById('invoice-number').value = ''; // Leer lassen! + document.getElementById('invoice-send-date').value = ''; itemCounter = 0; setDefaultDate(); - // Fetch next invoice number - fetchNextInvoiceNumber(); + // KEIN fetchNextInvoiceNumber() mehr — Nummer kommt von QBO // Add one default item addInvoiceItem(); @@ -1068,39 +1077,49 @@ async function handleInvoiceSubmit(e) { return; } - const invoiceNumber = document.getElementById('invoice-number').value; + const invoiceNumber = document.getElementById('invoice-number').value.trim(); - if (!invoiceNumber || !/^\d+$/.test(invoiceNumber)) { - alert('Invalid invoice number. Must be a numeric value.'); + // Invoice Number ist jetzt OPTIONAL + // Wenn angegeben, muss sie numerisch sein + if (invoiceNumber && !/^\d+$/.test(invoiceNumber)) { + alert('Invalid invoice number. Must be a numeric value or left empty.'); return; } const data = { - invoice_number: invoiceNumber, + invoice_number: invoiceNumber || null, // null wenn leer customer_id: parseInt(document.getElementById('invoice-customer').value), invoice_date: document.getElementById('invoice-date').value, terms: document.getElementById('invoice-terms').value, auth_code: document.getElementById('invoice-authorization').value, tax_exempt: document.getElementById('invoice-tax-exempt').checked, + scheduled_send_date: document.getElementById('invoice-send-date').value || null, items: items }; try { - const url = currentInvoiceId ? `/api/invoices/${currentInvoiceId}` : '/api/invoices'; - const method = currentInvoiceId ? 'PUT' : 'POST'; + let response; + if (currentInvoiceId) { + response = await fetch(`/api/invoices/${currentInvoiceId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + } else { + response = await fetch('/api/invoices', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + } - const response = await fetch(url, { - method, - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }); + const result = await response.json(); if (response.ok) { closeInvoiceModal(); loadInvoices(); } else { - const error = await response.json(); - alert(error.error || 'Error saving invoice'); + alert(result.error || 'Error saving invoice'); } } catch (error) { console.error('Error:', error); diff --git a/public/index.html b/public/index.html index d13275b..c693081 100644 --- a/public/index.html +++ b/public/index.html @@ -65,7 +65,7 @@ - +