This commit is contained in:
2026-04-23 14:38:41 +03:00
parent f17dd1486c
commit c0f78c6e18
90 changed files with 10819 additions and 1583 deletions
@@ -0,0 +1,84 @@
/**
* 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,
}
}