'use client' import { useTranslations } from 'next-intl' import { X } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { formatCurrency } from '@/lib/utils' import { computeInstallmentAmounts, countCalendarMonths, } from '@/lib/bookkeeping/accruals/compute' import { shouldShowK2AccrualHint } from '@/components/bookkeeping/accrual-k2-hint' import type { AccrualDirection } from '@/types' export interface AccrualFormValue { start: string end: string balanceAccount: string } // The statutory BAS interim accounts per direction: a fixed list reads // better than a full account combobox and mirrors the DB CHECK (17xx/29xx). const BALANCE_ACCOUNT_OPTIONS: Record> = { expense: [ { value: '1710', label: '1710 Förutbetalda hyreskostnader' }, { value: '1720', label: '1720 Förutbetalda leasingavgifter' }, { value: '1730', label: '1730 Förutbetalda försäkringspremier' }, { value: '1740', label: '1740 Förutbetalda räntekostnader' }, { value: '1790', label: '1790 Övriga förutbetalda kostnader' }, ], revenue: [ { value: '2970', label: '2970 Förutbetalda intäkter' }, { value: '2971', label: '2971 Förutbetalda hyresintäkter' }, { value: '2972', label: '2972 Förutbetalda medlemsavgifter' }, { value: '2979', label: '2979 Övriga förutbetalda intäkter' }, ], } /** * Per-line periodisering panel for the invoice editors: service period + * interim balance account + a live "N månader × X kr" preview. The parent * owns the toggle; this renders only while periodisering is active on the * line. VAT is never affected: only the net amount is deferred. */ export default function AccrualPeriodControl({ direction, amount, currency, exchangeRate, value, onChange, onRemove, idPrefix, }: { direction: AccrualDirection /** Net line amount (ex VAT), in `currency`: drives the preview and the K2 hint. */ amount: number /** ISO code of `amount`. Missing is treated as SEK (the editors' default). */ currency?: string | null /** * SEK per unit of `currency`. Only the supplier-invoice form has one; the * customer-invoice editor carries no rate, so the K2 hint stays hidden on * its foreign-currency lines instead of comparing kronor to euros. */ exchangeRate?: number | null value: AccrualFormValue onChange: (next: AccrualFormValue) => void onRemove: () => void idPrefix: string }) { const t = useTranslations('accruals') let preview: string | null = null let previewInvalid: string | null = null if (value.start && value.end) { if (value.end < value.start) { previewInvalid = t('preview_invalid_period') } else { try { const months = countCalendarMonths(value.start, value.end) if (months < 2) { previewInvalid = t('preview_min_months') } else if (amount > 0) { const amounts = computeInstallmentAmounts(amount, months) preview = t('preview', { months, amount: formatCurrency(amounts[0], currency || 'SEK'), }) } } catch { previewInvalid = t('preview_invalid_period') } } } // K2's 5 000 kr vasentlighetsgrans is measured in kronor, and it is a // simplification the company may use, not an obligation. So when the line // is in a foreign currency and no rate is available, show nothing at all // rather than compare the raw foreign amount against a SEK threshold. const showK2Hint = shouldShowK2AccrualHint({ amount, currency, exchangeRate }) return (
{t('panel_title')}
onChange({ ...value, start: e.target.value })} />
onChange({ ...value, end: e.target.value })} />
{(preview || previewInvalid) && (

{previewInvalid ?? preview}

)} {showK2Hint && (

{t('k2_hint')}

)}
) }