Files
accounted/lib/invoices/display.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

61 lines
2.3 KiB
TypeScript

export const INVOICE_NUMBER_DRAFT_LABEL = '(Utkast)'
export function invoiceNumberDisplay(value: string | null | undefined): string {
return value ?? INVOICE_NUMBER_DRAFT_LABEL
}
/**
* The number to show for an invoice. Self-billing invoices we received carry
* the counterparty's number in `external_invoice_number` (our own
* `invoice_number` is null by design), so fall back to it before the draft
* label.
*/
export function invoiceDisplayNumber(invoice: {
invoice_number?: string | null
external_invoice_number?: string | null
}): string {
return invoice.invoice_number ?? invoice.external_invoice_number ?? INVOICE_NUMBER_DRAFT_LABEL
}
/**
* The number a user must type to confirm crediting an invoice. Regular
* invoices confirm with their own `invoice_number`; self-billed invoices have
* `invoice_number` null by design, so the counterparty's
* `external_invoice_number` (the number shown everywhere in the UI) is the
* one to type. Null when the invoice carries no number at all: the credit
* flow must stay disabled then.
*/
export function creditConfirmNumber(invoice: {
invoice_number?: string | null
external_invoice_number?: string | null
}): string | null {
return invoice.invoice_number ?? invoice.external_invoice_number ?? null
}
/**
* True when an invoice line should render as a pure text row: description
* only, no quantity/unit/price/amount columns. Explicit text rows
* (line_type 'text') always qualify; so do product rows carrying no amounts
* at all (quantity and unit price both zero/absent). Users write free-text
* lines via the article picker's "Egen rad (fri text)" and leave antal/pris
* at zero; printing "0 / 0,00 SEK / 0,00 SEK" on those is noise (issue #1053).
*/
/**
* Collapse a possibly multi-line description to one line.
*
* Line descriptions may carry line breaks (the editor allows them so the PDF
* breaks where the user wants). Every consumer that is a single-line field by
* format (Peppol cbc:Name, journal entry and SIE texts) goes through this.
*/
export function toSingleLine(text: string | null | undefined): string {
return (text ?? '').replace(/\s+/g, ' ').trim()
}
export function isTextLikeLine(item: {
line_type?: 'product' | 'text' | null
quantity?: number | null
unit_price?: number | null
}): boolean {
return item.line_type === 'text' || (!item.quantity && !item.unit_price)
}