Tax return computation and submission for Norwegian AS (RF-1028 and RF-1167).
Ported from wenche/skattemelding.py in the original Python Wenche project.
beregn/2returns the computed tax return as a structured map suitable for rendering in a UI or further processing.valider/2andsend_inn/2orchestrate validation and submission via Skatteetaten / Altinn 3.
Supports:
- Standard 22 % corporate tax calculation
- Fritaksmetoden (participation exemption) for subsidiary dividends
- Loss carryforward deduction
- Caller-supplied
:permanent_forskjelleronSkattemeldingKonfig - Prior year comparison figures and equity reconciliation note (via
beregn/2)
Authentication
Validation and submission use different auth flows:
- Validation (
valider/3→SkdSkattemeldingClient) works with a Maskinporten + systemuser token, obtained viaWenche.Maskinporten.get_skd_skattemelding_token/2. Thealtinn:instances.read/.writescopes in that token are required so Skatteetaten can resolve the systemuser → executor trace via Altinn. - Submission (
send_inn/4→AltinnClient) requires an Altinn platform token obtained from ID-porten (end-user authentication + Altinn token exchange). Skatteetaten does not accept a system user for the submission step. Wenche does not provide an ID-porten flow — callers must obtain the Altinn token themselves and pass it viaAltinnClient.new/2.
Experimental: Systemic submission
Systemic submission of the skattemelding via Altinn 3 (send_inn/4)
is untested and highly experimental. It also requires the
submitting end-user to be a registered revisor or regnskapsfører.
Enable Wenche.Systembruker.rights([:skattemelding]) for the
validation flow; it has no effect on submission since that goes
through ID-porten.
Summary
Functions
Computes the tax return as a structured map.
Builds the data needed for <egenkapitalavstemming> in
næringsspesifikasjonen.
Computes the skattepliktig næringsinntekt brutto (the "skattemessig resultat") for a given regnskap and permanent_forskjeller list.
Parses Skatteetaten's /valider response and extracts the SKD-computed
values from the embedded skattemeldingUpersonligEtterBeregning document.
Returns {sum_tillegg, sum_fradrag} as positive integers from a list of
permanent forskjeller. Used by the XML emitter to populate the derived
<sumTilleggINaeringsinntekt> / <sumFradragINaeringsinntekt> elements
Skatteetaten flags as missing.
Submits the tax return to Skatteetaten via Altinn 3.
Validates the tax return against Skatteetaten's validation API.
Functions
Computes the tax return as a structured map.
Returns a map with all computed values for RF-1167 and RF-1028, suitable for rendering in a UI or further processing.
@spec beregn_egenkapitalavstemming(Wenche.Models.Aarsregnskap.t()) :: %{ inngaaende_ek: integer(), utgaaende_ek: integer(), endringer: [map()], sum_tillegg: integer(), sum_fradrag: integer() }
Builds the data needed for <egenkapitalavstemming> in
næringsspesifikasjonen.
Returns a map with:
:inngaaende_ek— IB total egenkapital in whole kroner.:utgaaende_ek— UB total egenkapital in whole kroner.:endringer— list of%{id, type, kategori, beloep}rows describing each årets endring.beloepis always emitted as a positive integer;kategori(:tilleggor:fradrag) tells the consumer which sign to apply.typeis a kode from the2025_egenkapitalendringstypekodeliste.:sum_tillegg,:sum_fradrag— derived sums for the optional XSD elements.
When the regnskap has no fjoraar data, IB is 0 and årsresultat is
treated as the entire build-up of EK.
Computes the skattepliktig næringsinntekt brutto (the "skattemessig resultat") for a given regnskap and permanent_forskjeller list.
Used by the XML emitter to populate <beregnetNaeringsinntekt> /
<skattemessigResultat> and the underskudd-derived elements
Skatteetaten flags as missing. Mirrors what beregn/2 already
computes internally for rf_1028.skattepliktig_inntekt_brutto.
Parses Skatteetaten's /valider response and extracts the SKD-computed
values from the embedded skattemeldingUpersonligEtterBeregning document.
SKD echoes back the skattemelding with derived (skatt:erAvledet="true")
elements populated — those are the numbers callers want to display alongside
a successful validation, since they're what Skatteetaten will use for tax
assessment.
Returns a map with the extracted fields. Fields that aren't present in the
response (which is normal — many are optional and only emitted when
applicable) come back as nil.
Extracted fields
:partsnummer— SKD's resolved partsnummer:inntektsaar:inntekt_foer_fradrag_for_eventuelt_avgitt_konsernbidrag— taxable income before group contribution deduction (negative on a loss year):samlet_inntekt— total income after deductions (zero on a loss year):samlet_underskudd— year-loss:fremfoert_underskudd_fra_tidligere_aar— prior-year carryforward used:fremfoerbart_underskudd_i_inntekt— total loss available for future carryforward (prior + this year's added):netto_formue— net wealth:samlet_formuesverdi_etter_verdsettingsrabatt:samlet_gjeld
Pass the raw response body from valider/4's {:ok, body} tuple.
Returns {sum_tillegg, sum_fradrag} as positive integers from a list of
permanent forskjeller. Used by the XML emitter to populate the derived
<sumTilleggINaeringsinntekt> / <sumFradragINaeringsinntekt> elements
Skatteetaten flags as missing.
Submits the tax return to Skatteetaten via Altinn 3.
Generates XML documents from the given Aarsregnskap and SkattemeldingKonfig,
then submits via the Altinn 3 skattemelding app.
To inspect the generated XML without submitting, call
Wenche.SkattemeldingXml.generer_skattemelding_xml/3,
generer_naeringsspesifikasjon_xml/2, and generer_request_xml/3 directly.
Options
:skd_client—SkdSkattemeldingClientused to fetch the real partsnummer and dokumentreferanse from SKD. Strongly recommended; without it the request envelope falls back to using the org number as partsnummer and emits no<dokumentreferanseTilGjeldendeDokument>, which causes SKD to reject the submission withinnkommendeForespoerselManglerSporTilUtfoerende.:dokumentidentifikator— reference to draft (fromhent_utkast); used only when:skd_clientis not supplied.:opprettet_av— text emitted in<opprettetAv>to identify the originating end-user system (default"Wenche").:innsendingstype,:innsendingsformaal— envelope overrides; seeSkattemeldingXml.generer_request_xml/3.
Returns {:ok, inbox_url} or {:error, reason}.
Validates the tax return against Skatteetaten's validation API.
Generates XML documents and sends them to the validation endpoint.
Returns {:ok, validation_result} or {:error, reason}.