fix(received-invoices): treat amount as gross (VAT-inclusive), back-calc VAT
The amount on a received invoice is the GROSS total the user pays (VAT included), and VAT is the portion contained within it — not a net base with VAT added on top. Switch the formula from amount*rate/100 to the VAT-inclusive amount*rate/(100+rate) via a shared, tested vatFromGross() helper used by both the create (manual upload + AI import share this endpoint) and edit paths. Rate 0 → no VAT. - AI extraction now asks Claude for the total INCLUDING VAT (was "základ bez DPH"), reinforced in the structured-output schema description. - Label the amount field "Částka s DPH" in the AI review card and the manual upload/edit forms so the gross convention is explicit. - Add unit tests with the user-confirmed figures (22 542,91 @ 21% → 3 912,41, base 18 630,50) plus other rates and the no-VAT case. Existing rows keep their stored (gross) amounts; their vat_amount recomputes on next edit. A one-shot backfill can correct historical vat_amount if wanted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
26
src/__tests__/received-invoices-vat.test.ts
Normal file
26
src/__tests__/received-invoices-vat.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user