Files
invoice-system/public/js/utils/api.js
2026-05-25 13:07:41 -05:00

215 lines
9.7 KiB
JavaScript

/**
* 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;