Årsavslutning — implementasjon
Denne siden beskriver den tekniske implementasjonen av årsavslutningskalkulatoren i Finsta. Mens de øvrige sidene Skatt til Disponering dekker regnskapsreglene, fokuserer denne siden på kalkulator-pipelinen, dens input og samspillet med saldobalansen.
Oversikt
YearEndClosingCalculator orkestrerer fem delkalkulatorer som kjøres i en fast rekkefølge.
Hvert steg kan avhenge av resultatet fra foregående steg.
plantuml::example$/year-end-closing/yec-pipeline.puml[]
Det endelige YearEndClosingEntity-objektet inneholder alle genererte tilleggsposteringer samt merknader og advarsler satt sammen av YearEndClosingRemarkAssembler.
Input
Kjernedata
| Input | Beskrivelse |
|---|---|
TrialBalance (saldobalanse) |
Kontosaldoer for regnskapsåret. Hentes via |
FinancialStatement |
Næringsoppgave-entiteten som inneholder inndataposter (parametere) lagt inn av brukeren. |
BusinessInformation |
Beregnede størrelser som beregnet næringsinntekt og midlertidige forskjeller. |
CompanyGroupRegistryComposite |
Konsernbidragsdata fra cogreg-modulen. Kun relevant når |
Parametere (CalculateYearEndClosingParams)
Åtte valgfrie parametere som hentes fra inndataposter lagret på næringsoppgaven.
YearEndClosingParamsBuilder leser inndataposter fra næringsoppgave-entiteten og mapper dem til params-recorden.
Konsernparametere er betinget av PostTrait.CompanyGroup.
Skatteparametere
| Postkode | Felt | Beskrivelse |
|---|---|---|
|
receivedCompanyGroupContributionsWithTaxEffect |
Mottatte konsernbidrag med skatteeffekt (kun konsern) |
|
transferableDeficitPrevious |
Fremførbart underskudd fra foregående år |
|
appliedDeficitFromPrevious |
Andel av fremførbart underskudd benyttet i år |
|
alreadyRegisteredTax |
Skatt allerede bokført før årsavslutning |
Parametere for utsatt skatt
| Postkode | Felt | Beskrivelse |
|---|---|---|
|
~registeredTransferableDeficitPrevious~ |
Utgår. Var brukt av den delta-drevne kalkulatoren til å angi hvor mye av foregående underskudd som allerede var reflektert i IB 1070/2120. Har ingen effekt i den NRS-kanoniske, målstyrte kalkulatoren og ignoreres. Beholdt for bakoverkompatibilitet med lagrede inndataposter. |
|
~negativelyBalancedOutPrevious~ |
Utgår. Pass-through-felt — leses fra params og lagres uendret på |
|
registerDeferredTaxAdvantage |
Om utsatt skattefordel skal bokføres (standard: |
ExistingAccounts-mønsteret
Alle delkalkulatorer leser kontosaldoer via ExistingAccounts, en record som slår opp saldoer i saldobalansen og samtidig ekskluderer eksisterende årsavslutningstransaksjoner.
ExistingAccounts.withoutYec(trialBalance, accountNoOrRange)
Metoden registered() returnerer:
closingBalance - excludedTransactionAmounts
Dette sikrer at kalkulatoren unngår dobbeltelling ved rekalkulering: eksisterende årsavslutningstransaksjoner strippes ut slik at beregningen alltid starter fra de samme grunnlagstallene.
Steg 1: Skatt (TaxCalculator)
Bygger en TaxBasisAntity basert på:
-
estimatedBusinessIncome— fra BusinessInformation -
receivedCompanyGroupContributionsWithTaxEffect— fra parametere (eller overstyrt fra cogreg) -
transferableDeficitPrevious— fra parametere eller utkast til skattemelding -
appliedDeficitFromPrevious— fra parametere -
alreadyRegisteredTax— fra parametere
Steg 2: Utsatt skatt (DeferredTaxCalculator)
NRS(F) Resultatskatt §2.4 / NRS 8 §10-alignert, målstyrt kalkulator. Se NRS(F) Resultatskatt 2008 og NRS 8 (des 2021).
| Kalkulatoren bruker standard regnskapskonvensjon gjennomgående: positivt = forpliktelsesretning (utsatt skatt), negativt = fordelsretning (skattefordel). |
Prinsipp: reelle verdier styrer grunnlaget
tempDiffCurrent (post 200DC) og transferableDeficit er de reelle inngangsstørrelsene.
IB på 1070/2120 er bokføringstekniske detaljer — de påvirker ikke grunnlaget, kun transaksjonsdelta-et.
Algoritme (fem steg)
Steg 1 — Fjorårsposisjon. To halvdeler:
-
Bokført — IB på
1070/2120, strippes for årsavslutningsposteringer viaExistingAccounts.withoutYec(). -
Ikke-bokført —
transferableDeficitPreviousognegativelyBalancedOutPreviousfra parametere. NårregisterDeferredTaxAdvantage = Noer IB på1070null per konstruksjon; disse parameterne er da eneste signal om at et ubenyttet underskudd/midlertidig forskjell videreføres.
Steg 2 — Årets posisjon i grunnlagsform.
ubBasis = tempDiffCurrent − transferableDeficit
der
transferableDeficit = deficitCurrent + transferableDeficitPrevious − appliedDeficitFromPrevious
Negativt ubBasis = skattefordelsretning, positivt = skatteforpliktelsesretning.
Steg 3 — Målsaldoer.
targetAdvantage = |ubBasis| × 0.22 hvis ubBasis < 0 AND register = Yes, ellers 0 targetObligation = −ubBasis × 0.22 hvis ubBasis > 0, ellers 0
Steg 4 — Transaksjoner som delta.
Δ1070 = targetAdvantage − IB 1070 Δ2120 = targetObligation − IB 2120
Ingen netting mellom 1070 og 2120 — eksisterende saldo fra urelaterte hendelser reverseres separat.
Steg 5 — Noteopplysning (NBO).
| Situasjon | Verdi |
|---|---|
|
0 — hele fordelen er bokført |
|
|
Begge variantene (nboBasisIfBooked, nboBasisIfNotBooked) er alltid tilgjengelige på NegativelyBalancedOutAntity slik at frontend kan vise en sammenligning.
registerDeferredTaxAdvantage-bryteren
|
Hovedregel: Utsatt skatt ( |
Bryteren påvirker kun målsaldoen for 1070 (steg 3) og noteopplysningen (steg 5).
Den delta-drevne logikken med removeDeferredTaxAdvantage-postpasset er fjernet.
Steg 3: Årsresultat (NetProfitOrLossCalculator)
Beregner det kompenserte årsresultatet fra post 9100 (resultat før skattekostnad). Post 9100 inneholder ingen 83xx- eller 89xx-kontoer, og gir dermed et rent utgangspunkt.
kompensertÅrsresultat = post9100 - skatt - actualDeferredTax()
actualDeferredTax() returnerer P&L-effekten av utsatt skatt — beløpet som faktisk er bokført på 8321/8322 i perioden (deferredTaxPnlEffect), ikke den fulle utsatt-skatt-verdien fra grunnlaget.
Når «bokfør utsatt skattefordel» er avslått, returneres i stedet nbo-verdien (tilbakeføring av eksisterende fordel).
Steg 4: Utbytte (DividendCalculator)
Leser eksisterende utbyttekontosaldoer:
| Konto | Beskrivelse |
|---|---|
|
Tilleggsutbytte fra tidligere år |
|
Ekstraordinært utbytte inneværende år |
|
Avsatt utbytte for neste år |
Beregning
sumDividend = additionalFromPreviousYear
+ extraordinaryCurrentYear
+ alreadyAllocatedForNextYear
availableForAllocation = compensatedNetProfitOrLoss - sumDividend
(begrenset til annen egenkapital-saldo)
totalAllocatedForNextYear = alreadyAllocated + extraAllocatedDividendForNextYear
Oppretter en tilleggspostering dersom ekstra utbytte avsettes: D: 8920, K: 2800.
Når spesifikasjonen angir et kontointervall (f.eks. 8920-8921), brukes alltid den første kontoen i intervallet som debetkonto i tilleggsposteringen.
Brukeren kan foreløpig ikke velge hvilken konto som benyttes.
Steg 5: Disponering (AllocationCalculator)
Fordeler sluttresultatet til egenkapital- eller tapskontoer.
Grunnlag
allocationAmount = compensatedNetProfitOrLoss
+ receivedCgOnDisponering (kompensasjon for 8930-8934, se under)
- totalSubmittedCompanyGroupContributions (justert for skatteeffekt)
- totalDividendAllocatedForNextYear
receivedCgOnDisponering er sum av saldoer på kontoene 8930–8934 (mottatt konsernbidrag ført direkte som disponeringspost), negert.
Disse kontoene inngår i POST 8999 (disponeringer) og ikke i POST 9200 (årsresultat).
Uten kompensasjonen ville bokføring av konsernbidrag på 8930 (disponeringspost) gi en lavere disponering enn bokføring på 8001 (inntektskonto), selv om egenkapitaleffekten er identisk.
Kompensasjonen sikrer at YEC-posteringen på 8960 → 2050 er den samme uavhengig av hvilken konto konsernbidraget er ført på.
Transaksjonsscenarioer
| Scenario | Debet | Kredit |
|---|---|---|
Overskudd dekker udekket tap fra tidligere |
|
|
Overskudd overført til annen egenkapital |
|
|
Underskudd overført fra annen egenkapital |
|
|
Underskudd overført til udekket tap |
|
|
Når disponeringen skifter retning (f.eks. overskudd som først må dekke tap før overføring til egenkapital), genereres to transaksjoner.
Spesifikasjonsversjonering
Årsavslutning hentes fra to separate CSV-kilder, hver med sitt eget ansvar og sin egen provider. Begge versjoneres etter samme prinsipp som postspesifikasjonene (se versjonering): versjonerte rader overstyrer uversjonerte rader for et gitt inkrement, slik at vi kan rette mappinger uten å påvirke eksisterende årsregnskap.
| Fil | Provider | Ansvar |
|---|---|---|
|
|
Kalkulatorens kontomapping og skattesats: hvilke kontoer skatt, utsatt skatt, utbytte og disponering bokføres på (8300/2500, 8321/8322/1070/2120, 8920–8923, 8960/8961/8969/8990/2050/2080), samt skattesatsen (0.22) og postkoder for midlertidige forskjeller (200DC/200DP/200R). |
|
|
YEC-inndataposter ( |
Med andre ord: yec-spec-mapping styrer hvor posteringene havner og hva skattesatsen er, mens posts-spec-mapping styrer hvilke spørsmål brukeren får og hvordan resultatene flyter videre inn i næringsspesifikasjonen.
| Komponent | Sti |
|---|---|
YearEndClosingSpecProvider |
|
YearEndClosingSpec |
|
PostsSpecProvider |
|
Kalkulator-CSV |
|
Inndataposter-CSV |
|
YEC-versjonering (YecVersion)
YecVersion er en enum som stempler hver YearEndClosingEntity med hvilken kalkulator-kontrakt raden ble produsert under.
Mønsteret speiler AssregVersion i aktivaregisteret.
Hvorfor versjonering trengs
Kalkulatoren gikk fra en delta-drevet modell — "bokfør differansen mellom det vi vil ha og det som allerede ligger i alreadyRegistered*`" — til en target-drevet modell: for hver bokføringsakse beregner kalkulatoren år-slutt-målbalansen (med Finstas egne YEC-transaksjoner strippet ut av eksisterende posisjon), og utleder bokføringen som `target − existing.
De fire aksene:
| Akse | Kontoer | Eksisterende posisjon på grunnlaget |
|---|---|---|
Skatt |
2500 ↔ 8300 |
|
Utsatt skatt |
1070 ↔ 2120 |
|
Utbytte |
2800 ↔ 8920 |
|
Disponering |
2050 ↔ 2080 |
|
Dette er en semantisk endring i hvordan YEC-bokføringer regnes ut, ikke bare et tillegg av felter. Rader produsert under V0 følger andre regler enn V1-rader, og resten av systemet (frontend-rendering, framtidige kalkulatorer, revisjoner) må kunne resonnere om det eksplisitt.
Verdiene
| Verdi | Beskrivelse |
|---|---|
|
YEC beregnet under den delta-drevne modellen. Null på entiteten normaliseres til denne verdien av mapperen. |
|
Target-drevet kalkulasjon (gjeldende). |
YecVersion.CURRENT peker på den nyeste verdien (i dag: V1_TargetDrivenCalc).
Når blir versjonen stemplet
Versjonen settes til YecVersion.CURRENT inne i YearEndClosingEntity.withData(…) — kalkulatorens ene innstegspunkt for å skrive nye data.
Det betyr at:
-
Levende årsregnskap får
CURRENTved nesteSynchronizationService-kjøring. -
Frosne årsregnskap blir værende på sin frys-tids-versjon. Frosset betyr frosset — ingen resync — og versjonen reflekterer reglene de ble beregnet under.
Migrasjonen V2_3_0_1190 legger til kolonnen yec_version uten backfill: null = V0_PreVersioning, som er korrekt semantikk for eldre rader.
Vedlikeholdsregel
YecVersion.CURRENT skal bumpes hver gang kalkulatorens kontrakt endres på en måte som gjør at eldre rader følger andre regler enn nye:
-
Ny bokføringsakse (eller endret kontomapping for en eksisterende akse).
-
Semantisk endring i hvordan
existing*utledes (f.eks. ny strip-strategi). -
Endring i hvilken delkalkulator som eier hvilken transaksjon.
Å glemme bumpen er et stille feilmodus — samme risikomodell som for AssregVersion.
Frontenden bruker for eksempel yecVersion === V1_TargetDrivenCalc til å gate den per-undertema kalkulasjonsdetalj-panelen; uten en bump ville V0-data feilrenderes som om den fulgte V1-kontrakten.
| Komponent | Sti |
|---|---|
YecVersion |
|
YearEndClosingEntity#withData |
|
YearEndClosingMapper |
|
Migrasjon |
|
Tilleggsposteringer
Alle tilleggsposteringer opprettes via YearEndClosingMethods.createYecAtx():
-
Oppretter debet- og kredit-
TransactionLineAntity -
Søker etter eksisterende årsavslutningstransaksjoner på de samme kontoene
-
Gjenbruker eksisterende transaksjons-UUID dersom funnet (idempotente oppdateringer)
-
Daterer transaksjonen til
period.getEndsAt()(slutten av regnskapsåret) -
Setter opprinnelse til
TransactionOriginAntity.of(Finsta, YearEndClosing)
Konsernbidrag
Når PostTrait.CompanyGroup er aktivt, påvirker konsernbidrag flere steg:
-
Skatt: mottatte bidrag med skatteeffekt LEGGES TIL beregnet næringsinntekt
-
Utsatt skatt: mottatte bidrag påvirker beregningen av fremførbart underskudd
-
Disponering: avgitte bidrag (justert med faktor
1 - skattesats) TREKKES FRA disponeringsbeløpet
Data hentes fra CompanyGroupRegistryComposite (cogreg-modulen).
Merknader
YearEndClosingRemarkAssembler delegerer til BusinessInformationEntity.toInfo().assembleYecRemarks() for å generere advarsler og merknader som returneres sammen med beregningsresultatet.
Planlagte merknader for utsatt skatt (ikke implementert — Phase 5)
Følgende merkander er identifisert men ikke implementert ennå.
De skal legges til i YearEndClosingRemarkAssembler og/eller DeferredTaxCalculator når utsatt-skatt-løsningen er stabilisert.
| Trigger | Alvorlighet | Merknad |
|---|---|---|
IB på |
Advarsel |
Eksisterende saldo på konto 1070 har feil fortegn. Sjekk om årsavslutningstransaksjoner er korrekt stripset, eller om det er en manuell bokføringsfeil. |
IB på |
Advarsel |
Eksisterende saldo på konto 2120 har feil fortegn. Sjekk om årsavslutningstransaksjoner er korrekt stripset, eller om det er en manuell bokføringsfeil. |
|
Feil |
Anvendt fremført underskudd overstiger tilgjengelig underskudd. Parametrene for YECT001/YECT002 er inkonsistente. |
|
Info |
Bokføring av utsatt skattefordel er slått av. Eksisterende saldo på 1070 reverseres. |
|
Info |
Bokføring av utsatt skattefordel er slått på. Fordelen balanseføres for første gang dette året. |
|
Info |
Utsatt skattefordel vil ikke bokføres, men det er en eksisterende saldo på 1070. Saldoen reverseres. |
Viktige kildefiler
| Komponent | Sti |
|---|---|
YearEndClosingCalculator |
|
TaxCalculator |
|
DeferredTaxCalculator |
|
NetProfitOrLossCalculator |
|
DividendCalculator |
|
AllocationCalculator |
|
ExistingAccounts |
|
YearEndClosingMethods |
|
YearEndClosingParamsBuilder |
|
YearEndClosingServiceBean |
|
YearEndClosingController |
|