feat(year-end): administrative undo of executed year-end closing + skatteverket scope fixes (#1081)

* fix(skatteverket): request the ska scope for skattekonto v2

The skattekonto v2 API rejects skahmst-only tokens with 403 "The required
scopes are not authorized" (observed in prod 2026-07-20; no company has
synced since 2026-05-10). The requested `skattekonto` scope is silently
dropped from every grant, while `ska` appears in one real May grant, so
request it too: SKV grants the intersection, so this is harmless if wrong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skatteverket): correct the skattekonto scope model around ska

Root cause of the May 10 skattekonto outage, confirmed via git history and
prod token data: the `ska` scope (the interactive skattekonto API's actual
scope, requested since the extension's first commit in March) was removed
by the "remove unused scopes" cleanup in the #431 series. Every token
issued after that hour lacks it and the API answers 403 "The required
scopes are not authorized"; no company has synced since. The May 15 repair
re-added skahmst, which per its tjanstebeskrivning is a different bulk
E-transport service and does not substitute; `skattekonto` is not a real
SKV scope name and is silently dropped from grants.

Follow-up to the ska re-request (cd8f7a30):
- document the confirmed scope model in oauth.ts so ska is never
  "cleaned up" again
- panel missing-scope warning and reconnect-button now gate on ska,
  not skahmst/skattekonto
- scope badge labels: ska takes the saldo & transaktioner label,
  skahmst relabeled as the E-transport file service
- consent-page note covers both terse scope names and says ska is
  required

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(year-end): warn on untaxed profit at verkstall, Swedish readiness messages, always-visible period selector

An aktiebolag could execute year-end with a profit and zero bolagsskatt
booked without any warning (support case: closing moved 592k to 2099
untaxed). The preview now computes bolagsskattMissing (AB + profit + no
89xx account among closed accounts, 8999 excluded) and both the preview
and execute steps render an advisory, bypassable warning.

validateYearEndReadiness messages are now Swedish (the bokslut wizard is
a stays-Swedish surface); the MCP year_end_readiness classifier matches
both the new Swedish strings and the legacy English ones.

The wizard period selector now always renders, keeps a selected-but-
ineligible period selectable, and resets a stale ?period= id from
another company instead of leaving the user stuck on the wrong year.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(year-end): administrative undo of an executed year-end closing

Storno-only reset used when a bokslut was executed prematurely (e.g.
without bolagsskatt) and no arsredovisning exists yet: reverses the next
period's result_appropriation and opening_balance entries, reopens the
period, reverses the closing entry, and detaches closing_entry_id.
Resumable if interrupted midway; attribution per BFL 5 kap 6.

Migration 20260720140000 adds the trigger escape hatch: closing_entry_id
may only change once set when the old closing entry is reversed with a
posted storno chain (status flag alone is forgeable via PostgREST), and
a non-NULL replacement must be a posted year_end entry in the same
period. Covered by a pg-real test.

planResultAppropriation idempotency is now posted-only: a reversed
omforing no longer blocks the re-run from posting a fresh 2099 -> 2098
reclassification (it previously returned null silently, leaving the new
year's equity polluted).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(review): address CodeRabbit, PR-Agent and compliance findings

- undo script: company_id filters on verify queries, period-scope the
  arsredovisning precondition checks, validate service-key format,
  escalate audit_log insert failure to a hard error (BFNAR 2013:2)
- detach migration: company-scope the storno chain EXISTS, replace the
  em dash in the new error message

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(review): address round-2 compliance swarm and Swedish review findings

- undo script: require --confirm-url with --commit so an env swap fails
  loud; retry the audit_log insert 3x and direct the operator to insert
  the behandlingshistorik row manually on final failure (BFNAR 2013:2)
- year-end preview: document why resultAccountSummary is a complete 89xx
  scan; warning text now also names periodiseringsfond and
  overavskrivningar as legitimate zero-tax reasons

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-07-20 16:17:43 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent e2d6c92e3a
commit 4e47335308
19 changed files with 1054 additions and 66 deletions
+3 -2
View File
@@ -2003,7 +2003,8 @@
"connect_waiting": "Waiting for BankID…",
"scope_momsdeklaration": "VAT declaration",
"scope_inkforetag": "Company information",
"scope_skahmst": "Tax account: balance & transactions",
"scope_ska": "Tax account: balance & transactions",
"scope_skahmst": "Tax account: file export via E-transport",
"scope_skattekonto": "Tax account",
"scope_agd": "Employer declaration",
"disconnect_failed": "Disconnect failed",
@@ -2012,7 +2013,7 @@
"loading_status": "Loading status…",
"disabled_message": "The Skatteverket integration is temporarily disabled. Contact support.",
"connect_intro": "Connect to Skatteverket with BankID to submit VAT declarations, employer declarations and fetch the tax account balance. When you connect, we immediately fetch the company's tax account balance and transactions and check for pending employer declaration receipts (kvittenser) at Skatteverket.",
"skahmst_note": "On Skatteverket's consent page one of the permissions appears as <code>skahmst (Rubrik saknas)</code>: that's the scope name for tax account balance and transactions (Skattekonto HuvudMan STatus). Skatteverket has not yet published a Swedish description. It's safe to approve.",
"skahmst_note": "On Skatteverket's consent page some permissions may appear as short names without a description, e.g. <code>ska</code> and <code>skahmst (Rubrik saknas)</code>. They are the tax account scope names: <code>ska</code> is required to fetch balance and transactions. Approve them.",
"connect_approve_all": "Approve all permissions on Skatteverket's consent page when you connect. If a permission is left unchecked we cannot, for example, fetch the tax account.",
"connect_with_bankid": "Connect with BankID",
"expired": "Expired",
+3 -2
View File
@@ -2003,7 +2003,8 @@
"connect_waiting": "Väntar på BankID…",
"scope_momsdeklaration": "Momsdeklaration",
"scope_inkforetag": "Företagsinformation",
"scope_skahmst": "Skattekonto: saldo & transaktioner",
"scope_ska": "Skattekonto: saldo & transaktioner",
"scope_skahmst": "Skattekonto: filuttag via E-transport",
"scope_skattekonto": "Skattekonto",
"scope_agd": "Arbetsgivardeklaration",
"disconnect_failed": "Frånkoppling misslyckades",
@@ -2012,7 +2013,7 @@
"loading_status": "Hämtar status…",
"disabled_message": "Skatteverket-integrationen är tillfälligt avstängd. Kontakta support.",
"connect_intro": "Anslut till Skatteverket med BankID för att skicka momsdeklaration, arbetsgivardeklaration och hämta saldot på skattekontot. När du ansluter hämtar vi direkt företagets skattekontosaldo och transaktioner och kontrollerar om det finns väntande kvittenser för arbetsgivardeklarationer hos Skatteverket.",
"skahmst_note": "På Skatteverkets samtyckessida visas en av behörigheterna som <code>skahmst (Rubrik saknas)</code>: det är scope-namnet för skattekontots saldo och transaktioner (Skattekonto HuvudMan STatus). Skatteverket har inte publicerat en svensk beskrivning för den ännu. Det är ofarligt att godkänna.",
"skahmst_note": "På Skatteverkets samtyckessida kan behörigheter visas med kortnamn utan beskrivning, till exempel <code>ska</code> och <code>skahmst (Rubrik saknas)</code>. Det är scope-namnen för skattekontot: <code>ska</code> krävs för att hämta saldo och transaktioner. Godkänn dem.",
"connect_approve_all": "Godkänn alla behörigheter på Skatteverkets samtyckessida när du ansluter. Lämnas en behörighet obockad kan vi till exempel inte hämta skattekontot.",
"connect_with_bankid": "Anslut med BankID",
"expired": "Utgången",