diff --git a/DECISIONS.md b/DECISIONS.md index 6f20ca15..b426d744 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -365,4 +365,7 @@ One line per decision: `[YYYY-MM-DD] : `. Appended by agents and [2026-07-24] Onboarding journey migration COMPLETE with PR #1150: wizard deleted, /companies/new on journey mode='add', BankID picker = searchable list, flag conditional removed (env var cleaned from Vercel post-merge). Bot-review triage: compliance findings on getUser()/redirect()/ensure_user_team skipped as App Router misreadings or pre-existing patterns; fixed the real ones (stale select_company keys, unused hasExistingCompanies plumbing). [2026-07-24] Invite recovery on onboarding surfaces = cookie retry + hint, NOT accept-by-email: a BankID signup's email is confirmed via a client-delivered magiclink (no mailbox proof), so auto-joining on email match would let anyone who registers the invitee's address claim the membership. The cookie/token path keeps mailbox possession required; cookie-less invitees get pointed back to the mailed link, with no company name leaked. [2026-07-24] Share-capital pair rule validated in the PUT route, not UpdateSettingsSchema: the all-or-nothing check needs the stored row (a partial update may send only one key), which Zod cannot see; the route already owns the other cross-field effective-value checks. +[2026-07-24] Declined compliance-bot ask to restore internal check codes (RC_BASIS_MISSING et al) in VAT check rows (#1161): the codes are local pre-flight rule IDs, not statutory references; the Swedish messages already cite the rutor and SKV felkod (FK004), and the founder explicitly flagged the raw codes as visual noise. +[2026-07-24] Declined compliance-bot ask to re-box the bolagsskattMissing warning (#1161): text-attn IS the locked house attention idiom (one ochre sentence, banners forbidden by design.md); PreviewStep keeps the inline action to the dispositions step, so salience + remediation path both remain. [2026-07-24] VAT RC checks proportional (0.5% + 1 kr tolerance) + latched stepper landing: the binary present/absent RC_BASIS_MISSING check cleared after one korrigering and hid a 38-voucher worklist behind "klart"; tolerance absorbs per-voucher basis rounding (moms/sats vs invoiced amount) without hiding a missing voucher; landing step latches once per period so a mid-work refetch cannot navigate the user off Kontrollera. +[2026-07-25] Removed invented 6-month minimum for first räkenskapsår: BFL 3 kap 3 § sets no floor (Bolagsverket: "hur kort som helst", max 18 months); the check only existed for isFirstPeriod, exactly the case the law exempts, and blocked a customer shortening an autumn-registered first year to Dec 31. diff --git a/components/bookkeeping/FiscalPeriodDateFields.tsx b/components/bookkeeping/FiscalPeriodDateFields.tsx index c7ad613f..0d28fb4a 100644 --- a/components/bookkeeping/FiscalPeriodDateFields.tsx +++ b/components/bookkeeping/FiscalPeriodDateFields.tsx @@ -34,7 +34,6 @@ function toSwedishError(msg: string): string { if (msg.includes('1st of a month')) return 'Startdatum måste vara den första i månaden.' if (msg.includes('last day of a month')) return 'Slutdatum måste vara den sista i månaden.' if (msg.includes('exceeds maximum 18 months')) return 'Räkenskapsåret får vara högst 18 månader (BFL 3 kap.).' - if (msg.includes('at least 6 months')) return 'Första räkenskapsåret måste vara minst 6 månader (BFL 3 kap.).' return msg } diff --git a/lib/bookkeeping/__tests__/validate-period-duration.test.ts b/lib/bookkeeping/__tests__/validate-period-duration.test.ts index bf4193a8..c3c34956 100644 --- a/lib/bookkeeping/__tests__/validate-period-duration.test.ts +++ b/lib/bookkeeping/__tests__/validate-period-duration.test.ts @@ -84,16 +84,16 @@ describe('validatePeriodDuration', () => { expect(result).toContain('18 months') }) - it('enforces 6-month minimum for first period (2 months)', () => { - expect(validatePeriodDuration('2026-03-25', '2026-05-31', { isFirstPeriod: true })).toBe( - 'First fiscal period must be at least 6 months (BFL 3 kap.)' - ) + // BFL 3 kap 3 § sets NO minimum for a first räkenskapsår: it may be + // "hur kort som helst" (Bolagsverket). A 6-month floor here blocked an + // autumn-registered AB from shortening its first year to Dec 31 for an + // early årsredovisning (real customer case 2026-07-25). + it('allows a short first period (3 months, autumn registration to Dec 31)', () => { + expect(validatePeriodDuration('2025-10-01', '2025-12-31', { isFirstPeriod: true })).toBeNull() }) - it('enforces 6-month minimum for first period (5 months)', () => { - expect(validatePeriodDuration('2026-08-01', '2026-12-31', { isFirstPeriod: true })).toBe( - 'First fiscal period must be at least 6 months (BFL 3 kap.)' - ) + it('allows a very short first period (2 months, mid-month start)', () => { + expect(validatePeriodDuration('2026-03-25', '2026-05-31', { isFirstPeriod: true })).toBeNull() }) it('allows exactly 6 months for first period', () => { diff --git a/lib/bookkeeping/validate-period-duration.ts b/lib/bookkeeping/validate-period-duration.ts index a8ad8656..7ac0da3a 100644 --- a/lib/bookkeeping/validate-period-duration.ts +++ b/lib/bookkeeping/validate-period-duration.ts @@ -1,7 +1,10 @@ /** * Validates fiscal period duration per BFL 3 kap. * Maximum 18 months for any fiscal period (first year may be extended). - * Normal ongoing periods are 12 months. + * Normal ongoing periods are 12 months. There is NO minimum length: BFL + * 3 kap 3 § expressly allows a räkenskapsår shorter than 12 months when + * bokföringsskyldigheten begins or the year is re-laid, with no floor + * (Bolagsverket: the first year may be "hur kort som helst"). */ /** @@ -56,16 +59,13 @@ export function validatePeriodDuration(start: string, end: string, options?: Val return 'Period end must be the last day of a month' } - // Max 18 months per BFL 3 kap. + // Max 18 months per BFL 3 kap. No minimum: a first (or re-laid) year may + // be arbitrarily short, e.g. an autumn-registered AB shortening its first + // year to end at Dec 31 for an early årsredovisning. const months = monthsBetween(start, end) if (months > 18) { return `Period duration ${months} months exceeds maximum 18 months (BFL 3 kap.)` } - // First fiscal period must be at least 6 months per BFL 3 kap. - if (options?.isFirstPeriod && months < 6) { - return `First fiscal period must be at least 6 months (BFL 3 kap.)` - } - return null } diff --git a/lib/company/__tests__/compute-fiscal-period.test.ts b/lib/company/__tests__/compute-fiscal-period.test.ts index 56a4c088..f453f5ae 100644 --- a/lib/company/__tests__/compute-fiscal-period.test.ts +++ b/lib/company/__tests__/compute-fiscal-period.test.ts @@ -89,14 +89,19 @@ describe('computeFiscalPeriod', () => { expect(result.periodName).toBe('') }) - it('rejects a first year shorter than 6 months', () => { + // BFL 3 kap 3 § allows a first räkenskapsår of any length up to 18 months: + // an autumn-registered AB may end its first year at Dec 31 (Bolagsverket + // offers this explicitly on the registration certificate). + it('accepts a first year shorter than 6 months', () => { const result = computeFiscalPeriod({ entity_type: 'aktiebolag', is_first_fiscal_year: true, first_year_start: '2026-10-01', first_year_end: '2026-12-31', }) - expect(result.error).toContain('at least 6 months') + expect(result.error).toBeNull() + expect(result.startStr).toBe('2026-10-01') + expect(result.endStr).toBe('2026-12-31') }) it('rejects an end date that is not the last day of a month', () => {