API

Guide 4 af 8

Beløb

Hvert beløb i API'en er et heltal i mindste enhed plus en valutakode. Aldrig en decimal, aldrig et flydende tal, aldrig en formateret streng.

{
  "id": "9c1f7b30-2a44-4d6e-b1c8-5e0a3f7d9214",
  "invoice_number": "2026-0184",
  "status": "sent",
  "subtotal": 1299000,
  "vat_amount": 324750,
  "total": 1623750,
  "paid_amount": 0,
  "outstanding": 1623750,
  "vat_rate": 0.25,
  "currency": "DKK"
}

total er 1623750. Med currency: "DKK" er det 16.237,50 kr.

Konverteringen

Levano er DKK, og DKK har to decimaler, så en øre er en hundrededel af en krone:

kroner = øre / 100
øre    = kroner * 100

Skal beløbet vises, formaterer du på visningstidspunktet og gemmer stadig heltallet:

$kroner = number_format($faktura['total'] / 100, 2, ',', '.');   // "16.237,50"
const kroner = new Intl.NumberFormat('da-DK', {
	style: 'currency',
	currency: invoice.currency,
}).format(invoice.total / 100); // "16.237,50 kr."

Brug altid currency fra svaret frem for at hardkode DKK. Feltet er der fordi det er en del af kontrakten, og en integration der antager DKK går i stykker den dag den ikke er det.

Hvorfor ikke decimaler

Fordi binære flydende tal ikke kan repræsentere de fleste decimale brøker. 0.1 + 0.2 er 0.30000000000000004 i enhver IEEE 754-implementering, og det gælder JavaScript, Python, PHP og Go lige meget.

I et bogholderi bliver den slags til afstemninger der ikke går op med én øre, og en øre der mangler er lige så meget arbejde at finde som tusind kroner. Heltal har ingen af de problemer: 129900 + 24500 er 154400, hver gang, i alle sprog.

Det gælder ikke kun på ledningen. Gem beløbet som et heltal i din egen database også. Læser du det ind i en float for at gemme det som DECIMAL(10,2), har du genindført præcis det problem heltallet skulle fjerne.

Undgå det klassiske par af fejl

Rund ved kanten, ikke undervejs. Skal du lægge moms på, så regn i heltal og rund én gang til sidst, ikke ved hver linje.

// Rigtigt: én afrunding, på summen
$moms = (int) round($subtotalØre * $momssats);

// Forkert: parse til float, regn, gem tilbage. To tabsgivende skridt.
$moms = (float) $subtotal * $momssats;

Parse ikke gennem en float. (int) ((float) '1299.00' * 100) giver 129899 på nogle input, fordi den nærmeste float til 1299.00 kan ligge lige under. Kommer beløbet fra en tekststreng, så parse det som decimaltal med vilkårlig præcision, eller endnu bedre: lad være med at have en decimalstreng i første omgang.

Felter der ikke er beløb

To felter i nærheden ser ud som beløb og er det ikke:

  • vat_rate er en decimalbrøk: 0.25 er 25 procent. Den er ikke i mindste enhed, og den er det ene sted decimaltal er rigtige.
  • duration_minutes på en tidsregistrering er minutter, ikke timer og ikke øre.

Tidsregistreringer har derimod både hourly_rate og amount, og de er begge i øre.

Momsbeløb er beregnet, ikke udledt af dig

vat_amount på en faktura er det tal Levano faktisk har bogført, inklusive momszone og eventuelle omvendt betalingspligt-regler. Genberegn det ikke som subtotal * vat_rate og skriv resultatet i dit eget system: de to kan afvige lovligt, og fakturaen er facit.