/** * API Utility * Centralized API calls for the frontend */ const API = { // Customer API customers: { getAll: () => fetch('/api/customers').then(r => r.json()), get: (id) => fetch(`/api/customers/${id}`).then(r => r.json()), create: (data) => fetch('/api/customers', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), update: (id, data) => fetch(`/api/customers/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), delete: (id) => fetch(`/api/customers/${id}`, { method: 'DELETE' }).then(r => r.json()), exportToQbo: (id) => fetch(`/api/customers/${id}/export-qbo`, { method: 'POST' }).then(r => r.json()) }, // Quote API quotes: { getAll: () => fetch('/api/quotes').then(r => r.json()), get: (id) => fetch(`/api/quotes/${id}`).then(r => r.json()), create: (data) => fetch('/api/quotes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), update: (id, data) => fetch(`/api/quotes/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), delete: (id) => fetch(`/api/quotes/${id}`, { method: 'DELETE' }).then(r => r.json()), convertToInvoice: (id) => fetch(`/api/quotes/${id}/convert-to-invoice`, { method: 'POST' }).then(r => r.json()), getPdf: (id) => window.open(`/api/quotes/${id}/pdf`, '_blank'), getHtml: (id) => window.open(`/api/quotes/${id}/html`, '_blank') }, // Invoice API invoices: { getAll: () => fetch('/api/invoices').then(r => r.json()), get: (id) => fetch(`/api/invoices/${id}`).then(r => r.json()), getNextNumber: () => fetch('/api/invoices/next-number').then(r => r.json()), create: (data) => fetch('/api/invoices', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), update: (id, data) => fetch(`/api/invoices/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), delete: (id) => fetch(`/api/invoices/${id}`, { method: 'DELETE' }).then(r => r.json()), exportToQbo: (id) => fetch(`/api/invoices/${id}/export`, { method: 'POST' }).then(r => r.json()), updateQbo: (id) => fetch(`/api/invoices/${id}/update-qbo`, { method: 'POST' }).then(r => r.json()), markPaid: (id, paidDate) => fetch(`/api/invoices/${id}/mark-paid`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paid_date: paidDate }) }).then(r => r.json()), markUnpaid: (id) => fetch(`/api/invoices/${id}/mark-unpaid`, { method: 'PATCH' }).then(r => r.json()), resetQbo: (id) => fetch(`/api/invoices/${id}/reset-qbo`, { method: 'PATCH' }).then(r => r.json()), setEmailStatus: (id, status) => fetch(`/api/invoices/${id}/email-status`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status }) }).then(r => r.json()), getPdf: (id) => window.open(`/api/invoices/${id}/pdf`, '_blank'), getHtml: (id) => window.open(`/api/invoices/${id}/html`, '_blank'), updateSentDates: (id, dates) => fetch(`/api/invoices/${id}/sent-dates`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sent_dates: dates }) }).then(r => r.json()) }, // Payment API payments: { getAll: () => fetch('/api/payments').then(r => r.json()), record: (data) => fetch('/api/qbo/record-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()) }, // NEU: Stripe API stripe: { createPaymentLink: (invoiceId) => fetch(`/api/invoices/${invoiceId}/create-payment-link`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(r => r.json()), checkPayment: (invoiceId) => fetch(`/api/invoices/${invoiceId}/check-payment`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }).then(r => r.json()) }, // QBO API qbo: { getStatus: () => fetch('/api/qbo/status').then(r => r.json()), getAccounts: () => fetch('/api/qbo/accounts').then(r => r.json()), getPaymentMethods: () => fetch('/api/qbo/payment-methods').then(r => r.json()), getLaborRate: () => fetch('/api/qbo/labor-rate').then(r => r.json()), getLastSync: () => fetch('/api/qbo/last-sync').then(r => r.json()), getOverdue: () => fetch('/api/qbo/overdue').then(r => r.json()), importUnpaid: () => fetch('/api/qbo/import-unpaid', { method: 'POST' }).then(r => r.json()), syncPayments: () => fetch('/api/qbo/sync-payments', { method: 'POST' }).then(r => r.json()), auth: () => window.location.href = '/auth/qbo' }, accounting: { // Phase 1 getAccounts: (type = null, activeOnly = true) => { const params = new URLSearchParams(); if (type) params.set('type', type); if (!activeOnly) params.set('activeOnly', 'false'); const qs = params.toString(); return fetch('/api/accounting/accounts' + (qs ? '?' + qs : '')).then(r => r.json()); }, getRegister: (accountId, startDate, endDate) => { const params = new URLSearchParams({ accountId }); if (startDate) params.set('startDate', startDate); if (endDate) params.set('endDate', endDate); return fetch('/api/accounting/register?' + params.toString()).then(r => r.json()); }, getProfitAndLoss: (startDate, endDate, accountingMethod = 'Accrual') => { const params = new URLSearchParams({ startDate, endDate, accountingMethod }); return fetch('/api/accounting/reports/profit-loss?' + params.toString()).then(r => r.json()); }, getBalanceSheet: (asOfDate, accountingMethod = 'Accrual') => { const params = new URLSearchParams({ asOfDate, accountingMethod }); return fetch('/api/accounting/reports/balance-sheet?' + params.toString()).then(r => r.json()); }, // Phase 2 Lieferung 1 — Sync + Cache-Reads syncAccounts: () => fetch('/api/accounting/sync-accounts', { method: 'POST' }).then(r => r.json()), syncVendors: () => fetch('/api/accounting/sync-vendors', { method: 'POST' }).then(r => r.json()), getSyncStatus: () => fetch('/api/accounting/sync-status').then(r => r.json()), getVendors: (search = '', limit = 200) => { const params = new URLSearchParams(); if (search) params.set('search', search); if (limit) params.set('limit', String(limit)); const qs = params.toString(); return fetch('/api/accounting/vendors' + (qs ? '?' + qs : '')).then(r => r.json()); }, getExpenseAccounts: () => fetch('/api/accounting/expense-accounts').then(r => r.json()), getPaymentAccounts: () => fetch('/api/accounting/payment-accounts').then(r => r.json()), getPaymentMethods: () => fetch('/api/accounting/payment-methods').then(r => r.json()), // Phase 2 Lieferung 2 — Mutations + List createVendor: (data) => fetch('/api/accounting/vendors', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), createExpense: (data) => fetch('/api/accounting/expenses', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), updateExpense: (id, data) => fetch(`/api/accounting/expenses/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(r => r.json()), attachToExpense: (expenseId, file, note) => { const fd = new FormData(); fd.append('file', file); if (note) fd.append('note', note); return fetch(`/api/accounting/expenses/${expenseId}/attach`, { method: 'POST', body: fd // KEIN Content-Type-Header setzen — Browser fügt boundary auto ein! }).then(r => r.json()); }, getAttachmentLimits: () => fetch('/api/accounting/attachments/limits').then(r => r.json()), listExpenses: (startDate, endDate, onlyMine = false) => { const params = new URLSearchParams({ startDate, endDate }); if (onlyMine) params.set('onlyMine', 'true'); return fetch('/api/accounting/expenses?' + params.toString()).then(r => r.json()); } }, // Settings API settings: { getLogo: () => fetch('/api/logo-info').then(r => r.json()), uploadLogo: (file) => { const formData = new FormData(); formData.append('logo', file); return fetch('/api/upload-logo', { method: 'POST', body: formData }).then(r => r.json()); } } }; // Make globally available window.API = API;