Validation: shared NaN-guarded Zod coercion helpers in schemas/common.ts replace the raw number|string transform idiom across every schema (the root-cause NaN bug class); emailOrEmpty + lenient isoDateString/timeString. Security: roles privilege-escalation closed; refresh-token family revocation on reuse; TOTP uses config params; read endpoints permission-guarded; received-invoices gross VAT on all paths; orders-pdf custom-items authz. Concurrency: $queryRaw SELECT...FOR UPDATE locks in ascending-id order (warehouse confirm/cancel, attendance lockUserRow); uniqueness checks moved into create transactions (TOCTOU -> 409); deterministic id tiebreak on second-precision timestamp ordering (plan resolveCell/resolveGrid, warehouse FIFO). Frontend: Rules-of-Hooks fixed across ~14 pages + PlanCellModal; UTC-date persisted fields; dashboard invalidation gaps; stale-closure confirm bugs. Tooling/tests: ESLint flat config (react-hooks/rules-of-hooks = error) + Prettier; tsconfig.test.json so tsc -b type-checks the tests; removed 3 dead deps; npm audit fix (8 -> 3). Suite 195 -> 247 (happy-path auth, FIFO oldest-first, flakiness fixes), isolated on app_test via .env.test with a hard-throw setup guard. Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
2.3 KiB
TypeScript
52 lines
2.3 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { vatFromGross } from "../routes/admin/received-invoices";
|
|
|
|
// `amount` on a received invoice is the GROSS total (VAT included). The VAT is
|
|
// the portion contained within it: gross * rate / (100 + rate).
|
|
describe("vatFromGross (VAT contained in a gross total)", () => {
|
|
it("derives VAT from a real gross total at 21%", () => {
|
|
// 22 542,91 @ 21% → 3 912,41 (base 18 630,50). User-confirmed figures.
|
|
expect(vatFromGross(22542.91, 21)).toBeCloseTo(3912.41, 2);
|
|
expect(22542.91 - vatFromGross(22542.91, 21)).toBeCloseTo(18630.5, 2);
|
|
});
|
|
|
|
it("derives VAT at other rates", () => {
|
|
expect(vatFromGross(1210, 21)).toBeCloseTo(210, 2); // base 1000
|
|
expect(vatFromGross(1100, 10)).toBeCloseTo(100, 2); // base 1000
|
|
expect(vatFromGross(1150, 15)).toBeCloseTo(150, 2); // base 1000
|
|
});
|
|
|
|
it("returns 0 when there is no VAT", () => {
|
|
expect(vatFromGross(5000, 0)).toBe(0);
|
|
});
|
|
|
|
it("returns 0 for a zero amount", () => {
|
|
expect(vatFromGross(0, 21)).toBe(0);
|
|
});
|
|
|
|
it("rounds the cent UP when the raw VAT's third decimal forces a carry", () => {
|
|
// 115.50 @ 21% -> raw 20.045454... the third decimal (5) rounds the cent
|
|
// up to 20.05. Pinned EXACTLY (not toBeCloseTo) so a truncate-instead-of-
|
|
// round regression (which would yield 20.04) fails.
|
|
expect(vatFromGross(115.5, 21)).toBe(20.05);
|
|
// 95.70 @ 21% -> raw 16.609090... rounds up to 16.61.
|
|
expect(vatFromGross(95.7, 21)).toBe(16.61);
|
|
});
|
|
|
|
it("rounds the cent DOWN when below the half-cent", () => {
|
|
// 100.40 @ 21% -> raw 17.424793... rounds down to 17.42.
|
|
expect(vatFromGross(100.4, 21)).toBe(17.42);
|
|
});
|
|
|
|
it("pins the result to the exact stored 2-dp value (not just close)", () => {
|
|
// The DB column is Decimal(_, 2). vatFromGross must already return a value
|
|
// with no third-decimal float drift. Strict equality + a 2-dp self-check,
|
|
// NOT toBeCloseTo, so a 3-dp regression (e.g. 210.0000001) would fail.
|
|
const v = vatFromGross(1210, 21);
|
|
expect(v).toBe(210); // exact, not ~210
|
|
expect(v).toBe(Math.round(v * 100) / 100); // genuinely 2-dp clean
|
|
// The real-world figure too: pinned exactly, replacing the toBeCloseTo above.
|
|
expect(vatFromGross(22542.91, 21)).toBe(3912.41);
|
|
});
|
|
});
|