* feat(arcim-migration): Briox provider with SIE-over-API import - Briox auth via account ID + application token (no app-level credentials); both tokens rotate on refresh and are persisted - New sie-fetcher pulls the general ledger as SIE through the provider API for Fortnox, Briox and Bjorn Lunden - Wizard stops on a failed SIE import and surfaces the real errors instead of proceeding to the misleading migrate-guard message - PROVIDER_SIE_ONLY_FORTNOX renamed to PROVIDER_SIE_NOT_SUPPORTED; new PROVIDER_TOKEN_INVALID for rejected provider credentials Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bookkeeping): per-line accruals (periodisering) on invoices and supplier invoices Defer revenue/costs per invoice line to 29xx/17xx interim accounts with automatic monthly dissolution (nightly cron + catch-up at registration), schedule cancellation on credit, year-end auto-detect exclusion for already-scheduled invoices, invoice-inbox service-period extraction for prefill, and an MCP tool to list schedules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bokslut): iXBRL arsredovisning generation and Bolagsverket digital filing Generate the annual report as iXBRL from a generated taxonomy registry (K2 element lists, taxonomy:generate/check scripts + CI guard), expose it via the fiscal-period API, and add the bolagsverket extension for digital submission to eget utrymme with webhook-driven status tracking (submissions table + pg tests, lifecycle events, year-end wizard UI). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(mcp): raise origin-guard test timeout to 20s The dynamic import pulls in the full server module; the parse alone flirts with the 5s default under full-suite parallel load. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add new scripts and documentation for K2 AB taxonomy generation and validation - Introduced `generate-taxonomy-registry.ts` to automate the generation of the iXBRL taxonomy concept registry from official element lists and tuple models. - Added `validate-ixbrl.mjs` for validating generated iXBRL reports against the official taxonomy package using Arelle. - Included new documentation files: - `k2-ab-arsredovisning-elementlista-2024-09-12_rev20250312_sv.xlsx` - `tuple-innehallsmodell-arsredovisning-k2-2024-09-12.xlsx` - `taxonomi-paket-2024-09-12_rev20250312.zip` * Add tests for bookkeeping accruals dissolution and supplier invoices - Implement tests for the POST /api/bookkeeping/accruals/[id]/dissolve route, covering success and error scenarios. - Add tests for the DELETE /api/supplier-invoices/[id] route, including authentication checks and validation of invoice deletion conditions. - Introduce tests for the Arcim migration provider client, ensuring token handling and error classification. - Create tests for the Bolagsverket extension, validating submission role enforcement and environment settings. - Add Zod schemas for Bolagsverket response payloads to ensure proper validation. - Implement tests for MCP server's list accrual schedules, confirming registration and scope mapping. - Add consistency tests for IXBRL document generation, ensuring duplicate facts and XML escaping are handled correctly. - Introduce typed domain errors for accrual schedules to improve error handling in the service. - Add tests for resolving consent with Briox token refresh concurrency, ensuring proper token management and error handling. * fix(tests): update payload size guard comments to reflect recent changes in tool descriptions and ceiling adjustments * fix(gitattributes): mark generated JSON files in bokslut taxonomy as linguist-generated * feat(migrations): add backfill for invoices.journal_entry_id and fallback for next_voucher_number user_id * feat(bokslut): enhance compliance and financial processing features with new submission details and security measures --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
235 lines
8.9 KiB
TypeScript
235 lines
8.9 KiB
TypeScript
/**
|
||
* Shared deterministic fixture for iXBRL generator tests: a small AB with a
|
||
* balanced BR (380 000 kr) and an RR netting to 120 000 kr, mapped through
|
||
* the real k2-mapper so concept amounts stay consistent with the mapping
|
||
* rules under test.
|
||
*
|
||
* Each year is a realistic post-bokslut TrialBalancePair:
|
||
* - `full` — the booked state AFTER the year-end closing entry: every
|
||
* class 3–8 account is zeroed (equal debit/credit churn) and 2099
|
||
* carries the year's result.
|
||
* - `preClosing` — the same year WITHOUT the closing entry
|
||
* (excludeYearEndClosing): RR accounts still open, 2099 only carries
|
||
* the prior-year churn from the resultatdisposition entry.
|
||
*/
|
||
|
||
import { mapTrialBalancesToK2, type TrialBalancePair, type TrialBalanceRowLike } from '../k2-mapper'
|
||
import type { IxbrlArsredovisningInput } from '../types'
|
||
|
||
const row = (
|
||
account: string,
|
||
name: string,
|
||
debit: number,
|
||
credit: number,
|
||
): TrialBalanceRowLike => ({
|
||
account_number: account,
|
||
account_name: name,
|
||
closing_debit: debit,
|
||
closing_credit: credit,
|
||
})
|
||
|
||
/** Current year WITHOUT the closing entry — RR accounts open. 2099 nets to 0
|
||
* (prior-year result IB balanced away by the disposition entry). */
|
||
const CURRENT_PRE_CLOSING: TrialBalanceRowLike[] = [
|
||
row('1220', 'Inventarier', 80_000, 0),
|
||
row('1229', 'Ack avskrivningar', 0, 20_000),
|
||
row('1510', 'Kundfordringar', 50_000, 0),
|
||
row('1930', 'Bank', 270_000, 0),
|
||
row('2081', 'Aktiekapital', 0, 25_000),
|
||
row('2091', 'Balanserad vinst', 0, 100_000),
|
||
row('2099', 'Årets resultat', 40_000, 40_000),
|
||
row('2110', 'Periodiseringsfond', 0, 40_000),
|
||
row('2440', 'Leverantörsskulder', 0, 30_000),
|
||
row('2510', 'Skatteskulder', 0, 35_000),
|
||
row('2610', 'Utgående moms', 0, 20_000),
|
||
row('2941', 'Upplupna sociala avgifter', 0, 10_000),
|
||
row('3010', 'Försäljning', 0, 1_000_000),
|
||
row('4010', 'Inköp', 200_000, 0),
|
||
row('5010', 'Lokalhyra', 100_000, 0),
|
||
row('7010', 'Löner', 400_000, 0),
|
||
row('7510', 'Arbetsgivaravgifter', 125_660, 0),
|
||
row('7832', 'Avskrivningar', 20_000, 0),
|
||
row('8310', 'Ränteintäkter', 0, 1_000),
|
||
row('8410', 'Räntekostnader', 4_000, 0),
|
||
row('8811', 'Avsättning periodiseringsfond', 10_000, 0),
|
||
row('8910', 'Skatt', 21_340, 0),
|
||
]
|
||
|
||
/** Current year WITH the closing entry — class 3–8 zeroed, 2099 = 120 000. */
|
||
const CURRENT_FULL: TrialBalanceRowLike[] = [
|
||
row('1220', 'Inventarier', 80_000, 0),
|
||
row('1229', 'Ack avskrivningar', 0, 20_000),
|
||
row('1510', 'Kundfordringar', 50_000, 0),
|
||
row('1930', 'Bank', 270_000, 0),
|
||
row('2081', 'Aktiekapital', 0, 25_000),
|
||
row('2091', 'Balanserad vinst', 0, 100_000),
|
||
row('2099', 'Årets resultat', 40_000, 160_000),
|
||
row('2110', 'Periodiseringsfond', 0, 40_000),
|
||
row('2440', 'Leverantörsskulder', 0, 30_000),
|
||
row('2510', 'Skatteskulder', 0, 35_000),
|
||
row('2610', 'Utgående moms', 0, 20_000),
|
||
row('2941', 'Upplupna sociala avgifter', 0, 10_000),
|
||
row('3010', 'Försäljning', 1_000_000, 1_000_000),
|
||
row('4010', 'Inköp', 200_000, 200_000),
|
||
row('5010', 'Lokalhyra', 100_000, 100_000),
|
||
row('7010', 'Löner', 400_000, 400_000),
|
||
row('7510', 'Arbetsgivaravgifter', 125_660, 125_660),
|
||
row('7832', 'Avskrivningar', 20_000, 20_000),
|
||
row('8310', 'Ränteintäkter', 1_000, 1_000),
|
||
row('8410', 'Räntekostnader', 4_000, 4_000),
|
||
row('8811', 'Avsättning periodiseringsfond', 10_000, 10_000),
|
||
row('8910', 'Skatt', 21_340, 21_340),
|
||
]
|
||
|
||
const PREVIOUS_PRE_CLOSING: TrialBalanceRowLike[] = [
|
||
row('1220', 'Inventarier', 80_000, 0),
|
||
row('1229', 'Ack avskrivningar', 0, 12_000),
|
||
row('1930', 'Bank', 185_000, 0),
|
||
row('2081', 'Aktiekapital', 0, 25_000),
|
||
row('2091', 'Balanserad vinst', 0, 60_000),
|
||
row('2110', 'Periodiseringsfond', 0, 30_000),
|
||
row('2440', 'Leverantörsskulder', 0, 25_000),
|
||
row('2510', 'Skatteskulder', 0, 15_000),
|
||
row('2610', 'Utgående moms', 0, 8_000),
|
||
row('2941', 'Upplupna sociala avgifter', 0, 50_000),
|
||
row('3010', 'Försäljning', 0, 500_000),
|
||
row('4010', 'Inköp', 200_000, 0),
|
||
row('5010', 'Lokalhyra', 80_000, 0),
|
||
row('7010', 'Löner', 150_000, 0),
|
||
row('7832', 'Avskrivningar', 8_000, 0),
|
||
row('8410', 'Räntekostnader', 2_000, 0),
|
||
row('8910', 'Skatt', 20_000, 0),
|
||
]
|
||
|
||
const PREVIOUS_FULL: TrialBalanceRowLike[] = [
|
||
row('1220', 'Inventarier', 80_000, 0),
|
||
row('1229', 'Ack avskrivningar', 0, 12_000),
|
||
row('1930', 'Bank', 185_000, 0),
|
||
row('2081', 'Aktiekapital', 0, 25_000),
|
||
row('2091', 'Balanserad vinst', 0, 60_000),
|
||
row('2099', 'Årets resultat', 0, 40_000),
|
||
row('2110', 'Periodiseringsfond', 0, 30_000),
|
||
row('2440', 'Leverantörsskulder', 0, 25_000),
|
||
row('2510', 'Skatteskulder', 0, 15_000),
|
||
row('2610', 'Utgående moms', 0, 8_000),
|
||
row('2941', 'Upplupna sociala avgifter', 0, 50_000),
|
||
row('3010', 'Försäljning', 500_000, 500_000),
|
||
row('4010', 'Inköp', 200_000, 200_000),
|
||
row('5010', 'Lokalhyra', 80_000, 80_000),
|
||
row('7010', 'Löner', 150_000, 150_000),
|
||
row('7832', 'Avskrivningar', 8_000, 8_000),
|
||
row('8410', 'Räntekostnader', 2_000, 2_000),
|
||
row('8910', 'Skatt', 20_000, 20_000),
|
||
]
|
||
|
||
export const CURRENT: TrialBalancePair = {
|
||
full: CURRENT_FULL,
|
||
preClosing: CURRENT_PRE_CLOSING,
|
||
}
|
||
|
||
export const PREVIOUS: TrialBalancePair = {
|
||
full: PREVIOUS_FULL,
|
||
preClosing: PREVIOUS_PRE_CLOSING,
|
||
}
|
||
|
||
export function makeInput(): IxbrlArsredovisningInput {
|
||
const mapping = mapTrialBalancesToK2(CURRENT, PREVIOUS)
|
||
return {
|
||
company: { name: 'Testbolaget AB', orgNumber: '556999-9999', city: 'Sundsvall' },
|
||
period: { start: '2025-01-01', end: '2025-12-31' },
|
||
previousPeriod: { start: '2024-01-01', end: '2024-12-31' },
|
||
isFirstFiscalYear: false,
|
||
rr: mapping.rr,
|
||
br: mapping.br,
|
||
totals: mapping.totals,
|
||
forvaltningsberattelse: {
|
||
allmantOmVerksamheten:
|
||
'Bolaget bedriver konsultverksamhet inom IT.\n\nBolaget har sitt säte i Sundsvall.',
|
||
vasentligaHandelser: 'Inga väsentliga händelser har inträffat under räkenskapsåret.',
|
||
// Rows 0/1 mirror the mapper outputs (duplicate facts with the RR must
|
||
// be value-identical, TA §2.7.3) — same override build-input applies.
|
||
flerarsoversikt: [
|
||
{
|
||
year: '2025',
|
||
nettoomsattning: mapping.rr['Nettoomsattning'].current,
|
||
resultatEfterFinansiellaPoster: mapping.totals.resultatEfterFinansiellaPoster.current,
|
||
soliditetPct: 64.5,
|
||
},
|
||
{
|
||
year: '2024',
|
||
nettoomsattning: mapping.rr['Nettoomsattning'].previous ?? 0,
|
||
resultatEfterFinansiellaPoster:
|
||
mapping.totals.resultatEfterFinansiellaPoster.previous ?? 0,
|
||
soliditetPct: 49.4,
|
||
},
|
||
{
|
||
year: '2023',
|
||
nettoomsattning: 300_000,
|
||
resultatEfterFinansiellaPoster: 25_000,
|
||
soliditetPct: 41.0,
|
||
},
|
||
],
|
||
flerarsPerioder: [
|
||
{ start: '2025-01-01', end: '2025-12-31' },
|
||
{ start: '2024-01-01', end: '2024-12-31' },
|
||
{ start: '2023-01-01', end: '2023-12-31' },
|
||
],
|
||
egetKapital: {
|
||
aktiekapital: { ib: 25_000, ub: 25_000 },
|
||
balanseratResultat: { ib: 60_000, ub: 100_000 },
|
||
aretsResultat: { ib: 40_000, ub: 120_000 },
|
||
totalt: { ib: 125_000, ub: 245_000 },
|
||
ovrigaPoster: { ib: 0, ub: 0 },
|
||
balanserasINyRakning: 40_000,
|
||
utdelning: 0,
|
||
forandringAktiekapital: 0,
|
||
ovrigForandringBalanserat: 0,
|
||
aretsResultatRorelse: 120_000,
|
||
},
|
||
resultatdisposition: {
|
||
balanseratResultat: 100_000,
|
||
overkursfond: 0,
|
||
aretsResultat: 120_000,
|
||
summa: 220_000,
|
||
utdelning: 0,
|
||
balanserasINyRakning: 220_000,
|
||
kommentar: 'Styrelsen föreslår att årets resultat balanseras i ny räkning.',
|
||
},
|
||
},
|
||
noter: [
|
||
{
|
||
number: 1,
|
||
title: 'Redovisnings- och värderingsprinciper',
|
||
body: 'Årsredovisningen är upprättad i enlighet med Årsredovisningslagen och Bokföringsnämndens allmänna råd BFNAR 2016:10 Årsredovisning i mindre företag (K2).',
|
||
},
|
||
{ number: 2, title: 'Medelantal anställda', body: 'Medelantalet anställda har uppgått till 2.' },
|
||
{ number: 3, title: 'Långfristiga skulder', body: 'Inga skulder förfaller senare än fem år efter balansdagen.' },
|
||
],
|
||
medelantalAnstallda: { current: 2, previous: 1 },
|
||
underskrifter: {
|
||
ort: 'Sundsvall',
|
||
dateringsdatum: '2026-02-20',
|
||
signers: [
|
||
{ firstName: 'Karl', lastName: 'Karlsson', role: 'Styrelseledamot', signedDate: '2026-02-20' },
|
||
{
|
||
firstName: 'Karin',
|
||
lastName: 'Olsson',
|
||
role: 'Verkställande direktör',
|
||
signedDate: '2026-02-21',
|
||
},
|
||
],
|
||
harVd: true,
|
||
},
|
||
faststallelseintyg: {
|
||
arsstammaDatum: '2026-03-15',
|
||
signerFirstName: 'Karl',
|
||
signerLastName: 'Karlsson',
|
||
signerRole: 'Styrelseledamot',
|
||
genereratDatum: '2026-02-25',
|
||
},
|
||
programvara: { namn: 'Accounted - Accounted', version: '2026.1' },
|
||
entryPointId: 'k2-ab-risbs-2024-09-12',
|
||
warnings: [],
|
||
}
|
||
}
|