* feat(invoices): implement öresavrundning logic and next invoice number preview - Added `getDisplayTotal` utility to handle rounding for SEK invoices based on company settings. - Updated `InvoicesPage` to utilize the new rounding logic when displaying totals. - Introduced `peek_next_invoice_number` function to allow previewing the next invoice number without consuming the sequence. - Modified invoice number generation to remove the year prefix and prevent truncation of numbers exceeding three digits. - Enhanced tests for invoice number generation and rounding functionality to ensure correctness. - Updated PDF template to reflect new rounding logic for totals and display appropriate values. - Adjusted company switcher to hide options in sandbox mode. - Improved error handling and logging in sandbox seeding process. * fix(skatteverket): remove unused scope labels from SCOPE_LABELS and DEFAULT_SCOPES
40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
import type { Invoice, CompanySettings } from '@/types'
|
|
|
|
type InvoiceTotalShape = Pick<Invoice, 'total' | 'currency'>
|
|
type CompanyRoundingShape = Pick<CompanySettings, 'ore_rounding'>
|
|
|
|
export interface DisplayTotal {
|
|
/** Total to render to the user (rounded if öresavrundning applies, raw otherwise). */
|
|
displayed: number
|
|
/** displayed - raw total. Zero when rounding does not apply or the total is already an integer. */
|
|
roundingDelta: number
|
|
/** True when both the company setting is on, currency is SEK, and there are öre to round. */
|
|
applies: boolean
|
|
}
|
|
|
|
/**
|
|
* Single source of truth for öresavrundning display logic. Mirrors the rule
|
|
* baked into the PDF template since day one: only SEK invoices, only when
|
|
* the company has the setting enabled, and only when there's actually a
|
|
* non-integer total to round. The helper centralizes the rule so the list,
|
|
* detail page, and PDF cannot drift apart.
|
|
*/
|
|
export function getDisplayTotal(
|
|
invoice: InvoiceTotalShape,
|
|
company: CompanyRoundingShape | null | undefined,
|
|
): DisplayTotal {
|
|
const enabled = company?.ore_rounding ?? true
|
|
if (!enabled || invoice.currency !== 'SEK') {
|
|
return { displayed: invoice.total, roundingDelta: 0, applies: false }
|
|
}
|
|
const rounded = Math.round(invoice.total)
|
|
if (rounded === invoice.total) {
|
|
return { displayed: invoice.total, roundingDelta: 0, applies: false }
|
|
}
|
|
return {
|
|
displayed: rounded,
|
|
roundingDelta: Math.round((rounded - invoice.total) * 100) / 100,
|
|
applies: true,
|
|
}
|
|
}
|