Postspesifikasjoner
Postspesifikasjonssystemet er et CSV-drevet konfigurasjonrammeverk som definerer hvordan regnskapsposter er strukturert, kategorisert og beregnet. CSV-filer parses ved kjøretid og transformeres til en sterkt typet Java-objektmodell som brukes gjennom hele årsoppgjørspipelinen.
Oversikt
Systemet fungerer i tre steg:
-
CSV-parsing — spesifikasjonsfiler leses og parses av
PostSpecCsvParser -
Transformasjon — parsede rader transformeres til typede postobjekter av
PostSpecTransformer -
Beregning —
PostsCalculatorevaluerer poster i to faser mot saldobalansen
Spesifikasjoner caches av SpecServiceBean etter SpecKey, slik at hver kombinasjon av regnskapsplikttype, år og inkrement parses maksimalt én gang per applikasjonslivssyklus.
flowchart LR A[CSV-fil] --> B[PostSpecCsvParser] B --> C[PostSpecTransformer] C --> D[PostsSpec] D --> E[PostsCalculator] E --> F[Beregnede poster]
CSV-filformat
Filnavngivning
CSV-filer følger mønsteret:
posts-spec-mapping-{year}-{type}.csv
For eksempel: posts-spec-mapping-2025-AS.csv for aksjeselskap i 2025.
Kolonneoppsett
| Indeks | Kolonne | Beskrivelse |
|---|---|---|
0 |
Type |
Posttypeidentifikator (se Posttyper) |
1 |
SummedByPost |
Overordnet postnummer som aggregerer denne posten |
2 |
Post |
Postnummer, f.eks. |
3 |
Replacement |
Utgått postnummer som denne posten erstatter |
4 |
Negate |
|
5 |
Factor Override |
Egendefinert multiplikator (overstyrer standard kategorifaktor) |
6 |
Definition |
Typespesifikt innhold: kontoområder for |
7 |
Traits |
Kommaseparerte traitkoder (se Traitsystem) |
8 |
Version |
Spesifikasjonsversjon, f.eks. |
9-11 |
Metadata |
Foreslått mapping, endringsbeskrivelser og annen dokumentasjon |
Typeidentifikatorer (kolonne 0)
Følgende verdier er gyldige i Type-kolonnen:
range
|
Post som henter beløp fra et eller flere kontoområder. |
input
|
Tallinput fra bruker. |
input-text
|
Tekstinput fra bruker. |
input-decimal
|
Desimaltallinput fra bruker. |
input-integer
|
Heltallsinput fra bruker. |
input-radio
|
Ja/nei-input (radioknapper). |
choose-account
|
Input der bruker velger en eller flere kontoer innenfor et tillatt område. Med trait |
input-object
|
Strukturert objektinput (JSON). |
from
|
Post som speiler verdien til en annen post. |
sum-post-inferred
|
Sumpost som automatisk bygges fra rader som peker på den via |
sum-post
|
Eksplisitt sumpost med en |
formula
|
Post med en mer avansert beregningsformel. |
composite
|
Aggregat av to eller flere underposter, summert. |
receiver
|
Post som mottar kontoer omdirigert hit fra andre poster (f.eks. ved fortegnsavvik). Har ingen egne kontoområder. |
disabled
|
Raden hoppes over ved parsing. |
pending
|
Raden hoppes over ved parsing (planlagt, ikke aktiv ennå). |
Versjonering
Spesifikasjonsversjoner følger formatet YYYY.N der YYYY er regnskapsåret og N er inkrementet.
Formål
Versjonering sikrer at beregnede data ikke endres når vi slipper nye versjoner av applikasjonen. Et årsregnskap låses til den spesifikasjonsversjonen det ble opprettet med, slik at mappinger og regler forblir stabile gjennom hele levetiden til årsregnskapet.
Versjonering brukes til å:
-
Rette feil i kontomappinger uten å påvirke eksisterende årsregnskap
-
Tilpasse mappinger når regnskapsregler eller skattemeldingsregler endres innenfor et regnskapsår
Når vi slipper en ny versjon for et år, inkrementerer vi N.
Nye årsregnskap opprettes med det nyeste inkrementet for året (SpecVersion.latest(year)).
SpecKey og SpecVersion
En spesifikasjon identifiseres unikt av en SpecKey: tupelen (AccountingObligationType, år, inkrement).
SpecVersion kapsler inn YYYY.N-formatet og tilbyr:
-
initial(year)— standardinkrement for året (brukes som fallback for årsregnskap uten lagret versjon) -
latest(year)— nyeste inkrement for året (brukes ved opprettelse av nye årsregnskap)
Versjonsoppløsning
Når en spesifikasjon lastes, velger systemet det høyeste inkrementet som ikke overstiger den forespurte versjonen. Rader uten versjon i CSV-filen gjelder for alle inkrementer. Versjonerte rader overstyrer uversjonerte rader dersom inkrementet er innenfor rekkevidde.
For årsregnskapsmappingen (annual-statement-group-mapping) brukes GroupKey som identitet: rader med samme GroupKey må stå sammenhengende i CSV-en, sortert stigende på Version.
Støttede versjoner
| Versjon | Merknader |
|---|---|
|
Basisversjon, brukes for eldre regnskapsår |
|
Første 2024-versjon |
|
Midtårsoppdatering |
|
Siste 2024-inkrement før årsavslutningsspesifikasjoner |
|
Rettet utbyttemapping i årsavslutning |
|
Første 2025-versjon |
|
Rettet utbyttemapping i årsavslutning |
|
Splittet 1570 og 2990 i underposter ( |
Omfang og begrensninger
-
Versjonslogikken er implementert for postspesifikasjoner, årsavslutningsspesifikasjoner og årsregnskapsmappingen (annsta).
-
Et årsregnskap kan i prinsippet bruke en spesifikasjonsversjon fra et annet år, men det er ingen garanti for at det fungerer dersom skattemeldingsreglene har endret seg.
-
Versjonsbeskrivelsene i frontend bør forbedres — dette venter på fageksperter.
-
Brukeren kan endre versjon i frontend (med visse begrensninger).
-
På veikartet: Brukere skal kunne opprette egne spesifikasjoner som bygger på en gitt spesifikasjonsversjon, med egne tilpasninger. Høyt prioritert av fageksperter, etterspurt av pro-brukere.
Posttyper
| Type | Klasse | Beskrivelse |
|---|---|---|
Range |
|
Kobler en post til ett eller flere kontoområder i saldobalansen. F.eks. post |
Input |
|
Brukeroppgitt verdi. Undertyper: Amount, Decimal, Integer, Text, Radio, ChooseAccount, DomainObject, Date. Verdier lagres som |
From |
|
Kopierer den beregnede verdien fra en annen post. En valgfri gate ( |
Sum |
|
Aggregerer andre poster med |
Formula |
|
Avansert beregning med flere deler og operatorer ( |
Derived |
|
Beregnet resultat. Ikke en CSV-typeidentifikator — opprettes av kalkulatorpipelinen, ikke deklarert i spec-filer. |
Composite |
|
Aggregerer underordnede poster («underposter») av en hvilken som helst eksisterende type til en sum.
Definerer sine deler i Definition-kolonnen som en kommaseparert liste over postnummer ( |
Receiver |
|
Absorberer kontoer som er omdirigert hit fra andre poster via |
Dummy |
|
Plassholderpost som ikke beregnes. |
Beregningspipeline
PostsCalculator evaluerer poster i to faser:
flowchart TB
subgraph Fase1["Fase 1 — Fra saldobalanse"]
R[RangePostCalculator] --> BP[BasePosts]
I[InputPostCalculators] --> BP
end
subgraph Fase2["Fase 2 — Fra BasePosts"]
BP --> S[SumPostCalculator]
BP --> F[FromPostCalculator]
BP --> FO[FormulaPostCalculator]
BP --> CO[CompositePostCalculator]
end
Fase 1 — Fra saldobalanse
| Kalkulator | Beskrivelse |
|---|---|
|
Skanner saldobalansekontoer, grupperer dem etter kontoområde, summerer saldoer og anvender kontokategorifaktoren |
|
Henter brukerinput fra |
Resultater fra fase 1 samles i BasePosts.
Fase 2 — Fra BasePosts
| Kalkulator | Beskrivelse |
|---|---|
|
Summerer refererte poster fra BasePosts |
|
Kopierer verdier fra BasePosts (med valgfri gate-transformasjon) |
|
Evaluerer komplekse uttrykk med verdier fra BasePosts |
|
Summerer underpost-verdier fra BasePosts, samler kontoer fra alle deler, og oppdager duplikater på tvers av deler |
Spesifikasjonsposter (postnumre som begynner med 0) beregnes alltid til slutt.
|
| Før en post beregnes, sjekker systemet om postens påkrevde traits er oppfylt av årsregnskapets aktive traitsett. Hvis ikke, returnerer posten tom. |
Traitsystem
PostTrait-enumen inneholder 60+ verdier som styrer postoppførsel. Hver trait har en PostBehaviour-klassifisering:
Oppførselskategorier
| Oppførsel | Formål | Eksempler |
|---|---|---|
Display |
Styrer synlighet i brukergrensesnittet |
|
Infer |
Styrer automatisk verdipopulering |
|
Requires |
Betinger beregning på årsregnskapsegenskaper |
|
Overstyringstraits
Traitene AllowsOverride og AllowsOverrideAmount tillater brukeren å manuelt overstyre en beregnet verdi.
Spesielle traits
| Trait | Beskrivelse |
|---|---|
|
Verdien beregnes utenfor den normale pipelinen |
|
Posten er gjenkjent, men ikke ennå implementert |
|
Posten er under utvikling |
|
Strukturell markør — posten er en del av en |
Postregler
PostRule validerer og transformerer eventuelt postbeløp:
| Regel | Oppførsel |
|---|---|
|
Verdi må være >= 0 |
|
Verdi må være <= 0 |
|
Verdi må være >= 0; returnerer absolutt verdi |
|
Verdi må være <= 0; returnerer absolutt verdi |
|
Ingen validering |
|
For grupperte inputposter |
Kontokategorier
AccountCategory kobler kontonummerområder til standard regnskapskategorier med en debet/kredit-faktor:
| Kategori | Kontoområde | Side | Faktor |
|---|---|---|---|
Eiendeler |
1000-1999 |
Debet |
+1 |
Egenkapital |
2000-2099 |
Kredit |
-1 |
Gjeld |
2100-2999 |
Kredit |
-1 |
Inntekt |
3000-3999 |
Kredit |
-1 |
Kostnader |
4000-7999 |
Debet |
+1 |
Finansinntekt |
8000-8099 |
Kredit |
-1 |
Finanskostnad |
8100-8199 |
Debet |
+1 |
Skatt |
8300-8399 |
Debet |
+1 |
Disponering |
8900-8999 |
Debet |
+1 |
Resultat |
9000-9999 |
— |
— |
CompositePost
En CompositePost er en post som aggregerer underposter av en hvilken som helst eksisterende type til en sum.
Den gjør det mulig å bygge kompleks funksjonalitet fra enkle byggeklosser.
CSV-format
composite,,0410,,,,"0410a,0410b,0410c",,,,,Kundefordringer
range,9000,0410a,,N,,1500-1599,SUB,,,,"Kundefordringer ordinære"
range,9000,0410b,,N,,1500-1599,SUB,,,,"Kundefordringer erstatning"
choose-account,9000,0410c,,,,1400-1499,"SUB, PFI",,,,"Velg kontoer"
Nøkkelegenskaper
-
CompositePostdefinerer sine deler i Definition-kolonnen som en mellomrom-separert liste over underpost-nummer. Ingen operatorer — "composite" impliserer summasjon. -
Minus må håndteres på underpost-nivå via faktor/negate.
-
Underposter er vanlige poster med
SUB-trait og kan være av enhver type: range, input, choose-account, sum-post, eller en annen composite. -
En underpost kan inngå i mer enn én composite eller sumpost.
-
Overstyring (
AllowsOverride) kan settes både på compositenivå og på underpostnivå. -
CompositePostberegnes i Fase 2 som enEquationPostCalculator, sortert etter ordinal.
SUB-trait og synlighet
SubPost (SUB) er en strukturell markør uten PostBehaviour.
Underposter vises ikke i øvre postoversikter fordi de ikke er tildelt navngitte felter i temavisninger — de er kun tilgjengelige via posts-kartet på temaets datatstruktur.
Innenfor en compositepost gjelder vanlige synlighetsregler (tomme poster skjules osv.).
Dupliserte kontoer
Hvis en og samme konto finnes i to eller flere underposter, legger CompositePostCalculator til en advarsel (PostDetailKey.DuplicateAccountAcrossSubPosts) på compositeposten.
Summen beregnes normalt — advarselen er informativ.
Nesting
En CompositePost kan ha en annen CompositePost som underpost.
I frontend vises en nestet composite kun som oppsummering (postnummer + beskrivelse + beløp) med en lenke til compositen.
Interaktiv redigering av composite-i-composite er ikke støttet, per design.
InlineSubPosts-trait (INL) og frontend-rendering
InlineSubPosts (kortform INL) er en PostBehaviour.Display-trait satt på selve compositeposten.
Den styrer kun visning — beregningen er den samme.
Datakontrakt fra backend til frontend:
-
Compositeposten leveres som en vanlig
Postmedtype = CompositePostog total iamount. -
Underposter leveres ved siden av compositeposten i samme sub-theme
posts-kart, ikke nestet under.PostsThemeCalculator#collectCompositeSubPostNossørger for at underpostnumre matcher selv om de ikke direkte er en del av sub-themets sumpost. -
Frontend rebygger nestingen i
ResultAndBalanceUtils.buildSubThemeog setter:-
subPosts: Post[]— underpostene i CSV-rekkefølge. -
defaultInline: boolean—truehvis composite-spec’en harINL-traiten.
-
Rendering i ResultAndBalanceSubTheme.vue:
defaultInline |
Standard visning |
|---|---|
|
Underposter alltid synlige under compositens rad. Ingen toggle. |
|
Bare compositens rad er synlig. Et tre-ikon i |
Når underposter vises, opptrer compositens rad og underpostene som én felles boks; compositens egne kontoer skjules siden totalen kommer fra underpostene. Splitt/slå sammen-knappen på compositens rad toggler kun synligheten av underpostene og påvirker ikke radens expand/collapse-tilstand.
Endringer i spec → forventet UI-effekt:
| Spec-endring | UI-effekt |
|---|---|
Legg til |
Underposter blir skjult som standard, tre-ikon-toggle vises. |
Fjern |
Underposter alltid synlige, ingen toggle. |
Legg til/fjern underpost i |
Underpostlisten oppdateres; rekkefølgen følger CSV. |
Endre underpost-traits (f.eks. |
Påvirker underpostens egen oppførsel som vanlig — ingen særegen composite-håndtering. |
Kjente begrensningar
-
En compositepost kan ha enhver posttype som del, men kun underposter med
SUB-trait bør vere redigerbare i compositens UI. Deler utanSUBbør visast som skriveverna verdiar — brukaren redigerer dei på sin vanlege plass, ikkje inne i compositen. -
Nestet composite-i-composite er alltid skriveverna (sjå Nesting ovanfor).
ReceiverPost
En ReceiverPost er en post som eksisterer for å absorbere kontoer som er omdirigert hit fra andre poster via erstatningsmekanismen (replacementPostNo på en RangePostSpec).
Den har ingen kontoområder av seg selv — innholdet bestemmes utelukkende av hva andre poster forskyver inn i den.
CSV-format
range,,2990a,1570,N,,2990-2999,SUB,2025.2,,,
receiver,,2990r,,,,,SUB,2025.2,,,
composite,9550,2990,,,,"2990a,2990r",INL,2025.2,,,
Kildeposten ruter til mottakeren ved å sette sin eksisterende replacementPostNo til mottakerens postNo.
Ingen ny kobling — eksisterende mekanisme gjenbrukes.
Beregning og opphav (PostDetail)
Når RangePostCalculator flytter en konto til en post som er en ReceiverPostSpec:
-
Beløpet legges inn med negert fortegn (samme oppførsel som dagens erstatningsposter).
-
Én
PostDetail-oppføring per mottatt konto attacheres på mottakerensPostAntity:-
level = Info -
key = ReceivedAccount -
value = ReceivedAccount\{accountNo, originalPostNo, reason\}
-
Når flere range-poster omdirigerer til samme mottaker, slår PostAntity.merge sammen beløp, kontoer og detaljer.
DisplacementReason
Enum i tritt.finsta.api.spec:
-
SignMismatch— kontoens beløpsfortegn matchet ikke kildepostens forventede faktorfortegn.
Eneste verdi i v1 fordi det er den eneste forskyvningstriggeren i kalkulatorpipelinen i dag. Nye verdier kan legges til som ikke-brytende utvidelser.
Frontend-rendering
Mottakerens underpost rendres av ResultAndBalanceReceiverSubPost.vue og:
-
Skjules helt når den ikke har noen kontoer.
-
Når ikke-tom: viser et generisk informasjonsbanner ("Kontoer i denne posten er omdirigert fra andre poster.") og hver konto-rad med en liten etikett som angir opprinnelig post og årsak (f.eks. "Fra 1570b — feil fortegn").
i18n-nøkler ligger under business-information.receiver.* (banner-tekst, "Fra {postNo}", og oversettelser for hver DisplacementReason).
Spec-drevet rendering og temaer
Frontend renderer skjemaer, rapporter og noter dynamisk fra postspesifikasjonene — strukturen i UI-et følger spec’en, ikke håndskreven Vue-kode per post.
Datatflyt
-
Backend henter spec’en for årsregnskapets
SpecKeyog kjørerPostsCalculatormot saldobalansen. -
PostsThemeCalculatorgrupperer de beregnede postene i sub-themes (f.eks. Resultat, Balanse, Egenkapital) basert på spec-strukturen. -
Hvert sub-theme leveres til frontend som et
posts-kart (postnr →Post), sammen med sumposter og evt. compositeposter med tilhørende underposter. -
Frontend bygger trærne på nytt (f.eks.
ResultAndBalanceUtils.buildSubTheme) og rendrer rader generisk fraPostTypeogPostTrait-settet — ingen postnummer er hardkodet i komponentene.
Konsekvenser for spec-endringer
-
Å legge til/fjerne en post i CSV-filen påvirker UI-et uten frontend-endringer, så lenge posten har gyldige traits og inngår i en sumpost som temaet konsumerer.
-
Display-traits (
ShowByDefault,HideByDefault,NeverShowEmpty,AccountantOnly,INL) styrer synlighet og layout — frontend leser disse direkte fraPost.traits. -
Themes navngir ikke underposter eksplisitt; underposter (
SUB) er kun synlige innenfor sin compositepost. Se CompositePost.
Viktige kildefiler
| Komponent | Plassering |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|