Arbetsgivardeklarationen is filed for the calendar month the pay went out (kontantprincipen), so a run for August paid on 25 September belongs to redovisningsperiod 202609. The generator, the submit route, the run page and the run header all took run.period_year/period_month instead, and three PATCH paths refused any payment date outside that month, which made lön i efterskott impossible to set up at all. - lib/salary/agi/reporting-period.ts: one dependency-free helper (agiReportingPeriod) derives the period from payment_date, falling back to the run period only when the date is missing. - generate-declaration.ts: XML Redovisningsperiod, the agi_declarations lookup/insert and the sanity warnings key on the payout month. New AGI_PERIOD_CONFLICT (409) refuses to overwrite another live run's declaration for the same payout month; corrections still replace. - submit route, run page (AGI panel, submission hook, tax-payment fetch, XML filename) and RunHeader use the helper; the header says "AGI redovisas för 2026-09 (utbetalningsmånaden)" whenever the two differ. - The in-period payment-date guard is lifted in the dashboard PATCH, lib/salary/update-run.ts (MCP staged tool + pending-ops executor) and the v1 PATCH, plus the RunHeader min/max; its only stated reason was the period-keyed AGI. Generated API skill reference updated. Existing agi_declarations rows keep their stored period: a declaration already filed under the earned month is a correction with Skatteverket, not a re-key. Rule verified against Skatteverket's guidance on redovisningsperiod (kontantprincipen). Closes #2191 Claude-Session: https://claude.ai/code/session_01QPQLwHNEiQfiCNLSMzXMiQ Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
54 lines
2.2 KiB
TypeScript
54 lines
2.2 KiB
TypeScript
/**
|
|
* The AGI redovisningsperiod follows the payout month (kontantprincipen),
|
|
* not the earned month on the run (#2191).
|
|
*/
|
|
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
agiPeriodDiffersFromRunPeriod,
|
|
agiReportingPeriod,
|
|
formatAgiPeriodCompact,
|
|
formatAgiPeriodDashed,
|
|
} from '../agi/reporting-period'
|
|
|
|
describe('agiReportingPeriod', () => {
|
|
it('uses the payout month for lön i efterskott (August work paid 25 September)', () => {
|
|
const run = { period_year: 2026, period_month: 8, payment_date: '2026-09-25' }
|
|
expect(agiReportingPeriod(run)).toEqual({ periodYear: 2026, periodMonth: 9 })
|
|
expect(agiPeriodDiffersFromRunPeriod(run)).toBe(true)
|
|
})
|
|
|
|
it('crosses the year boundary with the payout date (December work paid in January)', () => {
|
|
const run = { period_year: 2026, period_month: 12, payment_date: '2027-01-25' }
|
|
expect(agiReportingPeriod(run)).toEqual({ periodYear: 2027, periodMonth: 1 })
|
|
})
|
|
|
|
it('equals the run period when the pay goes out inside the earned month', () => {
|
|
const run = { period_year: 2026, period_month: 3, payment_date: '2026-03-25' }
|
|
expect(agiReportingPeriod(run)).toEqual({ periodYear: 2026, periodMonth: 3 })
|
|
expect(agiPeriodDiffersFromRunPeriod(run)).toBe(false)
|
|
})
|
|
|
|
it('falls back to the run period when payment_date is missing or malformed', () => {
|
|
expect(agiReportingPeriod({ period_year: 2026, period_month: 6 })).toEqual({
|
|
periodYear: 2026,
|
|
periodMonth: 6,
|
|
})
|
|
expect(agiReportingPeriod({ period_year: 2026, period_month: 6, payment_date: null })).toEqual({
|
|
periodYear: 2026,
|
|
periodMonth: 6,
|
|
})
|
|
expect(
|
|
agiReportingPeriod({ period_year: 2026, period_month: 6, payment_date: '25/06/2026' }),
|
|
).toEqual({ periodYear: 2026, periodMonth: 6 })
|
|
expect(
|
|
agiReportingPeriod({ period_year: 2026, period_month: 6, payment_date: '2026-13-01' }),
|
|
).toEqual({ periodYear: 2026, periodMonth: 6 })
|
|
})
|
|
|
|
it('formats the compact and dashed forms with a zero-padded month', () => {
|
|
const period = { periodYear: 2026, periodMonth: 9 }
|
|
expect(formatAgiPeriodCompact(period)).toBe('202609')
|
|
expect(formatAgiPeriodDashed(period)).toBe('2026-09')
|
|
})
|
|
})
|