mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-09-05 14:51:07 +00:00
Add compound tax support (#753)
* test(taxes): cover compound_tax API semantics and validate it as boolean * feat(taxes): restore the compound tax toggle in tax type settings * feat(taxes): compute document-level compound tax amounts Two-pass recalculation: simple taxes on the discounted subtotal, compound taxes on the subtotal plus all simple taxes (v2 parity; branch order fixed -> compound -> inclusive back-out -> simple). Amounts now also recalculate on tax add/remove and on the tax-inclusive toggle, which previously never triggered recalculation. * feat(taxes): support compound taxes in per-item mode Per-item tax rows now carry the compound flag and charge compound taxes on the item's discounted base plus its simple taxes, through the same calcTaxAmount branch order as document-level taxes. The item row splits its tax sums into simple and compound so a compound row can never widen its own base. Also fixes two pre-existing bugs: the per-item tax dropdown read window.__taxTypes, which nothing ever assigned, so it always rendered empty (tax types are now fetched by the items table and passed down); and removing a tax row hard-zeroed the item's totals instead of re-syncing them. * fix(invoices): skip placeholder tax rows when persisting item taxes The document form keeps one empty placeholder tax row per item in per-item tax mode. Its empty name is nullified by the framework's empty-string middleware, so inserting it violated the NOT NULL constraint and turned every per-item save into a 500. The equivalent v2 guard keyed on a null amount, which the v3 stub (amount: 0) evades; keying on the missing tax_type_id catches it.
This commit is contained in:
@@ -161,18 +161,36 @@ export function calcItemTotal(subtotal: number, discountVal: number): number {
|
||||
return subtotal - discountVal
|
||||
}
|
||||
|
||||
/** Calculate tax amount for a given total and tax config */
|
||||
/**
|
||||
* Calculate tax amount for a given total and tax config.
|
||||
*
|
||||
* A compound tax is charged on the base plus every simple (non-compound) tax
|
||||
* already applied, and is never backed out of a tax-inclusive total.
|
||||
*
|
||||
* @param total Base amount in cents (document subtotal after discount, or an item total after discount)
|
||||
* @param percent Percentage rate, when the tax is percentage based
|
||||
* @param fixedAmount Flat amount in cents, when the tax is fixed
|
||||
* @param calculationType `'fixed'` or `'percentage'`
|
||||
* @param taxIncluded Whether the base already includes the tax (back it out)
|
||||
* @param compoundTax Whether the tax is charged on top of the simple taxes
|
||||
* @param simpleTaxTotal Sum of the non-compound tax amounts in cents
|
||||
*/
|
||||
export function calcTaxAmount(
|
||||
total: number,
|
||||
percent: number | null,
|
||||
fixedAmount: number | null,
|
||||
calculationType: string | null,
|
||||
taxIncluded: boolean | null,
|
||||
compoundTax = false,
|
||||
simpleTaxTotal = 0,
|
||||
): number {
|
||||
if (calculationType === 'fixed' && fixedAmount != null) {
|
||||
return fixedAmount
|
||||
}
|
||||
if (!total || !percent) return 0
|
||||
if (compoundTax) {
|
||||
return Math.round(((total + simpleTaxTotal) * percent) / 100)
|
||||
}
|
||||
if (taxIncluded) {
|
||||
return Math.round(total - total / (1 + percent / 100))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user