- Added validation types for EN16931 compliance in `validation.types.ts`, including interfaces for `ValidationResult`, `ValidationOptions`, and `ValidationReport`. - Introduced `VATCategoriesValidator` in `vat-categories.validator.ts` to validate VAT categories according to EN16931 rules, including detailed checks for standard, zero-rated, exempt, reverse charge, intra-community, export, and out-of-scope services. - Enhanced `IEInvoiceMetadata` interface in `en16931-metadata.ts` to include additional fields required for full standards compliance, such as delivery information, payment information, allowances, and charges. - Implemented helper methods for VAT calculations and validation logic to ensure accurate compliance with EN16931 standards.
128 lines
4.5 KiB
TypeScript
128 lines
4.5 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import {
|
|
getCurrencyMinorUnits,
|
|
roundToCurrency,
|
|
getCurrencyTolerance,
|
|
areMonetaryValuesEqual,
|
|
CurrencyCalculator,
|
|
RoundingMode
|
|
} from '../ts/formats/utils/currency.utils.js';
|
|
|
|
tap.test('Currency Utils - should handle different currency decimal places', async () => {
|
|
// Standard 2 decimal currencies
|
|
expect(getCurrencyMinorUnits('EUR')).toEqual(2);
|
|
expect(getCurrencyMinorUnits('USD')).toEqual(2);
|
|
expect(getCurrencyMinorUnits('GBP')).toEqual(2);
|
|
|
|
// Zero decimal currencies
|
|
expect(getCurrencyMinorUnits('JPY')).toEqual(0);
|
|
expect(getCurrencyMinorUnits('KRW')).toEqual(0);
|
|
|
|
// Three decimal currencies
|
|
expect(getCurrencyMinorUnits('KWD')).toEqual(3);
|
|
expect(getCurrencyMinorUnits('TND')).toEqual(3);
|
|
|
|
// Unknown currency defaults to 2
|
|
expect(getCurrencyMinorUnits('XXX')).toEqual(2);
|
|
});
|
|
|
|
tap.test('Currency Utils - should round values correctly', async () => {
|
|
// EUR - 2 decimals
|
|
expect(roundToCurrency(10.234, 'EUR')).toEqual(10.23);
|
|
expect(roundToCurrency(10.235, 'EUR')).toEqual(10.24); // Half-up
|
|
expect(roundToCurrency(10.236, 'EUR')).toEqual(10.24);
|
|
|
|
// JPY - 0 decimals
|
|
expect(roundToCurrency(1234.56, 'JPY')).toEqual(1235);
|
|
expect(roundToCurrency(1234.49, 'JPY')).toEqual(1234);
|
|
|
|
// KWD - 3 decimals
|
|
expect(roundToCurrency(10.2345, 'KWD')).toEqual(10.235); // Half-up
|
|
expect(roundToCurrency(10.2344, 'KWD')).toEqual(10.234);
|
|
});
|
|
|
|
tap.test('Currency Utils - should use different rounding modes', async () => {
|
|
const value = 10.235;
|
|
|
|
// Half-up (default)
|
|
expect(roundToCurrency(value, 'EUR', RoundingMode.HALF_UP)).toEqual(10.24);
|
|
|
|
// Half-down
|
|
expect(roundToCurrency(value, 'EUR', RoundingMode.HALF_DOWN)).toEqual(10.23);
|
|
|
|
// Half-even (banker's rounding)
|
|
expect(roundToCurrency(10.235, 'EUR', RoundingMode.HALF_EVEN)).toEqual(10.24); // 23 is odd, round up
|
|
expect(roundToCurrency(10.245, 'EUR', RoundingMode.HALF_EVEN)).toEqual(10.24); // 24 is even, round down
|
|
|
|
// Always up
|
|
expect(roundToCurrency(10.231, 'EUR', RoundingMode.UP)).toEqual(10.24);
|
|
|
|
// Always down (truncate)
|
|
expect(roundToCurrency(10.239, 'EUR', RoundingMode.DOWN)).toEqual(10.23);
|
|
});
|
|
|
|
tap.test('Currency Utils - should calculate correct tolerance', async () => {
|
|
// EUR - tolerance is 0.005 (half of 0.01)
|
|
expect(getCurrencyTolerance('EUR')).toEqual(0.005);
|
|
|
|
// JPY - tolerance is 0.5 (half of 1)
|
|
expect(getCurrencyTolerance('JPY')).toEqual(0.5);
|
|
|
|
// KWD - tolerance is 0.0005 (half of 0.001)
|
|
expect(getCurrencyTolerance('KWD')).toEqual(0.0005);
|
|
});
|
|
|
|
tap.test('Currency Utils - should compare monetary values with tolerance', async () => {
|
|
// EUR comparisons
|
|
expect(areMonetaryValuesEqual(10.23, 10.234, 'EUR')).toEqual(true); // Within tolerance
|
|
expect(areMonetaryValuesEqual(10.23, 10.236, 'EUR')).toEqual(false); // Outside tolerance
|
|
|
|
// JPY comparisons
|
|
expect(areMonetaryValuesEqual(1234, 1234.4, 'JPY')).toEqual(true); // Within tolerance
|
|
expect(areMonetaryValuesEqual(1234, 1235, 'JPY')).toEqual(false); // Outside tolerance
|
|
|
|
// KWD comparisons
|
|
expect(areMonetaryValuesEqual(10.234, 10.2344, 'KWD')).toEqual(true); // Within tolerance
|
|
expect(areMonetaryValuesEqual(10.234, 10.235, 'KWD')).toEqual(false); // Outside tolerance
|
|
});
|
|
|
|
tap.test('CurrencyCalculator - should perform EN16931 calculations', async () => {
|
|
// EUR calculator
|
|
const eurCalc = new CurrencyCalculator('EUR');
|
|
|
|
// Line net calculation
|
|
const lineNet = eurCalc.calculateLineNet(5, 19.99, 2.50);
|
|
expect(lineNet).toEqual(97.45); // (5 * 19.99) - 2.50 = 97.45
|
|
|
|
// VAT calculation
|
|
const vat = eurCalc.calculateVAT(100, 19);
|
|
expect(vat).toEqual(19.00);
|
|
|
|
// JPY calculator (no decimals)
|
|
const jpyCalc = new CurrencyCalculator('JPY');
|
|
|
|
const jpyLineNet = jpyCalc.calculateLineNet(3, 1234.56);
|
|
expect(jpyLineNet).toEqual(3704); // Rounded to no decimals
|
|
|
|
const jpyVat = jpyCalc.calculateVAT(10000, 8);
|
|
expect(jpyVat).toEqual(800);
|
|
});
|
|
|
|
tap.test('CurrencyCalculator - should handle edge cases', async () => {
|
|
const calc = new CurrencyCalculator('EUR');
|
|
|
|
// Rounding at exact midpoint
|
|
expect(calc.round(10.235)).toEqual(10.24); // Half-up
|
|
expect(calc.round(10.245)).toEqual(10.25); // Half-up
|
|
|
|
// Very small values
|
|
expect(calc.round(0.001)).toEqual(0.00);
|
|
expect(calc.round(0.004)).toEqual(0.00);
|
|
expect(calc.round(0.005)).toEqual(0.01);
|
|
|
|
// Negative values
|
|
expect(calc.round(-10.234)).toEqual(-10.23);
|
|
expect(calc.round(-10.235)).toEqual(-10.24);
|
|
});
|
|
|
|
tap.start(); |