/** * useDocumentTotals * * Pure calculation hook for invoice-style documents (invoices, bills, estimates, * credit notes, vendor credits, etc.). * * Inputs * ──────────────────────────────────────────────────────────────────────────── * lineItems – flat array of all items (parts + services + expenses) * discountType – 'no' | 'line_item_level' | 'transaction_level' * discountAmount – used when discountType === 'transaction_level' * taxRate – percentage (e.g. 15 for 15 %). Undefined → no tax. * * Calculation order * ──────────────────────────────────────────────────────────────────────────── * 1. subTotal = Σ (qty × rate) * 2. lineItemDiscount = Σ discount_amount (only when discountType === 'line_item_level') * 3. transactionDiscount = discountAmount (only when discountType === 'transaction_level') * 4. totalDiscount = lineItemDiscount + transactionDiscount * 5. afterDiscount = subTotal - totalDiscount * 6. taxAmount = afterDiscount × (taxRate / 100) * 7. total = afterDiscount + taxAmount */ export type DocumentLineItem = { quantity: number rate: number discount_amount?: number } export type UseDocumentTotalsInput = { lineItems: DocumentLineItem[] discountType?: string | null discountAmount?: number taxRate?: number } export type DocumentTotals = { subTotal: number lineItemDiscount: number transactionDiscount: number totalDiscount: number afterDiscount: number taxAmount: number total: number hasLineItems: boolean } export function useDocumentTotals({ lineItems, discountType, discountAmount, taxRate, }: UseDocumentTotalsInput): DocumentTotals { const subTotal = lineItems.reduce((sum, item) => { const qty = Number(item.quantity) || 0 const rate = Number(item.rate) || 0 return sum + qty * rate }, 0) const lineItemDiscount = discountType === "line_item_level" ? lineItems.reduce((sum, item) => sum + (Number(item.discount_amount) || 0), 0) : 0 const transactionDiscount = discountType === "transaction_level" ? Number(discountAmount) || 0 : 0 const totalDiscount = lineItemDiscount + transactionDiscount const afterDiscount = Math.max(0, subTotal - totalDiscount) const taxAmount = taxRate ? afterDiscount * (taxRate / 100) : 0 const total = afterDiscount + taxAmount return { subTotal, lineItemDiscount, transactionDiscount, totalDiscount, afterDiscount, taxAmount, total, hasLineItems: lineItems.length > 0, } }