better states
This commit is contained in:
@@ -69,19 +69,121 @@ function getMonthName(i) {
|
||||
return ['January','February','March','April','May','June','July','August','September','October','November','December'][i];
|
||||
}
|
||||
|
||||
function isPaid(inv) { return !!inv.paid_date; }
|
||||
function isDraft(inv) { return !inv.qbo_id; }
|
||||
function isOverdue(inv) { return !isPaid(inv) && !isPartiallyPaid(inv) && daysSince(inv.invoice_date) > OVERDUE_DAYS; }
|
||||
// ============================================================
|
||||
// Status Helpers
|
||||
// ============================================================
|
||||
|
||||
function isPaid(inv) {
|
||||
return !!inv.paid_date;
|
||||
}
|
||||
|
||||
function isDraft(inv) {
|
||||
return !inv.qbo_id;
|
||||
}
|
||||
|
||||
function isPartiallyPaid(inv) {
|
||||
const amountPaid = parseFloat(inv.amount_paid) || 0;
|
||||
const balance = parseFloat(inv.balance) ?? ((parseFloat(inv.total) || 0) - amountPaid);
|
||||
return !inv.paid_date && amountPaid > 0 && balance > 0;
|
||||
}
|
||||
function isSent(inv) {
|
||||
return !!inv.qbo_id && !isPaid(inv) && !isPartiallyPaid(inv) && !isOverdue(inv) && inv.email_status === 'sent';
|
||||
|
||||
function getLastSentDate(inv) {
|
||||
const sentDates = Array.isArray(inv.sent_dates) ? inv.sent_dates.filter(Boolean) : [];
|
||||
|
||||
if (sentDates.length > 0) {
|
||||
return sentDates[sentDates.length - 1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getTermDays(inv) {
|
||||
const terms = String(inv.terms || '').toLowerCase();
|
||||
|
||||
if (terms.includes('due on receipt') || terms.includes('upon receipt')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const match = terms.match(/net\s*(\d+)/i);
|
||||
if (match) {
|
||||
return parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
// Default in deiner App ist Net 30
|
||||
return 30;
|
||||
}
|
||||
|
||||
function addDays(dateValue, days) {
|
||||
const d = parseLocalDate(dateValue);
|
||||
if (!d) return null;
|
||||
|
||||
d.setDate(d.getDate() + days);
|
||||
return d;
|
||||
}
|
||||
|
||||
function isBeforeToday(dateObj) {
|
||||
if (!dateObj) return false;
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const compare = new Date(dateObj);
|
||||
compare.setHours(0, 0, 0, 0);
|
||||
|
||||
return compare < today;
|
||||
}
|
||||
|
||||
function getEffectiveDueDate(inv) {
|
||||
const lastSentDate = getLastSentDate(inv);
|
||||
|
||||
// Wichtig: Nie versendete Rechnungen können nicht overdue sein.
|
||||
if (!lastSentDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Wenn due_date vorhanden ist, darf es genutzt werden,
|
||||
// aber nur nachdem die Rechnung tatsächlich gesendet wurde.
|
||||
if (inv.due_date) {
|
||||
return parseLocalDate(inv.due_date);
|
||||
}
|
||||
|
||||
return addDays(lastSentDate, getTermDays(inv));
|
||||
}
|
||||
|
||||
function getOverdueDays(inv) {
|
||||
const dueDate = getEffectiveDueDate(inv);
|
||||
if (!dueDate) return 0;
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
dueDate.setHours(0, 0, 0, 0);
|
||||
|
||||
return Math.max(0, Math.floor((today - dueDate) / 86400000));
|
||||
}
|
||||
|
||||
function isOverdue(inv) {
|
||||
return !!inv.qbo_id
|
||||
&& !isPaid(inv)
|
||||
&& !isPartiallyPaid(inv)
|
||||
&& !!getLastSentDate(inv)
|
||||
&& isBeforeToday(getEffectiveDueDate(inv));
|
||||
}
|
||||
|
||||
function isSent(inv) {
|
||||
return !!inv.qbo_id
|
||||
&& !isPaid(inv)
|
||||
&& !isPartiallyPaid(inv)
|
||||
&& !isOverdue(inv)
|
||||
&& inv.email_status === 'sent';
|
||||
}
|
||||
|
||||
function isOpen(inv) {
|
||||
return !!inv.qbo_id && !isPaid(inv) && !isPartiallyPaid(inv) && !isOverdue(inv) && inv.email_status !== 'sent';
|
||||
return !!inv.qbo_id
|
||||
&& !isPaid(inv)
|
||||
&& !isPartiallyPaid(inv)
|
||||
&& !isOverdue(inv)
|
||||
&& inv.email_status !== 'sent';
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
@@ -235,7 +337,7 @@ function renderInvoiceRow(invoice) {
|
||||
}
|
||||
statusBadge += `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-yellow-100 text-yellow-800" title="Paid: $${amountPaid.toFixed(2)} / Balance: $${balance.toFixed(2)}">Partial</span>`;
|
||||
} else if (overdue) {
|
||||
statusBadge = `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-red-100 text-red-800" title="${daysSince(invoice.invoice_date)} days">Overdue</span>`;
|
||||
statusBadge = `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-red-100 text-red-800" title="${getOverdueDays(invoice)} days overdue">Overdue</span>`;
|
||||
} else if (hasQbo && invoice.email_status === 'sent') {
|
||||
statusBadge = `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-cyan-200 text-cyan-800">Sent</span>`;
|
||||
} else if (hasQbo) {
|
||||
|
||||
Reference in New Issue
Block a user