...
The endpoint returns a JSON array of invoice objects (no wrapper). Each object contains a nested `Info` array of supplementary key/value (or title/value) entries.
| Code Block |
|---|
[
{
"Id": <value>, // Composite: $"<InstNr>-<ForbnNr>-<UdebNr>-<Id>", // Data: composite of{InstNr}-{ForbnNr}-{UdebNr}-{Id}" = AKOND.INSTNR + FORBNR + UDEBNR + REGNINGNR (REGNINGNR cast to varchar AS Id)
"ServiceId": -FORBNR-UDEBNR-REGNINGNR
"ServiceId": <value>, // Data: AUDEBFORS.FORSYNINGSART (+AFREGNTYPE) via ForsyningsartMapper. El-only join => in practice "consumption_trade", or null (see limitations).
"InvoiceDate": <value>, // Data: AUDEBFORSAKOND.FORSYNINGSARTBILAGSDATO (+ AKOND.AFREGNTYPE for KØL) via ForsyningsartMapper; null for unmapped/unknown/null supply typesDateTimeOffset, UTC offset 0) | null
"DueDate": "2026-01-31T00:00:00+00:00", <value>, // Data: AKOND.FORFDATO (DateTimeOffset, UTC offset 0); | null if NULL
"InvoiceDatePeriod": "2026-01-01T00:00:00+00:00" <value>, // Derived from StartDate/EndDate month-span: "1month"/"2month"/"3month"/"6month"/ Data: AKOND.BILAGSDATO (DateTimeOffset, UTC offset 0); null if NULL"12month" (defaults to "1month" when dates missing or other span)
"PeriodStartDate": <value>, // Data: "1month",AKOND.DATOFRA (DateTimeOffset, UTC offset 0) | null
"EndDate": <value>, // Data: derived from AKOND.DATOFRA/DATOTIL month span (1/2/3/6/12month); defaults "1month" if either date null or span unmatchedDateTimeOffset, UTC offset 0) | null
"StartDateRemainingAmount": <value>,// Data: running account saldo "2026-01-01T00:00:00+00:00", (SUM of AKOND.KR up to this line) - NOT a per-invoice remaining figure.
"TotalAmount": <value>, // Data: AKOND.DATOFRAKR (DateTimeOffset, UTC offset 0); null if NULLdecimal) | null
"EndDateInvoiceStatus": <value>, // Derived (see status "2026-01-31T00:00:00+00:00", // Data: AKOND.DATOTIL (DateTimeOffset, UTC offset 0); null if NULLlogic below): one of cancelled / credited / paid / overdue / unpaid non-empty BDOC.PAYLOAD exists, else "missing"
"RemainingAmount": 0.00, // Flat supplementary list; mostly DUPLICATES the structured fields above, as strings/dates.
"Info": [ // Data: AKOND.KR (TotalAmount) - PBSI PaidAmount; null unless BOTH present
"TotalAmount": 1953.12,
{ "Key": "invoiceNumber", "Value": <Id> }, // Data: AKOND.REGNINGNR (string)
{ "Key": "invoiceDate", "Value": <InvoiceDate> },// Data: AKOND.KR
BILAGSDATO (DateTimeOffset|null)
{ "InvoiceStatusKey": "paiddueDate", "Value": <DueDate> }, // Data: AKOND.FORFDATO (DateTimeOffset|null)
{ "Key": "totalAmount", "Value": <TotalAmount> },// Data: derivedAKOND.KR .ToString(see MapInvoiceStatus); one| of unpaid/paid/partly_paid/overdue/cancelled/credited only
null
{ "InvoiceTypeKey": "period", "pdf", "Value": <Period> }, // Derived period string
{ "Key": "remainingAmount", "Value": <Saldo> }, // Data: derivedrunning from HasPdf EXISTS check -> "pdf" or "missing" ("html" never produced)
saldo .ToString() | null
{ "Key": "status", "InfoValue": [ <InvoiceStatus> },// Derived status string
{ "Key": "invoiceNumberdescription", "Value": "<REGNINGNR>"<Tekst> }, // Data: AKOND.REGNINGNRTEKST
(as Id) — NOTE: raw value, NOT the composite top-level Id
{ "Key{ "Title": "invoiceDateTekst", "Value": "<offset>"<Tekst> }, // Data: AKOND.BILAGSDATOTEKST (DateTimeOffset)DUPLICATE orof nulldescription,
{ "Key": "dueDate", "Value": "<offset>" }, // Data: AKOND.FORFDATO (DateTimeOffset) or null
{ "Key": "totalAmount", "Value": "1953.12" }, // Data: AKOND.KR (.ToString(), so a STRING here// vs decimal atsent topvia level)
{ "Key": "period",Title (untranslated) not Key)
"Value"// NOTE: "1monthocr" },key is NOT emitted (source field not yet // Data: same derivation as top-level Periodidentified - TODO)
{ "Key": "remainingAmount", "Value": "0.00" }, // Data: TotalAmount - PaidAmount (.ToString(); null unless both present)
{ "Key": "status", "Value": "paid" }, // Data: same derivation as top-level InvoiceStatus
{ "Key": "description", "Value": "<Tekst>" }, // Data: AKOND.TEKST
{ "Title": "Tekst", "Value": "<Tekst>" } // Data: AKOND.TEKST (DUPLICATE of description; uses Title (untranslated) instead of Key)
// "ocr" -> Not mapped (TODO: source field in SonWin not yet identified)
]
}
] |
Field origin detail (top-level BrightInvoice):
]
}
] |
Field origin detail (top-level BrightInvoice):
- Id
- Id <- composite "{InstNr}-{ForbnNr}-{UdebNr}-{Id}" (Id = REGNINGNR)
- ServiceId <- ForsyningsartMapper.MapToBrightServiceType(ForsyningsArt, AfregnType)
- DueDate <- AKOND.FORFDATO
- InvoiceDate <- AKOND.BILAGSDATO
- Period <- MapPeriod(DATOFRA, DATOTIL)
- StartDate <- AKOND.DATOFRA
- EndDate <- AKOND.DATOTIL
- RemainingAmount <- AKOND.KR - PBSI PaidAmount (null unless both present)
- TotalAmount <- AKOND.KR
- InvoiceStatus <- MapInvoiceStatus(row).GetDisplayName()
- InvoiceType <- MapInvoiceType(row) ("pdf" if HasPdf else "missing") Info
- < - list assembled in the mapper (see above)
- - composite "{InstNr}-{ForbnNr}-{UdebNr}-{Id}" (Id = REGNINGNR)
- ServiceId <- ForsyningsartMapper.MapToBrightServiceType(ForsyningsArt, AfregnType)
- DueDate <- AKOND.FORFDATO
- InvoiceDate <- AKOND.BILAGSDATO
- Period <- MapPeriod(DATOFRA, DATOTIL)
- StartDate <- AKOND.DATOFRA
- EndDate <- AKOND.DATOTIL
- RemainingAmount <- AKOND.KR - PBSI PaidAmount (null unless both present)
- TotalAmount <- AKOND.KR
- InvoiceStatus <- MapInvoiceStatus(row).GetDisplayName()
- InvoiceType <- MapInvoiceType(row) ("pdf" if HasPdf else "missing")
- Info <- list assembled in the mapper (see above)
INVOICE STATUS LOGIC (MapInvoiceStatus, first match wins):
1. KORTSTATUS = 99 InvoiceStatus derivation (MapInvoiceStatus), in order:
1. KORTSTATUS == 99 -> "cancelled"
2. TotalAmount (KR) < 0 -> "credited"
3. SettlementDate (UDLIGNDATO) set -> "paid" (regardless of PaidAmount)
4. PaidAmount >= TotalAmount -> "paid"
5. DueDate < today (and not fully paid)-> "overdue"
6. PaidAmount > 0 -> "partly_paid"
7. otherwise -> "unpaidcancelled"
(collection / reminder / deferred_* / investigation / paid_out are never returned — TODO.)
PaidAmount source: OUTER APPLY over Sonlinc.AKOND where TARIFART='PBSI' for the same INSTNR+FORBNR+UDEBNR+REGNINGNR+FIRMANR; SUM(KR*ANTAL)*-1
(PBSI rows are negative), ISNULL(...,0). Not exposed as its own response field — only feeds RemainingAmount and the status derivation.
confirmed cancelled)
2. TotalAmount (KR) < 0 -> "credited" (credit note)
3. UDLIGNDATO set OR running saldo <= 0 -> "paid" (account square through this line; balance-forward)
4. DueDate (FORFDATO) in the past -> "overdue"
5. otherwise -> "unpaid"
('partly_paid' is intentionally never produced.)
PERIOD LOGIC (MapPeriod): month span = (EndDate - StartDate) in whole months;
- 2→"2month"
- 3→"3month"
- 6→"6month"
- 12→"12month"
- anything else (incl. missing dates or 1-month) -> "1month".
(PBSI rows are negative), ISNULL(...,0). Not exposed as its own response field — only feeds RemainingAmount and the status derivation.DbInvoice columns selected but NOT surfaced as their own response field:
- AfregnType (AKOND.AFREGNTYPE) used only by ServiceId (KØL -> cooling)
- KortStatus (AKOND.KORTSTATUS) used only by status (== 99 -> cancelled)
- KortType (AKOND.KORTTYPE) selected, currently unused in mapping
- SettlementDate (AKOND.UDLIGNDATO) used only by status
- CollectiveBillNr (AKOND.SAMLREGNINGNR) selected; also used as a WHERE filter
- HasPdf (EXISTS check) used only by InvoiceType
- PaidAmount (PBSI sum) used by RemainingAmount + status