The year total (tb / tb_ex_year_end) aggregates posted AND reversed entries, so a same-year storno nets to 0. The monthly section, both the get_kpi_report_aggregates RPC and the dimension-filtered JS fallback in lib/reports/monthly-breakdown.ts, was posted-only: it dropped the reversed original but kept the storno (itself posted). 10 000 kr on 3041 in March, reversed in April, gave March 0 kr, April -10 000 kr, year 0 kr, and PR #2198 made the per-month figures visible enough to add up. Migration 20260903160000 replaces the RPC with the monthly join on tb_ex_year_end's entry set verbatim (no extra status predicate); the JS fallback filters status in ('posted','reversed') the same way. The pg-real pin ("in tb, not in monthly") is flipped and a storno case asserts sum(months) = net result. Everything else in the function is byte-identical to 20260730090000. Verified: pg-real suite against a rebuilt local supabase/postgres with every migration applied (9 tests), unit suite, lint. Closes #2201 Claude-Session: https://claude.ai/code/session_01VnConrmMCxJRQ5kfiPPWyy Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5.1
parent
3918ff6620
commit
d69de86b71
@@ -93,10 +93,16 @@ export function assembleMonthlyBreakdown(
|
||||
/**
|
||||
* Generate monthly income vs expenses breakdown for a fiscal period.
|
||||
*
|
||||
* Groups posted journal entry lines by month and account class:
|
||||
* Groups posted AND reversed journal entry lines by month and account class:
|
||||
* - Class 3 (30xx) = revenue (credit side)
|
||||
* - Class 4-7 (40xx-79xx) = expenses (debit side)
|
||||
*
|
||||
* Reversed originals are included on purpose (issue #2201): the year total
|
||||
* (tb_ex_year_end / the income statement) counts posted + reversed, so a
|
||||
* same-year storno nets to 0 there. Dropping the reversed original here but
|
||||
* keeping the storno (itself posted) made the months stop summing to
|
||||
* Nettoresultat. The same entry set on both paths keeps sum(months) = year.
|
||||
*
|
||||
* Year-end entries are excluded, including the storno/correction chain of a
|
||||
* REVERSED year-end entry (an undone bokslut). Without that the resultatavslut,
|
||||
* which posts the mirror image of every P&L account, showed the whole year's
|
||||
@@ -145,8 +151,8 @@ export async function generateMonthlyBreakdown(
|
||||
)
|
||||
).map((r) => r.id)
|
||||
|
||||
// Get all posted journal entry lines for this period with their entry dates,
|
||||
// via the two-step entry-lines fetch (see lib/bookkeeping/entry-lines.ts).
|
||||
// Get all posted and reversed journal entry lines for this period with their
|
||||
// entry dates, via the two-step entry-lines fetch (lib/bookkeeping/entry-lines.ts).
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let lines: any[]
|
||||
try {
|
||||
@@ -158,7 +164,8 @@ export async function generateMonthlyBreakdown(
|
||||
let query = q
|
||||
.eq('fiscal_period_id', fiscalPeriodId)
|
||||
.eq('company_id', companyId)
|
||||
.eq('status', 'posted')
|
||||
// posted + reversed: the same set as tb_ex_ye_entries in the RPC.
|
||||
.in('status', ['posted', 'reversed'])
|
||||
.neq('source_type', 'year_end')
|
||||
if (reversedYearEndIds.length > 0) {
|
||||
const idList = `(${reversedYearEndIds.join(',')})`
|
||||
|
||||
Reference in New Issue
Block a user