feat(skatteverket): expose filed VAT declarations and decisions via the v1 API (#1773)
* feat(skatteverket): expose filed VAT declarations and decisions via the v1 API Add GET /api/v1/companies/:companyId/skatteverket/vat-declarations, returning a period's momsdeklaration as Skatteverket has it on file: the submitted declaration (SKV /inlamnat) and Skatteverket's beslut (SKV /beslutat), either individually via ?state= or both. - Auth: compliance:read scope; member-visibility read model per #1673 (resolveReadAuth: caller's token, any member's active token, or system credentials with a verified ombud grant). - Architecture: core reaches the Skatteverket extension through the registry-resolved services channel (contract in lib/skatteverket/declaration-status.ts), so core never imports from @/extensions/. - New structured error SKATTEVERKET_API_ERROR (502) for upstream SKV failures; 404 from SKV maps to submitted/decided = null with HTTP 200. - 19 new tests (route: auth, validation, extension-disabled, happy path; extension service: auth resolution, state filtering, SKV error mapping). Fixes #1663 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skatteverket): address review findings on the vat-declarations read API Consolidated fixes for PR #1773 review round: - apiskill sync (core-build Checks): map the new skatteverket endpoint group into the periods.md reference and regenerate skills/accounted-api (124 -> 125 operations). - CodeRabbit: parse the SKV 2xx body before writing the audit row, so an unreadable body is audited as skv_error and returns the structured SKATTEVERKET_API_ERROR 502 instead of escaping as an internal 500; regression test added. - Compliance swarm (ISO A.8.12 / SOC2 CC6.1): stop forwarding the raw upstream SKV response body to API consumers; the caller now gets the status code and a generic Swedish message, the body is logged server-side only. - Compliance swarm (GDPR Art.30): add the moms.declaration_status_read processing activity to .compliance/ropa.yaml (live read, no payload persisted, audit-log metadata only). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
0de766c6a4
commit
60920ec794
@@ -8,7 +8,7 @@ description: >-
|
||||
transactions and reconciliation, payroll (lön), VAT/moms and financial
|
||||
reports, SIE import/export, documents, webhooks. Covers auth with
|
||||
gnubok_sk_ API keys, conventions (dry-run, idempotency, cursor
|
||||
pagination, scopes), and all 124 endpoints.
|
||||
pagination, scopes), and all 125 endpoints.
|
||||
---
|
||||
|
||||
<!-- GENERATED FILE, do not edit. Source: lib/api/v1 registry + scripts/api-skill/overlays. Regenerate with `npm run apiskill:generate`. -->
|
||||
@@ -140,7 +140,7 @@ call can undo it, e.g. invoice credit).
|
||||
|
||||
## Endpoint index
|
||||
|
||||
API version `2026-05-12`, 124 operations. Paths are shown without
|
||||
API version `2026-05-12`, 125 operations. Paths are shown without
|
||||
their `/api/v1` prefix (full base URL: `https://app.gnubok.se/api/v1`).
|
||||
|
||||
### Core (4)
|
||||
@@ -169,7 +169,7 @@ POST /companies/{companyId}/journal-entries/batch-create : Create up to 50 draft
|
||||
POST /companies/{companyId}/voucher-gap-explanations : Document a gap in the verifikationsserie (BFL 5 kap 6-7 §§) [scope:bookkeeping:write risk:low idempotent dry-run]
|
||||
```
|
||||
|
||||
### Periods and registers (12)
|
||||
### Periods and registers (13)
|
||||
|
||||
Full detail: [references/periods.md](references/periods.md)
|
||||
|
||||
@@ -186,6 +186,7 @@ POST /companies/{companyId}/fiscal-periods/{id}/currency-revaluation : Run FX re
|
||||
POST /companies/{companyId}/fiscal-periods/{id}/lock : Lock a fiscal period (no new entries can be posted into it) [scope:bookkeeping:write risk:high idempotent reversible]
|
||||
POST /companies/{companyId}/fiscal-periods/{id}/opening-balances : Generate opening-balance verifikation for the next fiscal period [scope:bookkeeping:write risk:high idempotent reversible]
|
||||
POST /companies/{companyId}/fiscal-periods/{id}/year-end : Execute year-end closing (currency revaluation + closing entry) [scope:bookkeeping:write risk:high idempotent]
|
||||
GET /companies/{companyId}/skatteverket/vat-declarations : Read a filed momsdeklaration (submitted and/or decided) from Skatteverket [scope:compliance:read risk:low idempotent]
|
||||
```
|
||||
|
||||
### Invoices (AR) (10)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Periods and registers endpoints
|
||||
|
||||
Fiscal periods and their lock/close/year-end lifecycle (async operations), the BAS chart of accounts, cost-center/project dimensions, and the compliance pre-flight check.
|
||||
Fiscal periods and their lock/close/year-end lifecycle (async operations), the BAS chart of accounts, cost-center/project dimensions, the compliance pre-flight check, and reading filed VAT declarations (and beslut) from Skatteverket.
|
||||
|
||||
Conventions (auth, envelope, pagination, dry-run, idempotency, standard errors)
|
||||
are in SKILL.md and are not repeated per endpoint.
|
||||
@@ -500,3 +500,39 @@ Response `200`:
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/v1/companies/{companyId}/skatteverket/vat-declarations`
|
||||
|
||||
**Read a filed momsdeklaration (submitted and/or decided) from Skatteverket.**
|
||||
`scope:compliance:read · risk:low · idempotent`
|
||||
|
||||
Fetches the momsdeklaration for one period as Skatteverket has it on file: `submitted` is the declaration as filed (SKV /inlamnat), `decided` is Skatteverket's beslut (SKV /beslutat). Either section is null when nothing is on file for the period (or when excluded via ?state=). Query params: period_type (monthly|quarterly|yearly), year, period (1-12 monthly, 1-4 quarterly, 1 yearly), optional state (submitted|decided|both, default both). Requires the company to have an active Skatteverket connection (any member's BankID connection, or a verified ombud grant). Live read against Skatteverket, not a cached copy.
|
||||
|
||||
**Use when:** You want to verify what was actually filed for a VAT period, compare a period against last year's filed declaration, or check whether Skatteverket has decided a period.
|
||||
**Do not use for:** Computing the declaration from the books (use the VAT report), or filing: submission is a separate BankID-signed flow.
|
||||
|
||||
**Pitfalls:**
|
||||
- This is a live Skatteverket read: it fails with SKATTEVERKET_NOT_CONNECTED (401) until someone in the company has connected with BankID under Installningar, and the response reflects SKV's state, not the books.
|
||||
- submitted=null and decided=null with HTTP 200 means "nothing on file for the period": it is not an error.
|
||||
- A submitted declaration can lack a beslut for days: poll decided separately rather than assuming both appear together.
|
||||
- redovisningsperiod is SKV's YYYYMM format (the period's LAST month): quarterly period 1 is 03, not 01.
|
||||
|
||||
| Parameter | In | Type | Required | Notes |
|
||||
|---|---|---|---|---|
|
||||
| `companyId` | path | `string` | yes | |
|
||||
|
||||
Response `200`:
|
||||
```ts
|
||||
{
|
||||
data: { redovisare: string, redovisningsperiod: string, submitted?: unknown, decided?: unknown },
|
||||
meta: {
|
||||
request_id: string,
|
||||
api_version: string,
|
||||
next_cursor?: string,
|
||||
audit?: { voucher_number?: string, voucher_url?: string, audit_trail_url?: string, immutable_at?: string },
|
||||
partial_expansions?: string[]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user