fix(suppliers): stop requiring standardkonto that was never meant to be required (#1636)
* fix(suppliers): stop requiring standardkonto that was never meant to be required The supplier form initializes every optional field to '' and sent them as-is, while CreateSupplierSchema validates default_expense_account with the 4-digit account rule behind .optional(): an empty string is a present string, so saving a supplier with the field untouched failed with "Kontonummer måste vara 4 siffror" even though the field carries no required mark (reported by Björn with a screen recording; the edit page failed the same way for any supplier without a default account). Schemas now own the normalization, split by verb: on create '' becomes undefined (key dropped, column NULL), on update '' becomes null, because update routes pass fields straight into .update() where undefined means "leave unchanged" and clearing must actually write NULL. Email gets the same treatment and the form's old client-side email strip is removed; stripping empty strings client-side would break exactly the clear path. The free-text Standardkonto input is replaced with the shared AccountCombobox (browsable list filtered to cost classes 4-7, the same rule the agent-path expenseAccountField enforces), with the selected account name shown under the field and a clear button when set. Standardkonto itself stays optional: it only prefills supplier-invoice lines and the ledger-context suggestion covers the empty case. Verified end to end against the running app: saving a supplier without a default account succeeds on the update path, and the combobox search/select/clear cycle works inside the create dialog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(api-spec): render preprocess pipes by output side, required-ness by undefined-acceptance The minimal Zod-to-JSON-schema walker described every pipe by its input side. For .transform() that is right (the caller sends the input), but z.preprocess() is the mirror image: the callable sits on the input side, so the supplier schemas' new empty-string normalization rendered email and default_expense_account as required untyped fields in the OpenAPI spec and the generated accounted-api skill. Describe the output side when the input is a transform. Required-ness now derives from schema.safeParse(undefined) instead of a top-level discriminator check: a field may be omitted exactly when the schema accepts undefined. Besides the preprocess pipes, this corrects several fields the old check misrendered as required (z.unknown() bodies, union-with-empty-string settings fields, preprocessed personal_number), so the regenerated skill references only flip required to optional where runtime validation already allowed omission. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
Jakob Wennberg
parent
2eb3441244
commit
25524e1df4
@@ -1025,3 +1025,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-08-17] Full-archive direct download resurfaced as an ImportRow + small centered dialog on /import's Exportera tab (row "Komplett arkiv", hash #full-archive), not by re-mounting the orphaned components/settings/BackupDownloadForm.tsx: the form was pre-frame card styling with a duplicate cloud-backup section, while the export tab's existing SIE dialog sets the house pattern (ImportRow -> sm:max-w-md dialog). Its logic (estimate, 413 handling, last-download stamp) ported into components/import/FullArchiveDialog.tsx; the orphan and its dead settings_backup_download i18n namespace deleted. The dialog reuses FiscalYearSelector despite design.md's "legacy, no new uses" line: FyPicker is a toolbar context chip, and the SIE dialog in the same file already uses FiscalYearSelector for the identical dialog-form slot, so matching it beats introducing a third pattern. Row gated to owner/admin because GET /api/reports/full-archive enforces that role server-side; showing members a download that can only 403 helps nobody.
|
||||
[2026-08-17] Article picker now overwrites the line's ROT/RUT (deduction_type + work_type) from the article's housework_type, INCLUDING clearing it when the article has none: article-defines-the-row is the established applyArticle semantic (description/price/unit already overwrite), and keeping a RUT flag when switching a row to a material article would silently claim a deduction on material (HUSFL labor-only rule). Kundkort personnummer prefill is a server-side fallback in buildInvoiceWriteData (typed > stored draft > kundkort), never a client prefill: customers.personal_number reaches the browser only as ciphertext/mask by design, so the editor just relaxes the required-mark and says where the number will come from.
|
||||
[2026-08-17] Arcim's "610 bilagor i Hela historiken men 100 i räkenskapsåret" in the full-archive dialog is NOT a pagination bug: verified against prod, exactly 100 documents are linked to posted vouchers in the single (extended) fiscal year and 510 are unlinked inbox/receipt docs, which scope=all includes by design (same split as cloud backup's year-ZIPs vs Grunddata.zip). Kept the semantics, fixed two things instead: estimateArchiveSize's period branch ran one unpaginated read with one flat IN() over every entry id (undercounts past the PostgREST row cap, URL blowup past ~a few hundred ids) -> now CHILD_FK_CHUNK-chunked and fetchAllRows-paginated like writeDocuments already was; and the dialog now states per scope which document set is counted, so the gap reads as intent, not as a bug.
|
||||
[2026-08-17] Supplier standardkonto empty-string fix lives in the API schemas, split by verb: '' normalizes to undefined on create (key dropped, column NULL) but to null on update, because update routes pass validated fields straight into .update() where undefined means "leave unchanged"; without the null mapping a cleared standardkonto/e-post would silently never clear. Client keeps sending '' as-is (the old email-strip hack removed), since stripping client-side would break exactly that clear path. The field itself became an AccountCombobox filtered to cost classes 4-7 (matches the agent-path expenseAccountField rule); other 4-digit numbers stay typeable, and the API still enforces format only. Standardkonto stays optional: it only prefills supplier-invoice lines, and the ledger-context suggestion covers the empty case, so requiring it (what the bug accidentally did) is wrong for the target user.
|
||||
|
||||
Reference in New Issue
Block a user