From abaab1bf8d6ce3608805c5ac38ffe194052582ac Mon Sep 17 00:00:00 2001 From: Mattsson <111893710+mattssonn@users.noreply.github.com> Date: Fri, 4 Sep 2026 23:33:48 +0200 Subject: [PATCH] fix(settings): show series names in the verifikationsserie per typ picker (#2295) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(settings): show series names in the verifikationsserie per typ picker The "Verifikationsserier per typ" selects offered bare letters A to Z while the manual verifikat dialog and the per-bankkonto picker already show the letter with its Swedish name. Use the same closed preset list here, keep any letter the company already uses so no saved value drops out, and correct the help text, which named series that contradicted the presets. Reported by a byrå partner on 2026-09-03. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_017othUwkVfteSUf5q1CxPeC * fix(settings): keep saved non-preset series selectable and name the convention Seed the extras from the saved per-type map as well as the draft, so a non-preset letter stays in the list after the user changes that row away from it (a misclick could otherwise not be undone without a reload) and a letter that lands in settings after mount is offered. Tie the help text to the Fortnox convention instead of claiming general Swedish practice: the SIE-world convention differs and the letter is a free choice. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_017othUwkVfteSUf5q1CxPeC --------- Co-authored-by: Claude Fable 5.1 --- .../VoucherSeriesPerSourceTypeForm.tsx | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/components/settings/VoucherSeriesPerSourceTypeForm.tsx b/components/settings/VoucherSeriesPerSourceTypeForm.tsx index 9d3c625f..c2cfba8b 100644 --- a/components/settings/VoucherSeriesPerSourceTypeForm.tsx +++ b/components/settings/VoucherSeriesPerSourceTypeForm.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useMemo, useState } from 'react' import { useTranslations } from 'next-intl' import { Button } from '@/components/ui/button' import { useToast } from '@/components/ui/use-toast' @@ -13,9 +13,10 @@ import { } from '@/components/settings/SettingsRows' import { cn } from '@/lib/utils' import { getErrorMessage } from '@/lib/errors/get-error-message' +import { VOUCHER_SERIES_PRESETS } from '@/lib/bookkeeping/voucher-series-resolver' import type { CompanySettings, JournalEntrySourceType } from '@/types' -const SERIES_OPTIONS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('') +const SERIES_LETTER_RE = /^[A-Z]$/ // Subset of source_types presented to the user. The DB column accepts every // JournalEntrySourceType, but several values (storno, correction, etc.) are @@ -82,6 +83,29 @@ export function VoucherSeriesPerSourceTypeForm({ settings, onSettingsUpdated }: const [isSaving, setIsSaving] = useState(false) const [showAll, setShowAll] = useState(false) + // Same closed list as the manual verifikat form and the per-bankkonto + // picker: the fixed Swedish presets, then any letter already configured in + // settings that the presets do not cover. The saved map is read alongside + // the draft so a non-preset letter stays selectable after the user changes + // that row away from it (otherwise a misclick could not be undone without + // a reload) and so a letter that lands in settings after mount is offered. + // A free A-Z list would let a typo start an undocumented series. + const seriesOptions = useMemo(() => { + const preset = new Set(VOUCHER_SERIES_PRESETS.map((p) => p.letter)) + const extras = [ + settings.default_voucher_series, + ...Object.values(settings.default_voucher_series_per_source_type ?? {}), + ...Object.values(draft), + ].filter( + (v): v is string => typeof v === 'string' && SERIES_LETTER_RE.test(v) && !preset.has(v), + ) + const uniqueExtras = Array.from(new Set(extras)).sort() + return [ + ...VOUCHER_SERIES_PRESETS, + ...uniqueExtras.map((letter) => ({ letter, label: '' })), + ] + }, [settings.default_voucher_series, settings.default_voucher_series_per_source_type, draft]) + const handleChange = (sourceType: JournalEntrySourceType, value: string) => { setDraft((prev) => ({ ...prev, [sourceType]: value })) } @@ -142,9 +166,9 @@ export function VoucherSeriesPerSourceTypeForm({ settings, onSettingsUpdated }: onChange={(e) => handleChange(key, e.target.value)} className="font-mono" > - {SERIES_OPTIONS.map((letter) => ( - ))} @@ -157,7 +181,7 @@ export function VoucherSeriesPerSourceTypeForm({ settings, onSettingsUpdated }: return ( {alwaysVisible.map((entry, i) => // The last always-visible row sits right above the fold toggle: