import { expect, tap } from '@git.zone/tstest/tapbundle'; import { DecimalCurrencyCalculator } from '../ts/formats/utils/currency.calculator.decimal.js'; import { Decimal } from '../ts/formats/utils/decimal.js'; tap.test('DecimalCurrencyCalculator - EUR calculations', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); // Line calculation const lineNet = calculator.calculateLineNet('3', '33.333', '0'); expect(lineNet.toString()).toEqual('100'); // calculateLineNet rounds the result // VAT calculation const vat = calculator.calculateVAT('100', '19'); expect(vat.toString()).toEqual('19'); // Gross amount const gross = calculator.calculateGrossAmount('100', '19'); expect(gross.toString()).toEqual('119'); }); tap.test('DecimalCurrencyCalculator - JPY calculations (no decimals)', async () => { const calculator = new DecimalCurrencyCalculator('JPY'); // Should round to 0 decimal places const amount = calculator.round('1234.56'); expect(amount.toString()).toEqual('1235'); // VAT calculation const vat = calculator.calculateVAT('1000', '10'); expect(vat.toString()).toEqual('100'); }); tap.test('DecimalCurrencyCalculator - KWD calculations (3 decimals)', async () => { const calculator = new DecimalCurrencyCalculator('KWD'); // Should maintain 3 decimal places const amount = calculator.round('123.4567'); expect(amount.toString()).toEqual('123.457'); // VAT calculation const vat = calculator.calculateVAT('100.000', '5'); expect(vat.toString()).toEqual('5'); }); tap.test('DecimalCurrencyCalculator - sum line items', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); const items = [ { quantity: '2', unitPrice: '50.00', discount: '5.00' }, { quantity: '3', unitPrice: '33.33', discount: '0' }, { quantity: '1', unitPrice: '100.00', discount: '10.00' } ]; const total = calculator.sumLineItems(items); expect(total.toString()).toEqual('284.99'); }); tap.test('DecimalCurrencyCalculator - VAT breakdown', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); const items = [ { netAmount: '100.00', vatRate: '19' }, { netAmount: '50.00', vatRate: '19' }, { netAmount: '200.00', vatRate: '7' } ]; const breakdown = calculator.calculateVATBreakdown(items); expect(breakdown).toHaveLength(2); const vat19 = breakdown.find(b => b.rate.toString() === '19'); expect(vat19?.baseAmount.toString()).toEqual('150'); expect(vat19?.vatAmount.toString()).toEqual('28.5'); const vat7 = breakdown.find(b => b.rate.toString() === '7'); expect(vat7?.baseAmount.toString()).toEqual('200'); expect(vat7?.vatAmount.toString()).toEqual('14'); }); tap.test('DecimalCurrencyCalculator - distribute amount', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); // Distribute 100 EUR across three items const items = [ { value: '30' }, // 30% { value: '50' }, // 50% { value: '20' } // 20% ]; const distributed = calculator.distributeAmount('100', items); expect(distributed[0].toString()).toEqual('30'); expect(distributed[1].toString()).toEqual('50'); expect(distributed[2].toString()).toEqual('20'); // Sum should equal total const sum = Decimal.sum(distributed); expect(sum.toString()).toEqual('100'); }); tap.test('DecimalCurrencyCalculator - compound adjustments', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); const adjustments = [ { type: 'allowance' as const, value: '10', isPercentage: true }, // -10% { type: 'charge' as const, value: '5', isPercentage: false }, // +5 EUR { type: 'allowance' as const, value: '2', isPercentage: false } // -2 EUR ]; const result = calculator.calculateCompoundAmount('100', adjustments); // 100 - 10% = 90, + 5 = 95, - 2 = 93 expect(result.toString()).toEqual('93'); }); tap.test('DecimalCurrencyCalculator - validation', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); // Valid calculation const result1 = calculator.validateCalculation('119.00', '119.00', 'BR-CO-15'); expect(result1.valid).toBeTrue(); expect(result1.expected).toEqual('119.00'); expect(result1.calculated).toEqual('119.00'); // Invalid calculation const result2 = calculator.validateCalculation('119.00', '118.99', 'BR-CO-15'); expect(result2.valid).toBeFalse(); expect(result2.difference).toEqual('0.01'); }); tap.test('DecimalCurrencyCalculator - different rounding modes', async () => { // HALF_DOWN for specific requirements const calculator = new DecimalCurrencyCalculator('EUR', 'HALF_DOWN'); const amount1 = calculator.round('10.125'); // Should round down expect(amount1.toString()).toEqual('10.12'); const amount2 = calculator.round('10.135'); // Should round down with HALF_DOWN expect(amount2.toString()).toEqual('10.13'); // HALF_EVEN (Banker's rounding) for statistical accuracy const bankerCalc = new DecimalCurrencyCalculator('EUR', 'HALF_EVEN'); const amount3 = bankerCalc.round('10.125'); // Round to even (down) expect(amount3.toString()).toEqual('10.12'); const amount4 = bankerCalc.round('10.135'); // Round to even (up) expect(amount4.toString()).toEqual('10.14'); }); tap.test('DecimalCurrencyCalculator - real invoice scenario', async () => { const calculator = new DecimalCurrencyCalculator('EUR'); // Invoice lines const lines = [ { quantity: '2.5', unitPrice: '45.60', discount: '5.00' }, { quantity: '10', unitPrice: '12.34', discount: '0' }, { quantity: '1', unitPrice: '250.00', discount: '25.00' } ]; // Calculate line totals const lineTotal = calculator.sumLineItems(lines); expect(lineTotal.toString()).toEqual('457.4'); // Apply document-level allowance (2%) const allowance = calculator.calculatePaymentDiscount(lineTotal, '2'); expect(allowance.toString()).toEqual('9.15'); const netAfterAllowance = lineTotal.subtract(allowance); expect(calculator.round(netAfterAllowance).toString()).toEqual('448.25'); // Calculate VAT at 19% const vat = calculator.calculateVAT(netAfterAllowance, '19'); expect(vat.toString()).toEqual('85.17'); // Total with VAT const total = calculator.calculateGrossAmount(netAfterAllowance, vat); expect(total.toString()).toEqual('533.42'); // Format for display const formatted = calculator.formatAmount(total); expect(formatted).toEqual('533.42 EUR'); }); export default tap.start();