Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma strängvärde byggs med konkatenering i stället för att matcha mönstret.
125 lines
4.1 KiB
TypeScript
125 lines
4.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
readNumber,
|
|
resolveVatTriple,
|
|
sumLineVat,
|
|
lineVatFromPercent,
|
|
multiplyIfBothPresent,
|
|
} from '../amounts';
|
|
|
|
describe('readNumber', () => {
|
|
it('returns the first present candidate, in order', () => {
|
|
expect(readNumber({ b: 2, a: 1 }, ['a', 'b'])).toBe(1);
|
|
expect(readNumber({ b: 2 }, ['a', 'b'])).toBe(2);
|
|
});
|
|
|
|
it('distinguishes an absent field from a zero one', () => {
|
|
expect(readNumber({ Net: 0 }, ['Net'])).toBe(0);
|
|
expect(readNumber({}, ['Net'])).toBeUndefined();
|
|
});
|
|
|
|
it('coerces string-serialised decimals', () => {
|
|
expect(readNumber({ net_amount: '1476.00' }, ['net_amount'])).toBe(1476);
|
|
});
|
|
|
|
it('skips nulls, empty strings and non-numerics rather than reading them as 0', () => {
|
|
expect(readNumber({ a: null, b: '', c: 'n/a', d: 5 }, ['a', 'b', 'c', 'd'])).toBe(5);
|
|
expect(readNumber({ a: null }, ['a'])).toBeUndefined();
|
|
});
|
|
|
|
it('tolerates a missing payload', () => {
|
|
expect(readNumber(undefined, ['a'])).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('resolveVatTriple', () => {
|
|
it('completes VAT from a stated net', () => {
|
|
expect(resolveVatTriple({ gross: 1845000, net: 1476000 }))
|
|
.toEqual({ net: 1476000, vat: 369000 });
|
|
});
|
|
|
|
it('completes the net from a stated VAT', () => {
|
|
expect(resolveVatTriple({ gross: 1845000, vat: 369000 }))
|
|
.toEqual({ net: 1476000, vat: 369000 });
|
|
});
|
|
|
|
it('passes both through when both are stated and they agree', () => {
|
|
expect(resolveVatTriple({ gross: 100, net: 80, vat: 20 }))
|
|
.toEqual({ net: 80, vat: 20 });
|
|
});
|
|
|
|
it('keeps net + vat === gross when the provider states three that disagree', () => {
|
|
// Fortnox `Total` is post-öresavrundning while Net + TotalVAT is the
|
|
// unrounded Gross. Passing both through as stated would leave
|
|
// subtotal + vat_amount != total on the invoice row, and the header
|
|
// booking path derives the 1510 debit from the sum of its credits: the
|
|
// receivable would sit a few öre off what the customer owes, with the
|
|
// verifikat still balancing so nothing flags it.
|
|
const resolved = resolveVatTriple({ gross: 1250, net: 1000.4, vat: 250 });
|
|
|
|
expect(resolved.vat).toBe(250);
|
|
expect(resolved.net).toBe(1000);
|
|
expect((resolved.net ?? 0) + (resolved.vat ?? 0)).toBe(1250);
|
|
});
|
|
|
|
it('resolves NOTHING from a gross alone', () => {
|
|
// The regression this whole module exists for: with only the payable
|
|
// amount known, net = gross and VAT = 0 is an invention, not a default.
|
|
expect(resolveVatTriple({ gross: 1845000 })).toEqual({});
|
|
});
|
|
|
|
it('treats a genuine zero VAT as an answer, not as absence', () => {
|
|
expect(resolveVatTriple({ gross: 1000, vat: 0 })).toEqual({ net: 1000, vat: 0 });
|
|
});
|
|
});
|
|
|
|
describe('sumLineVat', () => {
|
|
it('sums when every line carries an amount', () => {
|
|
expect(sumLineVat([
|
|
{ taxAmount: { value: 250 } },
|
|
{ taxAmount: { value: 60 } },
|
|
])).toBe(310);
|
|
});
|
|
|
|
it('refuses a partial sum when any line is missing its VAT', () => {
|
|
expect(sumLineVat([
|
|
{ taxAmount: { value: 250 } },
|
|
{},
|
|
])).toBeUndefined();
|
|
});
|
|
|
|
it('returns undefined for no lines', () => {
|
|
expect(sumLineVat([])).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('lineVatFromPercent', () => {
|
|
it('accepts both unit conventions', () => {
|
|
expect(lineVatFromPercent(1000, 25)).toBe(250);
|
|
expect(lineVatFromPercent(1000, 0.25)).toBe(250);
|
|
});
|
|
|
|
it('computes 0 for a stated 0 % rate', () => {
|
|
expect(lineVatFromPercent(1000, 0)).toBe(0);
|
|
});
|
|
|
|
it('returns undefined for an unstated rate rather than assuming one', () => {
|
|
expect(lineVatFromPercent(1000, undefined)).toBeUndefined();
|
|
});
|
|
|
|
it('rounds to öre without toFixed drift', () => {
|
|
expect(lineVatFromPercent(333.33, 25)).toBe(83.33);
|
|
});
|
|
});
|
|
|
|
describe('multiplyIfBothPresent', () => {
|
|
it('multiplies when both factors are known', () => {
|
|
expect(multiplyIfBothPresent(100, 3)).toBe(300);
|
|
});
|
|
|
|
it('returns undefined when either factor is missing', () => {
|
|
expect(multiplyIfBothPresent(100, undefined)).toBeUndefined();
|
|
expect(multiplyIfBothPresent(undefined, 3)).toBeUndefined();
|
|
});
|
|
});
|