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>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5.1
parent
601e521584
commit
cb39cded81
@@ -249,11 +249,15 @@ describe('commitPendingOperation: update_salary_run', () => {
|
||||
expect(findCall('salary_run_employees', 'update')).toEqual([{ calculation_breakdown: null }])
|
||||
})
|
||||
|
||||
it('rejects a payment_date outside the run period month with 400', async () => {
|
||||
it('commits a payment_date in the month after the period (lön i efterskott, #2191)', async () => {
|
||||
// The AGI redovisningsperiod follows payment_date, so a March run paid
|
||||
// in April is declared for April; no period guard refuses it.
|
||||
const { supabase, enqueue } = createQueuedMockSupabase()
|
||||
enqueue({ data: { id: 'op-1' }, error: null }) // CAS claim
|
||||
enqueue({ data: RUN_ROW }) // draft gate passes; period is 2026-03
|
||||
enqueue({ data: null, error: null }) // finalize (rejected)
|
||||
enqueue({ data: { ...RUN_ROW, payment_date: '2026-04-05' } }) // optimistic-locked update
|
||||
enqueue({ data: null, error: null }) // roster calculation_breakdown clear
|
||||
enqueue({ data: null, error: null }) // finalize
|
||||
|
||||
const op = makePendingOp({
|
||||
operation_type: 'update_salary_run',
|
||||
@@ -261,8 +265,8 @@ describe('commitPendingOperation: update_salary_run', () => {
|
||||
})
|
||||
const result = await commitPendingOperation(supabase as never, 'user-1', 'company-1', op)
|
||||
|
||||
expect(result.status).not.toBe('committed')
|
||||
expect(result.http_status).toBe(400)
|
||||
expect(result.status).toBe('committed')
|
||||
expect(result.data).toMatchObject({ payment_date: '2026-04-05' })
|
||||
})
|
||||
|
||||
it('fails when the calculation-invalidation clear errors (guard is not best-effort)', async () => {
|
||||
|
||||
Reference in New Issue
Block a user