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
@@ -3,6 +3,11 @@
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import {
|
||||
agiPeriodDiffersFromRunPeriod,
|
||||
agiReportingPeriod,
|
||||
formatAgiPeriodDashed,
|
||||
} from '@/lib/salary/agi/reporting-period'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
@@ -163,12 +168,12 @@ export function RunHeader({
|
||||
if (!saved) setPaymentDateDraft(run.payment_date)
|
||||
}
|
||||
|
||||
// The payment date must stay within the run's period month: the AGI is
|
||||
// declared per payment month (kontantprincipen), so the API refuses dates
|
||||
// outside it. min/max keeps the native picker inside the month; typed
|
||||
// values outside it still get the server's 400 toast.
|
||||
const periodMonthPrefix = `${run.period_year}-${String(run.period_month).padStart(2, '0')}`
|
||||
const periodMonthLastDay = new Date(run.period_year, run.period_month, 0).getDate()
|
||||
// The AGI is declared for the PAYOUT month (kontantprincipen), so a run
|
||||
// paid the month after its period (lön i efterskott) files under that
|
||||
// later month. Say so in the meta line whenever the two differ: nothing
|
||||
// else on the page explains why "augusti" is declared in September.
|
||||
const agiPeriod = agiReportingPeriod(run)
|
||||
const agiPeriodDiffers = agiPeriodDiffersFromRunPeriod(run)
|
||||
|
||||
const metaParts: React.ReactNode[] = [
|
||||
<span key="payment" className="inline-flex items-center gap-2">
|
||||
@@ -177,8 +182,6 @@ export function RunHeader({
|
||||
<Input
|
||||
type="date"
|
||||
value={paymentDateDraft}
|
||||
min={`${periodMonthPrefix}-01`}
|
||||
max={`${periodMonthPrefix}-${String(periodMonthLastDay).padStart(2, '0')}`}
|
||||
onChange={(e) => setPaymentDateDraft(e.target.value)}
|
||||
onBlur={commitPaymentDate}
|
||||
onKeyDown={(e) => {
|
||||
@@ -194,6 +197,13 @@ export function RunHeader({
|
||||
</span>,
|
||||
<span key="count">{t('header_employees', { count: employeeCount })}</span>,
|
||||
]
|
||||
if (agiPeriodDiffers) {
|
||||
metaParts.push(
|
||||
<span key="agi-period">
|
||||
{t('agi_period_note', { period: formatAgiPeriodDashed(agiPeriod) })}
|
||||
</span>,
|
||||
)
|
||||
}
|
||||
if (run.is_correction && run.corrects_run_id) {
|
||||
metaParts.push(
|
||||
<Link
|
||||
|
||||
Reference in New Issue
Block a user