The first production run of the row-completion pass (#2291) wrote 345
Profilio invoices whose rows summed to the invoice GROSS with 25 % VAT
computed on top, beside a header (Net / TotalVAT) that was right. Fortnox
prices an invoice either excluding or including VAT and says which with
the invoice-level VATIncluded flag; the mapper had always read the row
Total and Price as net. Every such row set is 1.25 x its header net, to
the öre, across all 345.
- lib/providers/fortnox/mapper.ts: netOfVat() divides row Total and Price
by (1 + rate) when VATIncluded is true; TotalExcludingVAT and
PriceExcludingVAT are preferred when the payload carries them. A row
without a rate cannot be split and keeps its amount.
- complete-invoice-lines.ts: rows whose net or VAT disagree with the header
the same payload established by more than 1 kr are reported as
rowsMismatch and left untouched. Öresavrundning stays inside the
tolerance; VAT-inside rows, header-level freight and discounts do not.
Rows that contradict their own header are worse than no rows.
- Cron summary carries rowsMismatch.
None of the 345 is open or booked; a separate repair removes today's rows
for them so the fixed pass refills them.
Claude-Session: https://claude.ai/code/session_01DG5aYcshzKJ1EA7PPhGtVf
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>