fix(vat): make the ruta drill-down reconcile with the figure it explains (#2016)
* fix(vat): make the ruta drill-down reconcile with the figure it explains get_vat_declaration_totals drops four classes of entry before summing: posted closing entries, source_type 'vat_settlement', the two kontantmetod year-end reversals, and anything shaped like a momsredovisning. The drill-down behind each ruta filtered on company, status and date only. So expanding a ruta listed verifikat that are not in the number it claims to explain, and the panel shows no total that would reveal the mismatch. On production, 322 posted/reversed entries carrying 26xx lines across 214 companies sit in those excluded classes. A momsdeklaration is räkenskapsinformation under BFL 5 kap. and this drill-down is what a consultant uses to substantiate a filed figure, so the two have to agree exactly. The exclusion CTEs are lifted verbatim from the figure rather than re-derived, because any divergence reintroduces exactly this bug. The new pg test asserts the equality for the whole account set at once, so editing one function and not the other fails CI instead of silently misreporting. opening_balance entries are deliberately kept: the figure exempts them from its `shaped` set, which leaves their lines in the totals, so excluding them here would break the equality in the other direction. That has its own test. Verified the test catches the defect by reinstalling the old function body and watching it fail with the real numbers (2611: drill-down 250/240 vs figure 0/200), then restoring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(vat): update the existing drill-down pg test to the new signature get_vat_ruta_source_lines gained p_ruta_accounts / p_net_accounts, and production-error-regressions.pg.test.ts still called the old 9-argument form, so pg-real failed with 42883 "function does not exist". I had grepped app/, lib/ and extensions/ for callers and not tests/. Neither fixture in that paging test is settlement-shaped, so paging behaviour is unchanged; the equality itself is covered by the new reconcile test. Also documents, in the tool-pg reset script, that its blanket grant to `anon` (which PostgREST requires) makes that database invalid for the pg-real suite: ~29 of those files assert least privilege and fail there even on unmodified main. That cost a confusing local run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
Jakob Wennberg
parent
4e1eb3d662
commit
338ac4e913
@@ -0,0 +1,155 @@
|
||||
-- Make the VAT ruta drill-down return exactly the lines the filed figure sums.
|
||||
--
|
||||
-- `get_vat_declaration_totals` (the FIGURE, migration 20260813124510) drops
|
||||
-- four classes of entry before summing: posted closing entries, source_type
|
||||
-- 'vat_settlement', the two kontantmetod year-end reversals, and any entry
|
||||
-- shaped like a momsredovisning (a line on a ruta account AND a line on
|
||||
-- 2650/1650). `get_vat_ruta_source_lines` (the DRILL-DOWN, migration
|
||||
-- 20260721103000) filtered on company, status and date only, so expanding a
|
||||
-- ruta listed verifikat that are not in the number it claims to explain, with
|
||||
-- no total on the panel to reveal the mismatch.
|
||||
--
|
||||
-- Measured on production 2026-08-28: 322 posted/reversed entries carrying 26xx
|
||||
-- lines across 214 companies sit in those excluded classes. A momsdeklaration
|
||||
-- is räkenskapsinformation (BFL 5 kap.), and this drill-down is what a
|
||||
-- consultant uses to substantiate a filed figure, so the two must agree
|
||||
-- exactly.
|
||||
--
|
||||
-- The exclusion CTEs below are lifted VERBATIM from get_vat_declaration_totals
|
||||
-- rather than re-derived. That is deliberate: any divergence between the two
|
||||
-- reintroduces exactly this bug, and a copy that reads identically is easy to
|
||||
-- diff when the figure changes. tests/pg/vat-ruta-drilldown-reconcile.pg.test.ts
|
||||
-- asserts the equality directly, so a future edit to one and not the other
|
||||
-- fails CI rather than silently misreporting.
|
||||
--
|
||||
-- Settlement-shape is detected against journal_entry_lines directly instead of
|
||||
-- through the figure's `vat_lines` CTE. That is equivalent, not a shortcut:
|
||||
-- the figure passes p_ruta_accounts = VAT_ACCOUNTS and p_net_accounts =
|
||||
-- ['2650','1650'], both strict subsets of its p_accounts, so restricting to
|
||||
-- vat_lines first cannot change which entries match. It lets the drill-down
|
||||
-- keep p_accounts meaning "the accounts of the ruta being expanded" without a
|
||||
-- fourth account parameter.
|
||||
--
|
||||
-- opening_balance entries are deliberately NOT excluded. They are excluded
|
||||
-- from the figure's `shaped` set, which keeps their lines IN the totals, so
|
||||
-- dropping them here would break the equality in the other direction.
|
||||
--
|
||||
-- pg-test: tests/pg/vat-ruta-drilldown-reconcile.pg.test.ts
|
||||
|
||||
-- The signature gains p_ruta_accounts / p_net_accounts. DROP first: adding
|
||||
-- parameters with CREATE OR REPLACE registers a second overload, and PostgREST
|
||||
-- then cannot choose between them (documented in 20260421140000).
|
||||
DROP FUNCTION IF EXISTS public.get_vat_ruta_source_lines(
|
||||
uuid, date, date, text[], date, integer, uuid, uuid, integer
|
||||
);
|
||||
|
||||
-- OR REPLACE on the NEW signature, so re-running this file is a no-op rather
|
||||
-- than "function already exists with same argument types". The DROP above is
|
||||
-- what removes the old arity; this cannot reintroduce an overload.
|
||||
CREATE OR REPLACE FUNCTION public.get_vat_ruta_source_lines(
|
||||
p_company_id uuid,
|
||||
p_start date,
|
||||
p_end date,
|
||||
-- Accounts of the ruta being expanded: which lines to RETURN.
|
||||
p_accounts text[],
|
||||
-- Settlement-shape detectors, mirroring the figure's own parameters.
|
||||
p_ruta_accounts text[],
|
||||
p_net_accounts text[],
|
||||
p_cursor_date date DEFAULT NULL,
|
||||
p_cursor_voucher_number integer DEFAULT NULL,
|
||||
p_cursor_entry_id uuid DEFAULT NULL,
|
||||
p_cursor_line_id uuid DEFAULT NULL,
|
||||
p_limit integer DEFAULT 501
|
||||
)
|
||||
RETURNS TABLE (
|
||||
line_id uuid,
|
||||
journal_entry_id uuid,
|
||||
voucher_number integer,
|
||||
voucher_series text,
|
||||
entry_date date,
|
||||
description text,
|
||||
debit_amount numeric,
|
||||
credit_amount numeric
|
||||
)
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
SECURITY INVOKER
|
||||
SET search_path TO 'public'
|
||||
AS $$
|
||||
WITH closing_entries AS (
|
||||
SELECT fp.closing_entry_id AS id
|
||||
FROM public.fiscal_periods fp
|
||||
WHERE fp.company_id = p_company_id
|
||||
AND fp.closing_entry_id IS NOT NULL
|
||||
),
|
||||
scoped_entries AS (
|
||||
SELECT e.id, e.status, e.entry_date, e.source_type, e.description,
|
||||
e.voucher_series, e.voucher_number
|
||||
FROM public.journal_entries e
|
||||
WHERE e.company_id = p_company_id
|
||||
AND e.status IN ('posted', 'reversed')
|
||||
AND e.entry_date >= p_start
|
||||
AND e.entry_date <= p_end
|
||||
AND NOT (
|
||||
e.status = 'posted'
|
||||
AND EXISTS (SELECT 1 FROM closing_entries c WHERE c.id = e.id)
|
||||
)
|
||||
),
|
||||
non_settlement_entries AS (
|
||||
SELECT * FROM scoped_entries
|
||||
WHERE source_type IS DISTINCT FROM 'vat_settlement'
|
||||
AND NOT (
|
||||
source_type = 'year_end'
|
||||
AND description IN (
|
||||
'Vändning kundfordringar bokslut (kontantmetoden)',
|
||||
'Vändning leverantörsskulder bokslut (kontantmetoden)'
|
||||
)
|
||||
)
|
||||
),
|
||||
shaped AS (
|
||||
SELECT e.id
|
||||
FROM non_settlement_entries e
|
||||
WHERE e.source_type IS DISTINCT FROM 'opening_balance'
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.journal_entry_lines l
|
||||
WHERE l.journal_entry_id = e.id
|
||||
AND l.account_number = ANY (p_ruta_accounts)
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.journal_entry_lines l
|
||||
WHERE l.journal_entry_id = e.id
|
||||
AND l.account_number = ANY (p_net_accounts)
|
||||
)
|
||||
)
|
||||
SELECT
|
||||
l.id AS line_id,
|
||||
je.id AS journal_entry_id,
|
||||
je.voucher_number,
|
||||
COALESCE(je.voucher_series, 'A') AS voucher_series,
|
||||
je.entry_date,
|
||||
COALESCE(je.description, '') AS description,
|
||||
l.debit_amount,
|
||||
l.credit_amount
|
||||
FROM non_settlement_entries je
|
||||
JOIN public.journal_entry_lines l ON l.journal_entry_id = je.id
|
||||
WHERE l.account_number = ANY (p_accounts)
|
||||
AND NOT EXISTS (SELECT 1 FROM shaped s WHERE s.id = je.id)
|
||||
AND (
|
||||
p_cursor_date IS NULL
|
||||
OR (
|
||||
je.entry_date,
|
||||
je.voucher_number,
|
||||
je.id,
|
||||
l.id
|
||||
) > (
|
||||
p_cursor_date,
|
||||
p_cursor_voucher_number,
|
||||
COALESCE(p_cursor_entry_id, 'ffffffff-ffff-ffff-ffff-ffffffffffff'::uuid),
|
||||
COALESCE(p_cursor_line_id, 'ffffffff-ffff-ffff-ffff-ffffffffffff'::uuid)
|
||||
)
|
||||
)
|
||||
ORDER BY je.entry_date, je.voucher_number, je.id, l.id
|
||||
LIMIT LEAST(GREATEST(COALESCE(p_limit, 501), 1), 501);
|
||||
$$;
|
||||
|
||||
NOTIFY pgrst, 'reload schema';
|
||||
Reference in New Issue
Block a user