diff --git a/public/app.js b/public/app.js index 8097309..7014739 100644 --- a/public/app.js +++ b/public/app.js @@ -1,5 +1,5 @@ // Global state -let customers = []; // shared, updated by customer-view.js +let customers = window.customers || []; let quotes = []; let invoices = []; let currentQuoteId = null; @@ -20,14 +20,14 @@ function customerSearch(type) { get filteredCustomers() { // Wenn Suche leer ist: ALLE Kunden zurückgeben (kein .slice mehr!) if (!this.search) { - return customers; + return (window.getCustomers ? window.getCustomers() : customers); } const searchLower = this.search.toLowerCase(); // Filtern: Sucht im Namen, Line1, Stadt oder Account Nummer // Auch hier: Kein .slice mehr, damit alle Ergebnisse (z.B. alle mit 'C') angezeigt werden - return customers.filter(c => + return (window.getCustomers ? window.getCustomers() : customers).filter(c => (c.name || '').toLowerCase().includes(searchLower) || (c.line1 || '').toLowerCase().includes(searchLower) || (c.city || '').toLowerCase().includes(searchLower) || @@ -76,9 +76,7 @@ window.customerSearch = customerSearch; // Initialize app document.addEventListener('DOMContentLoaded', () => { - loadCustomers(); loadQuotes(); - //loadInvoices(); setDefaultDate(); checkCurrentLogo(); loadLaborRate(); @@ -95,7 +93,6 @@ document.addEventListener('DOMContentLoaded', () => { } // Setup form handlers - document.getElementById('customer-form').addEventListener('submit', handleCustomerSubmit); document.getElementById('quote-form').addEventListener('submit', handleQuoteSubmit); document.getElementById('invoice-form').addEventListener('submit', handleInvoiceSubmit); document.getElementById('quote-tax-exempt').addEventListener('change', updateQuoteTotals); @@ -128,7 +125,9 @@ function showTab(tabName) { } else if (tabName === 'invoices') { loadInvoices(); } else if (tabName === 'customers') { - loadCustomers(); + if (window.customerView) { + window.customerView.loadCustomers(); + } } else if (tabName === 'settings') { checkCurrentLogo(); } @@ -204,167 +203,6 @@ async function uploadLogo() { } } - - - -// --- 2. Credits async laden --- -async function loadCustomerCredits() { - const qboCustomers = customers.filter(c => c.qbo_id); - for (const cust of qboCustomers) { - const span = document.getElementById(`customer-credit-${cust.id}`); - if (!span) continue; - try { - const res = await fetch(`/api/qbo/customer-credit/${cust.qbo_id}`); - const data = await res.json(); - if (data.credit > 0) { - span.innerHTML = `Credit: $${data.credit.toFixed(2)}`; - } else { - span.textContent = ''; - } - } catch (e) { - span.textContent = ''; - } - } -} -// --- 3. Downpayment Dialog --- -async function openDownpaymentModal(customerId, customerQboId, customerName) { - // Load QBO data if needed - let bankAccounts = []; - let paymentMethods = []; - try { - const [accRes, pmRes] = await Promise.all([ - fetch('/api/qbo/accounts'), - fetch('/api/qbo/payment-methods') - ]); - if (accRes.ok) bankAccounts = await accRes.json(); - if (pmRes.ok) paymentMethods = await pmRes.json(); - } catch (e) { console.error('Error loading QBO data:', e); } - - const accountOptions = bankAccounts.map(a => ``).join(''); - const filtered = paymentMethods.filter(p => /check|ach/i.test(p.name)); - const methods = filtered.length > 0 ? filtered : paymentMethods; - const methodOptions = methods.map(p => ``).join(''); - const today = new Date().toISOString().split('T')[0]; - - let modal = document.getElementById('downpayment-modal'); - if (!modal) { - modal = document.createElement('div'); - modal.id = 'downpayment-modal'; - modal.className = 'modal fixed inset-0 bg-black bg-opacity-50 z-50 justify-center items-start pt-10 overflow-y-auto'; - document.body.appendChild(modal); - } - - modal.innerHTML = ` -
-
-

💰 Record Downpayment

- -
- -
-

- Customer: ${customerName}
- This will record an unapplied payment (credit) on the customer's QBO account. -

-
- -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- - -
-
`; - - modal.classList.add('active'); - document.getElementById('dp-amount').focus(); -} - -function closeDownpaymentModal() { - const modal = document.getElementById('downpayment-modal'); - if (modal) modal.classList.remove('active'); -} -async function submitDownpayment(customerId, customerQboId) { - const amount = parseFloat(document.getElementById('dp-amount').value); - const date = document.getElementById('dp-date').value; - const ref = document.getElementById('dp-reference').value; - const methodSelect = document.getElementById('dp-method'); - const depositSelect = document.getElementById('dp-deposit'); - - if (!amount || amount <= 0) { alert('Please enter an amount.'); return; } - if (!date || !methodSelect.value || !depositSelect.value) { alert('Please fill in all fields.'); return; } - - if (!confirm(`Record downpayment of $${amount.toFixed(2)}?`)) return; - - const btn = document.getElementById('dp-submit-btn'); - btn.innerHTML = '⏳ Processing...'; - btn.disabled = true; - if (typeof showSpinner === 'function') showSpinner('Recording downpayment in QBO...'); - - try { - const response = await fetch('/api/qbo/record-downpayment', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - customer_id: customerId, - customer_qbo_id: customerQboId, - amount: amount, - payment_date: date, - reference_number: ref, - payment_method_id: methodSelect.value, - payment_method_name: methodSelect.options[methodSelect.selectedIndex]?.text || '', - deposit_to_account_id: depositSelect.value, - deposit_to_account_name: depositSelect.options[depositSelect.selectedIndex]?.text || '' - }) - }); - const result = await response.json(); - if (response.ok) { - alert(`✅ ${result.message}`); - closeDownpaymentModal(); - renderCustomers(); // Refresh credit display - } else { - alert(`❌ Error: ${result.error}`); - } - } catch (e) { - alert('Network error.'); - } finally { - btn.innerHTML = '💰 Record Downpayment'; - btn.disabled = false; - if (typeof hideSpinner === 'function') hideSpinner(); - } -} - // Quote Management async function loadQuotes() { try {