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:

  1. CSV-parsing — spesifikasjonsfiler leses og parses av PostSpecCsvParser

  2. Transformasjon — parsede rader transformeres til typede postobjekter av PostSpecTransformer

  3. Beregning — PostsCalculator evaluerer 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. 3000, 4005

3

Replacement

Utgått postnummer som denne posten erstatter

4

Negate

Y eller N — om verdien skal negeres

5

Factor Override

Egendefinert multiplikator (overstyrer standard kategorifaktor)

6

Definition

Typespesifikt innhold: kontoområder for range-poster, postreferanser for sum-post, formler for formula

7

Traits

Kommaseparerte traitkoder (se Traitsystem)

8

Version

Spesifikasjonsversjon, f.eks. 2025.0

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 PFI forhåndsfylles utvalget fra saldobalansen.

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 SummedByPost.

sum-post

Eksplisitt sumpost med en SUM-formel over andre poster.

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

Table 1. Postspesifikasjoner og årsregnskapsmapping
Versjon Merknader

2022.0

Basisversjon, brukes for eldre regnskapsår

2024.0

Første 2024-versjon

2024.1

Midtårsoppdatering

2024.2

Siste 2024-inkrement før årsavslutningsspesifikasjoner

2024.3

Rettet utbyttemapping i årsavslutning

2025.0

Første 2025-versjon

2025.1

Rettet utbyttemapping i årsavslutning

2025.2

Splittet 1570 og 2990 i underposter (composite) og introduserte mottakerpost (ReceiverPost) for fortegnsmessig omdirigering. Se ReceiverPost. Flyttet kontorekken 1585-1589 fra trade-debtors til other-receivables i årsregnskapsmappingen.

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

RangePost

Kobler en post til ett eller flere kontoområder i saldobalansen. F.eks. post 3000 = sum av kontoer 3000-3099. AccountCategory bestemmer fortegnsfaktoren (Kredit = -1, Debet = +1). En valgfri erstatningspost håndterer beløp med motsatt fortegn.

Input

InputPost

Brukeroppgitt verdi. Undertyper: Amount, Decimal, Integer, Text, Radio, ChooseAccount, DomainObject, Date. Verdier lagres som InputPostAntity-poster på saldobalansen. Kan ha en PostRule for validering.

From

FromPost

Kopierer den beregnede verdien fra en annen post. En valgfri gate (PostRule) kan transformere verdien før kopiering.

Sum

SumPost

Aggregerer andre poster med + / - operatorer. F.eks. post 9010 = +3000 +4005 +5000. Kan være eksplisitt (definert i Definition-kolonnen) eller utledet (bygget automatisk fra SummedByPost-kolonnereferanser).

Formula

FormulaPost

Avansert beregning med flere deler og operatorer (+, -, *, /). Brukes til komplekse beregninger på tvers av skjemaer.

Derived

DerivedPost

Beregnet resultat. Ikke en CSV-typeidentifikator — opprettes av kalkulatorpipelinen, ikke deklarert i spec-filer.

Composite

CompositePost

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 (0410a,0410b,0410c). Underposter er vanlige poster tagget med SUB-traiten — de vises kun innenfor sin compositepost og er usynlige for eksterne forbrukere (skattemelder, øvre postoversikter). Se CompositePost.

Receiver

ReceiverPost

Absorberer kontoer som er omdirigert hit fra andre poster via replacementPostNo-mekanismen. Har ingen kontoområder selv — innholdet kommer utelukkende fra andre poster. Brukes typisk som underpost i en CompositePost, slik at omdirigerte kontoer får en tydelig plass i UI-et med opphavsmerker per konto. Se ReceiverPost.

Dummy

DummyPost

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

RangePostCalculator

Skanner saldobalansekontoer, grupperer dem etter kontoområde, summerer saldoer og anvender kontokategorifaktoren

InputPostCalculators

Henter brukerinput fra InputPostAntity-poster på saldobalansen

Resultater fra fase 1 samles i BasePosts.

Fase 2 — Fra BasePosts

Kalkulator Beskrivelse

SumPostCalculator

Summerer refererte poster fra BasePosts

FromPostCalculator

Kopierer verdier fra BasePosts (med valgfri gate-transformasjon)

FormulaPostCalculator

Evaluerer komplekse uttrykk med verdier fra BasePosts

CompositePostCalculator

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

ShowByDefault, HideByDefault, NeverShowEmpty, AccountantOnly

Infer

Styrer automatisk verdipopulering

PrefillInput, PrefillInputAccountRanges

Requires

Betinger beregning på årsregnskapsegenskaper

NonDomestic, Agriculture, CompanyGroup, Securities, Salary, Liquidated

Overstyringstraits

Traitene AllowsOverride og AllowsOverrideAmount tillater brukeren å manuelt overstyre en beregnet verdi.

Spesielle traits

Trait Beskrivelse

Precalculated

Verdien beregnes utenfor den normale pipelinen

NotSupported

Posten er gjenkjent, men ikke ennå implementert

Experimental

Posten er under utvikling

SubPost

Strukturell markør — posten er en del av en CompositePost. Underposter vises kun innenfor sin compositepost, ikke i øvre postoversikter (de er ikke tildelt navngitte felter i temavisninger). Ingen PostBehaviour — påvirker ikke TraitsService-logikken. Kortform: SUB.

Postregler

PostRule validerer og transformerer eventuelt postbeløp:

Regel Oppførsel

PositiveAmount

Verdi må være >= 0

NegativeAmount

Verdi må være <= 0

PositiveAmountToAbs

Verdi må være >= 0; returnerer absolutt verdi

NegativeAmountToAbs

Verdi må være <= 0; returnerer absolutt verdi

AnyAmount

Ingen validering

Group

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

  • CompositePost definerer 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å.

  • CompositePost beregnes i Fase 2 som en EquationPostCalculator, 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 Post med type = CompositePost og total i amount.

  • Underposter leveres ved siden av compositeposten i samme sub-theme posts-kart, ikke nestet under. PostsThemeCalculator#collectCompositeSubPostNos sørger for at underpostnumre matcher selv om de ikke direkte er en del av sub-themets sumpost.

  • Frontend rebygger nestingen i ResultAndBalanceUtils.buildSubTheme og setter:

    • subPosts: Post[] — underpostene i CSV-rekkefølge.

    • defaultInline: booleantrue hvis composite-spec’en har INL-traiten.

Rendering i ResultAndBalanceSubTheme.vue:

defaultInline Standard visning

false (ingen INL)

Underposter alltid synlige under compositens rad. Ingen toggle.

true (INL satt)

Bare compositens rad er synlig. Et tre-ikon i label-suffix lar brukeren utvide til å se underpostene. Toggle-state lagres per postNo lokalt.

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 INL på composite

Underposter blir skjult som standard, tre-ikon-toggle vises.

Fjern INL fra composite

Underposter alltid synlige, ingen toggle.

Legg til/fjern underpost i SUM …-uttrykket

Underpostlisten oppdateres; rekkefølgen følger CSV.

Endre underpost-traits (f.eks. AO, PFI)

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 utan SUB bø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å mottakerens PostAntity:

    • 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

  1. Backend henter spec’en for årsregnskapets SpecKey og kjører PostsCalculator mot saldobalansen.

  2. PostsThemeCalculator grupperer de beregnede postene i sub-themes (f.eks. Resultat, Balanse, Egenkapital) basert på spec-strukturen.

  3. Hvert sub-theme leveres til frontend som et posts-kart (postnr → Post), sammen med sumposter og evt. compositeposter med tilhørende underposter.

  4. Frontend bygger trærne på nytt (f.eks. ResultAndBalanceUtils.buildSubTheme) og rendrer rader generisk fra PostType og PostTrait-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 fra Post.traits.

  • Themes navngir ikke underposter eksplisitt; underposter (SUB) er kun synlige innenfor sin compositepost. Se CompositePost.

Beslektede kalkulatorer

PostsThemeCalculator ligger ved siden av PostsCalculator og bruker samme BasePosts som input. Når en spec-endring må reflekteres i et nytt eller endret tema, er det her koblingen mellom spec og UI-struktur faktisk skjer.

Viktige kildefiler

Komponent Plassering

SpecServiceBean

finsta-csv-parser/…​/spec/SpecServiceBean.java

PostsSpecProvider

finsta-csv-parser/…​/parser/posts/PostsSpecProvider.java

PostSpecCsvParser

finsta-csv-parser/…​/parser/posts/PostSpecCsvParser.java

PostSpecTransformer

finsta-csv-parser/…​/parser/posts/PostSpecTransformer.java

PostsSpec

finsta-api/…​/api/spec/posts/PostsSpec.java

SpecVersion

finsta-api/…​/api/spec/SpecVersion.java

PostType

finsta-api/…​/api/posts/PostType.java

PostTrait

finsta-api/…​/api/PostTrait.java

PostRule

finsta-api/…​/api/spec/posts/PostRule.java

AccountCategory

finsta-api/…​/api/spec/AccountCategory.java

PostsCalculator

finsta-service/…​/service/posts/calculators/PostsCalculator.java

CompositePostCalculator

finsta-service/…​/service/posts/calculators/CompositePostCalculator.java

CompositePostSpec

finsta-api/…​/api/spec/posts/CompositePostSpec.java

AnnualStatementSpecProvider

finsta-csv-parser/…​/parser/annsta/AnnualStatementSpecProvider.java

AnnualStatementGroupCsvParser

finsta-csv-parser/…​/parser/annsta/AnnualStatementGroupCsvParser.java

AnnualStatementGroupSpecLine

finsta-csv-parser/…​/parser/annsta/AnnualStatementGroupSpecLine.java