Skip to content

jarls-side-projects/elixir-wenche

Repository files navigation

Wenche (Elixir)

Hex.pm

Elixir library for Norwegian small business filings — Maskinporten authentication, Altinn 3 API client, BRG XML/iXBRL generation, tax calculation, and shareholder register XML generation.

Ported from the Python CLI tool Wenche.

Modules

Module Description Origin
Wenche.Maskinporten JWT-based auth against Maskinporten + Altinn token exchange wenche/auth.py
Wenche.Systembruker System user registration and management for Altinn 3 wenche/systembruker.py
Wenche.AltinnClient Altinn 3 API client (instances, data upload, completion) wenche/altinn_client.py
Wenche.Aarsregnskap Annual accounts submission flow (config, validation, submission) wenche/aarsregnskap.py
Wenche.BrgXml BRG annual statement XML (hovedskjema/underskjema) wenche/brg_xml.py
Wenche.Ixbrl Inline XBRL (iXBRL) HTML document generation wenche/xbrl.py
Wenche.Noter Notes (noter) for small enterprises — structured XML + iXBRL text wenche/noter.py
Wenche.Skattemelding Tax calculation, structured beregn/2, and Altinn 3 submission orchestration for skattemelding (RF-1028/RF-1167) wenche/skattemelding.py
Wenche.SkattemeldingXml Skattemelding XML (skattemeldingUpersonlig v5, naeringsspesifikasjon v6, request envelope v2) against Skatteetaten XSDs
Wenche.SkdSkattemeldingClient Skatteetaten REST API client for skattemelding (pre-filled draft, valider)
Wenche.MvaMelding VAT return (MVA-melding) submission via Altinn 3. Experimental.
Wenche.MvaMeldingXml MVA-melding XML generation (mvaMeldingInnsending + mvaMeldingDto)
Wenche.Aksjonaerregister RF-1086 shareholder register XML generation wenche/aksjonaerregister.py
Wenche.SkdClient Skatteetaten REST API client for RF-1086 (1086H / 1086U / bekreft)
Wenche.Models Data structures (Selskap, Aarsregnskap, Resultatregnskap, Balanse, SkattemeldingKonfig, etc.) wenche/models.py

Installation

Add wenche to your list of dependencies in mix.exs:

def deps do
  [
    {:wenche, "~> 0.3.0"}
  ]
end

Usage

Maskinporten Authentication

config = [
  client_id: "your-client-id",
  kid: "your-key-id",
  private_key_pem: File.read!("maskinporten_privat.pem"),
  env: "prod"
]

# Standard token for instance operations
{:ok, token} = Wenche.Maskinporten.get_altinn_token(config)

# System user token for organization-specific operations
{:ok, token} = Wenche.Maskinporten.get_systemuser_token(config, "912345678")

# Admin token for system register operations
{:ok, admin_token} = Wenche.Maskinporten.get_admin_token(config)

Submitting Annual Statement

alias Wenche.Models.{
  Aarsregnskap, Selskap, Resultatregnskap, Balanse,
  Driftsinntekter, Driftskostnader, Finansposter,
  Eiendeler, Anleggsmidler, Omloepmidler,
  EgenkapitalOgGjeld, Egenkapital, LangsiktigGjeld, KortsiktigGjeld
}

# Build company data
selskap = %Selskap{
  navn: "Mitt Selskap AS",
  org_nummer: "912345678",
  daglig_leder: "Ola Nordmann",
  styreleder: "Kari Nordmann",
  forretningsadresse: "Storgata 1, 0001 Oslo",
  stiftelsesaar: 2020,
  aksjekapital: 30000
}

# Build financial data
resultatregnskap = %Resultatregnskap{
  driftsinntekter: %Driftsinntekter{
    salgsinntekter: 0,
    andre_driftsinntekter: 0
  },
  driftskostnader: %Driftskostnader{
    loennskostnader: 0,
    avskrivninger: 0,
    andre_driftskostnader: 5000
  },
  finansposter: %Finansposter{
    utbytte_fra_datterselskap: 100000,
    andre_finansinntekter: 500,
    rentekostnader: 0,
    andre_finanskostnader: 0
  }
}

balanse = %Balanse{
  eiendeler: %Eiendeler{
    anleggsmidler: %Anleggsmidler{
      aksjer_i_datterselskap: 500000,
      andre_aksjer: 0,
      langsiktige_fordringer: 0
    },
    omloepmidler: %Omloepmidler{
      kortsiktige_fordringer: 0,
      bankinnskudd: 125500
    }
  },
  egenkapital_og_gjeld: %EgenkapitalOgGjeld{
    egenkapital: %Egenkapital{
      aksjekapital: 30000,
      overkursfond: 0,
      annen_egenkapital: 595500
    },
    langsiktig_gjeld: %LangsiktigGjeld{
      laan_fra_aksjonaer: 0,
      andre_langsiktige_laan: 0
    },
    kortsiktig_gjeld: %KortsiktigGjeld{
      leverandoergjeld: 0,
      skyldige_offentlige_avgifter: 0,
      annen_kortsiktig_gjeld: 0
    }
  }
}

regnskap = %Aarsregnskap{
  selskap: selskap,
  regnskapsaar: 2025,
  resultatregnskap: resultatregnskap,
  balanse: balanse
}

# Generate XML documents
hovedskjema = Wenche.BrgXml.generer_hovedskjema(regnskap)
underskjema = Wenche.BrgXml.generer_underskjema(regnskap)
ixbrl_html = Wenche.Ixbrl.generer_ixbrl(regnskap)

# Or submit directly via Altinn
client = Wenche.AltinnClient.new(altinn_token, env: "prod")
{:ok, inbox_url} = Wenche.Aarsregnskap.send_inn(regnskap, client)

Tax Calculation

Wenche.Skattemelding.beregn/2 returns the computed tax return as a structured map (RF-1167 næringsoppgave, RF-1028 skattemelding, balance overview, equity note, sammenligning, warnings). Render it however you like.

alias Wenche.Models.SkattemeldingKonfig

konfig = %SkattemeldingKonfig{
  anvend_fritaksmetoden: true,
  eierandel_datterselskap: 100,
  underskudd_til_fremfoering: 0
}

beregning = Wenche.Skattemelding.beregn(regnskap, konfig)
# %{
#   selskap: %{navn: ..., org_nummer: ...},
#   regnskapsaar: 2025,
#   rf_1167: %{driftsinntekter: %{...}, driftskostnader: %{...}, ...},
#   rf_1028: %{utbytte: ..., beregnet_skatt: ..., ...},
#   balanse: %{i_balanse: true, differanse: 0, ...},
#   sammenligning: %{...} | nil,
#   egenkapitalnote: %{...} | nil,
#   advarsler: [...]
# }

Shareholder Register (RF-1086)

alias Wenche.Models.{Aksjonaerregisteroppgave, Aksjonaer}

aksjonaerer = [
  %Aksjonaer{
    navn: "Ola Nordmann",
    fodselsnummer: "12345678901",
    antall_aksjer: 100,
    aksjeklasse: "A",
    utbytte_utbetalt: 50000,
    innbetalt_kapital_per_aksje: 300
  }
]

oppgave = %Aksjonaerregisteroppgave{
  selskap: selskap,
  regnskapsaar: 2025,
  aksjonaerer: aksjonaerer
}

:ok = Wenche.Aksjonaerregister.valider(oppgave)
xml = Wenche.Aksjonaerregister.generer_xml(oppgave)

System User Setup (One-time)

By default, the system user requests rights for årsregnskap and aksjonærregisteroppgaven. The skattemelding scope is not included by default because systemic submission requires being a registered revisor or regnskapsfører. Enable it explicitly via the :features option if you have the appropriate authorization.

# Get admin token
{:ok, admin_token} = Wenche.Maskinporten.get_admin_token(config)

# Register system with default rights (årsregnskap + aksjonærregister)
{:ok, _} = Wenche.Systembruker.registrer_system(admin_token, vendor_orgnr, client_id,
  name: "my_system", description: %{"nb" => "Mitt system", "nn" => "Mitt system", "en" => "My system"})

# Or include skattemelding scope (requires revisor/regnskapsfører authorization)
{:ok, _} = Wenche.Systembruker.registrer_system(admin_token, vendor_orgnr, client_id,
  name: "my_system", description: %{"nb" => "...", "nn" => "...", "en" => "..."},
  features: [:skattemelding])

# Create system user request for an organization
{:ok, request} = Wenche.Systembruker.opprett_forespoersel(admin_token, vendor_orgnr, org_nummer,
  name: "my_system")

# Check approval status
{:ok, status} = Wenche.Systembruker.hent_forespoersel_status(admin_token, request["id"])

# List all approved system users
{:ok, users} = Wenche.Systembruker.hent_systembrukere(admin_token, vendor_orgnr,
  name: "my_system")

License

MIT — see LICENSE.

This project is an Elixir port of the Python tool Wenche, originally licensed under the MIT License.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages