diff --git a/public/invoice-view.js b/public/invoice-view.js
index 8d19d4c..1b504e4 100644
--- a/public/invoice-view.js
+++ b/public/invoice-view.js
@@ -147,7 +147,12 @@ function renderInvoiceRow(invoice) {
: `Draft`;
let statusBadge = '';
+ const amountPaid = parseFloat(invoice.amount_paid) || 0;
+ const balance = parseFloat(invoice.balance) ?? (parseFloat(invoice.total) - amountPaid);
+ const isPartiallyPaid = !paid && amountPaid > 0 && balance > 0;
+
if (paid) statusBadge = `Paid`;
+ else if (isPartiallyPaid) statusBadge = `Partial $${amountPaid.toFixed(2)}`;
else if (overdue) statusBadge = `Overdue`;
// Send Date
@@ -203,7 +208,12 @@ function renderInvoiceRow(invoice) {
${formatDate(invoice.invoice_date)} |
${sendDateDisplay} |
${invoice.terms} |
- $${parseFloat(invoice.total).toFixed(2)} |
+
+ ${isPartiallyPaid
+ ? `$${balance.toFixed(2)} $${parseFloat(invoice.total).toFixed(2)}`
+ : `$${parseFloat(invoice.total).toFixed(2)}`
+ }
+ |
${editBtn} ${qboBtn} ${pdfBtn} ${htmlBtn} ${paidBtn} ${delBtn}
|
diff --git a/public/payment-modal.js b/public/payment-modal.js
index f2e36c0..58648e9 100644
--- a/public/payment-modal.js
+++ b/public/payment-modal.js
@@ -37,9 +37,12 @@ export async function openPaymentModal(invoiceIds = []) {
const res = await fetch(`/api/invoices/${id}`);
const data = await res.json();
if (data.invoice) {
+ const total = parseFloat(data.invoice.total);
+ const amountPaid = parseFloat(data.invoice.amount_paid) || 0;
+ const balance = total - amountPaid;
selectedInvoices.push({
invoice: data.invoice,
- payAmount: parseFloat(data.invoice.total)
+ payAmount: balance > 0 ? balance : total
});
}
} catch (e) { console.error('Error loading invoice:', id, e); }
@@ -82,9 +85,13 @@ async function addInvoiceById() {
const detailRes = await fetch(`/api/invoices/${match.id}`);
const detailData = await detailRes.json();
+ const detailInv = detailData.invoice;
+ const detailTotal = parseFloat(detailInv.total);
+ const detailPaid = parseFloat(detailInv.amount_paid) || 0;
+ const detailBalance = detailTotal - detailPaid;
selectedInvoices.push({
- invoice: detailData.invoice,
- payAmount: parseFloat(detailData.invoice.total)
+ invoice: detailInv,
+ payAmount: detailBalance > 0 ? detailBalance : detailTotal
});
renderInvoiceList();
@@ -223,8 +230,14 @@ function renderInvoiceList() {
container.innerHTML = selectedInvoices.map(si => {
const inv = si.invoice;
const total = parseFloat(inv.total);
- const isPartial = si.payAmount < total;
- const isOver = si.payAmount > total;
+ const amountPaid = parseFloat(inv.amount_paid) || 0;
+ const balance = total - amountPaid;
+ const isPartial = si.payAmount < balance;
+ const isOver = si.payAmount > balance;
+
+ const paidInfo = amountPaid > 0
+ ? `Paid: $${amountPaid.toFixed(2)}`
+ : '';
return `
@@ -232,6 +245,7 @@ function renderInvoiceList() {
#${inv.invoice_number || 'Draft'}
${inv.customer_name || ''}
(Total: $${total.toFixed(2)})
+ ${paidInfo}
${isPartial ? 'Partial' : ''}
${isOver ? 'Overpay' : ''}
diff --git a/server.js b/server.js
index 18befa5..15de2ed 100644
--- a/server.js
+++ b/server.js
@@ -403,12 +403,19 @@ app.delete('/api/quotes/:id', async (req, res) => {
app.get('/api/invoices', async (req, res) => {
try {
const result = await pool.query(`
- SELECT i.*, c.name as customer_name, c.qbo_id as customer_qbo_id
+ SELECT i.*, c.name as customer_name, c.qbo_id as customer_qbo_id,
+ COALESCE((SELECT SUM(pi.amount) FROM payment_invoices pi WHERE pi.invoice_id = i.id), 0) as amount_paid
FROM invoices i
LEFT JOIN customers c ON i.customer_id = c.id
ORDER BY i.created_at DESC
`);
- res.json(result.rows);
+ // balance berechnen
+ const rows = result.rows.map(r => ({
+ ...r,
+ amount_paid: parseFloat(r.amount_paid) || 0,
+ balance: (parseFloat(r.total) || 0) - (parseFloat(r.amount_paid) || 0)
+ }));
+ res.json(rows);
} catch (error) {
console.error('Error fetching invoices:', error);
res.status(500).json({ error: 'Error fetching invoices' });
@@ -429,25 +436,30 @@ app.get('/api/invoices/next-number', async (req, res) => {
app.get('/api/invoices/:id', async (req, res) => {
const { id } = req.params;
try {
- // KORRIGIERT: c.line1, c.line2, c.line3, c.line4 statt c.street
const invoiceResult = await pool.query(`
- SELECT i.*, c.name as customer_name, c.line1, c.line2, c.line3, c.line4, c.city, c.state, c.zip_code, c.account_number
+ SELECT i.*, c.name as customer_name, c.qbo_id as customer_qbo_id,
+ c.line1, c.line2, c.line3, c.line4, c.city, c.state, c.zip_code, c.account_number,
+ COALESCE((SELECT SUM(pi.amount) FROM payment_invoices pi WHERE pi.invoice_id = i.id), 0) as amount_paid
FROM invoices i
LEFT JOIN customers c ON i.customer_id = c.id
WHERE i.id = $1
`, [id]);
-
+
if (invoiceResult.rows.length === 0) {
return res.status(404).json({ error: 'Invoice not found' });
}
-
+
+ const invoice = invoiceResult.rows[0];
+ invoice.amount_paid = parseFloat(invoice.amount_paid) || 0;
+ invoice.balance = (parseFloat(invoice.total) || 0) - invoice.amount_paid;
+
const itemsResult = await pool.query(
'SELECT * FROM invoice_items WHERE invoice_id = $1 ORDER BY item_order',
[id]
);
-
+
res.json({
- invoice: invoiceResult.rows[0],
+ invoice: invoice,
items: itemsResult.rows
});
} catch (error) {
@@ -456,6 +468,9 @@ app.get('/api/invoices/:id', async (req, res) => {
}
});
+
+
+
app.post('/api/invoices', async (req, res) => {
const { invoice_number, customer_id, invoice_date, terms, auth_code, tax_exempt, items, created_from_quote_id, scheduled_send_date } = req.body;