Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.
Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.
Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.
Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat(bookkeeping): Ny verifikat modal, ledger-style list, SIE no-underlag exemptions
Verifikat UX
- "Ny verifikat" opens in a modal (NewJournalEntryDialog) instead of an inline tab;
the review step renders inline in the dialog rather than stacking a second dialog.
- JournalEntryForm: konteringsrader are the focus, with a compact pre-filled metadata
bar (datum/serie/text/valuta/period) on top; verifikationstext auto-fills from the
first row's account.
- JournalEntryList: belopp shown on collapsed rows; expanded view is an aligned
Konto/Benämning/Debet/Kredit table.
SIE imports no longer flood "Att hantera: saknade underlag"
- Import gains an opt-in (off by default) toggle to mark imported verifikat as "Inget
underlag krävs"; a "Rekommenderas vid migrering" badge nudges it for historical years.
- Multi-select batch-mark in the list for selective cleanup.
- Filter-scoped bulk mark (POST /api/bookkeeping/no-doc-required/bulk-missing): marks
every missing-doc verifikat matching the active filters across all pages, with a
dry_run count to confirm scope — the scalable remedy for a post-import flood.
- Shared helper markEntriesNoDocRequired + per-entry batch route.
Tests: no-doc helper, batch route, bulk-missing route.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(bookkeeping): address PR #698 review findings
- JournalEntryForm: restore the explicit "no underlag" acknowledgement in the
modal's inline review. When no document is attached, the confirm button reads
"Bokför utan underlag" (BFL 5 kap 6-7 §§), equivalent to the blocking dialog the
non-bare flow shows — the bare path no longer posts behind only a passive banner.
- batch no-doc route: guard the ownership query with source_type IN
NEEDS_DOC_SOURCE_TYPES so a crafted request can't exempt non-document-requiring
entries (defense in depth on top of company + posted scoping).
- bulk-missing route: resolve doc/exemption status by querying only the candidate
ids (chunked) instead of loading the company's full document_attachments and
journal_entry_no_doc_required tables into memory — data minimisation + bounded
memory for large migrations (the most-repeated reviewer finding).
Triaged as non-issues (left as-is): partial-import exemption (gated on
result.success == zero errors), reason write-back (sidecar row is FK-linked and
carries the reason), and "bulk-exempting manual entries" (consistent with the
existing per-entry NoDocRequiredToggle). No DB migration — reuses the existing
journal_entry_no_doc_required table.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(bookkeeping): centralize bulk-missing date/series validation in Zod
Move the ISO-date and verifikationsserie format checks into the Zod schema so
malformed input is rejected with a clean 400 instead of being silently nulled
(or, for a shaped-but-invalid date, throwing a 500 via fetchAllRows). The date
refinement rejects values like 9999-99-99 / 2026-02-30 that a bare
/^\d{4}-\d{2}-\d{2}$/ regex lets through. Addresses the PR #698 reviewer nit on
split schema-vs-runtime validation. +2 route tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>