8094f288fa1e8fbf47453fdcd90e66ea7f2dcd28
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ec27228a8e |
style: remove em/en dashes repo-wide, add CLAUDE.md rule against them (#890)
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> |
||
|
|
86c924a6e9 |
fix(dimensions): exempt system source types from account dimension rules (#888)
SIE import books three system entries through the engine (opening balances, IB resynk, the omföring adjustment for excluded vouchers) — after PR10 those passed through the rules layer, so a required rule could block an import and default/fixed rules could inject dimensions into derived historical entries. Year-end, currency revaluation and credit instruments had the same exposure. Policy now governs NEW business events only: source types in DIMENSION_RULE_EXEMPT_SOURCE_TYPES (opening_balance, import, year_end, storno, correction, credit_note, supplier_credit_note, currency_revaluation, system) skip both the draft-time apply and the commit-time assert — imported history lands verbatim (BFL 5 kap), bokslut can never be blocked by a dimension rule, and crediting an entry that pre-dates a rule always works. Operational sources (manual, bank_transaction, invoice_*, supplier_* registrations/payments, salary_payment) stay enforced. The SIE round-trip test now also covers a PR10-created custom dimension (#DIM 20) with a custom child (#UNDERDIM 25 ... 20) and a tagged line — proving user-created dims survive export → parse structurally. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
764348e99c |
feat(dimensions): PR10 advanced — custom dimensions, hierarchy, account rules, commit enforcement (#886)
* feat(dimensions): PR10 advanced — custom dimensions, hierarchy, account rules, commit enforcement The final rung of the dimensions ladder (dev_docs/dimensions_implementation_plan.md §7 row 10): - custom dimensions: POST /api/dimensions creates registry dims (next free SIE number >= 20 when omitted; explicit numbers allowed — SIE import already mints reserved ones); register gets a 'Ny dimension' dialog with a quiet Avancerat disclosure for the #UNDERDIM parent; GET now carries parent_sie_dim_no (the column + SIE round-trip existed since PR1/PR5 — this exposes it) - account_dimension_rules (migration 20260703120000): one rule per (account, dimension) — required / default / fixed, per-rule is_active, company-scoped RLS, composite FK to the registry, value-presence CHECK - enforcement, opt-in BY CONSTRUCTION (zero rules = engine byte-identical; deliberately NO settings toggle — a rule that exists but is ignored is worse than either extreme): default/fixed apply onto line bags at draft creation (fixed overwrites, default fills); required asserts at commitEntry with a Swedish MANDATORY_DIMENSION_MISSING naming every account + dimension; the bulk-book route runs the same policy before its RPC; storno/correction paths never pass through commitEntry so history always reverses regardless of policy; rule fetches fail open incl. thrown exceptions - chart of accounts: per-account Dimensionsregler section in EditAccountDialog (Krävs/Förval/Låst, value picker, pause switch), gated on the existing dimensions toggle, quiet when empty - pickers: LineDimensionFields is registry-driven (one combobox per active dimension, cached fetch, hardcoded 1/6 fallback) — every existing mount lights up custom dims with zero changes - agent briefing: per-dimension required_on_accounts/default_on_accounts so agents self-correct instead of bouncing off the policy error - rules CRUD API with existence/active/company validation and qualified DTO ids; firm_id FK deferred until the firms table lands (per plan) 39 new tests (pure-fn rules, engine enforcement, both new API surfaces, pg-real RLS/CHECK/cascade suite); full suite 6,791 green; migration replayed on a fresh container. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: renumber migration to 20260703200000 — version collision with prod The concurrent session shipped pending_operations_add_link_document_to_voucher as 20260703120000 today; the Supabase preview branch (cloned from prod) rejected the duplicate version key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: review round — auto-pick retry on collision, fail-open warnings, query schema - POST /api/dimensions retries once past a concurrent number claim when the number was auto-picked (explicit choices still 409) - every fail-open skip of the dimension-rules policy now logs a structured warning (engine draft/commit paths + bulk-book) — deliberate fail-open, but observable - GET /api/dimensions/rules validates its query through ListDimensionRulesQuerySchema instead of an inline regex Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fb3f0a9cee |
feat(dimensions): PR9 cutover — cost_center/project become GENERATED columns, dual-write removed (#870)
The dual-write window ends (dev_docs/dimensions_implementation_plan.md PR9): journal_entry_lines.cost_center/project are now GENERATED ALWAYS AS (NULLIF(dimensions->>'1'/'6','')) STORED — divergence from the bag is impossible by construction instead of by convention. - migration 20260702230000: drift pre-flight (refuses cutover on inconsistent data; prod verified 0 drift across 593k rows), column swap (DROP metadata-only + one-rewrite ADD pair), and atomic redefinition of the two SQL writers — retag_line_dimensions (SET dimensions only) and bulk_book_transactions (INSERT names the bag only) - TS writers stripped of the mirror spread: engine buildLineInserts (covers create/update/reversal), storno-service (reversal + correction), SIE import bulk insert, sandbox seed - lineDimensionColumns() removed from dimension-resolver — nothing derives mirrors in TypeScript anymore; normalizeLineDimensions + the deprecated cost_center/project INPUT aliases stay (API contract, they normalize into the bag); JournalEntryLine ROW type keeps the fields (generated columns still SELECT) - immutability carve-out unchanged BY DESIGN: its whole-row diff already subtracts dimensions/cost_center/project on both sides, which is exactly what makes it correct with generated columns (BEFORE-trigger NEW carries not-yet-recomputed mirror values) - audited every reader (v1 journal-entries, MCP query_journal filters + group_by, rc-basis-gaps) — reads are untouched; no index, view, or constraint referenced the TEXT columns, so DROP COLUMN cascades nothing - new pg suite: generated derivation, explicit-mirror-write rejection, draft-update recompute; existing retag/substrate/bulk-book suites updated to bag-only writes (their mirror assertions now exercise the generation expression) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
11126d6d56 |
feat(dimensions): PR3 tagging — voucher-form pickers, MCP dimension tools with resolve-don't-select, engine soft validation (#859)
Phase 3 of dev_docs/dimensions_implementation_plan.md. Companies with dimensions_enabled=false see zero change; existing free-text API writers keep working (validation is toggle-governed). Engine (soft validation): - validateEntryDimensions() in dimension-resolver: zero queries for untagged entries; toggle off → passthrough; toggle on → one settings fetch + two registry queries, rejects unknown dims/codes and archived values with Swedish per-code messages (DimensionValidationError, 400, details.issues). Wired into createDraftEntry + updateDraftEntry before any insert; reversal/ storno paths untouched (verbatim copies). Fails open on transient registry errors — soft validation must never block bookkeeping. MCP (agent write path): - New tools: gnubok_list_dimensions, gnubok_list_dimension_values (fuse.js fuzzy), gnubok_create_dimension_value (STAGED via pending_operations — agents never silently mint reporting values; new op type + CHECK migration + executor with duplicate-idempotency). - create_voucher/correct_entry: per-line dimensions bag + default_dimensions, resolve-don't-select server-side (code OR natural-language name; exact → fuzzy ≤0.30 with ≥0.15 runner-up margin; non-exact resolutions echoed with confidence; ambiguous → ranked candidates, no auto-create). - gnubok_get_agent_briefing gains a dimensions block (enabled, dims, top values) — omitted when registry empty. - TOOL_SCOPE_MAP entries; risk tier low for staged value creation. UI: - JournalEntryForm (manual voucher + TransactionBookingDialog embed): header "+ Kostnadsställe/Projekt" progressive disclosure (gäller alla rader with documented inheritance rule) + per-row tag popover + compact KS·PR badges; gated on dimensions_enabled. - Voucher detail: display-only dimension badges with registry-name resolution. - EditDraftEntryDialog carries line dimensions so editing a draft no longer strips tags. categorize/bulk_book dims deferred to PR7 (needs the bulk_book RPC migration). Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |