* fix(vat): danstillställningar 25% to 6% from 2026-07-01 in guidance and suggestions (#1483)
From 2026-07-01 tillträde till danstillställningar is 6% VAT, aligned with
other cultural events. 6% was already a supported rate; every guidance
surface was silent on the change, so a dance-event customer plausibly got
25% suggested.
- swedish-vat skill: rate table row + a July 2026 change note under rate
misclassification (cutover date, mixed-venue split vs 25% alcohol), and
the 2631 account table mentions dance admission
- revenue_reduced_6 descriptor: dans/danstillstallning/dansband/entre
keywords and updated description, which flows to both the UI suggestions
and gnubok_suggest_categories via findMatchingTemplates
- atom seed regenerated; the generator now emits a version-downgrade guard
on the ON CONFLICT so two branches each carrying a full 108-atom seed can
no longer clobber each other's atom bodies depending on merge order
Closes#1483
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* review(vat): drop overbroad entré keyword, cite SkU25 and the prepayment rule
Two findings from the Swedish compliance review bot:
- bare 'entré' matched generic admission that is not always reduced-rate;
the dance-specific keywords stay
- the rate claim now cites its primary sources (riksdagen 2025/26:SkU25,
Skatteverket halvårsskiftet 2026) and records that tickets sold and paid
before 2026-07-01 keep 25%
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* review(vat): admission-specific keywords only, explicit cutoff wording, seed rebuilt post-merge
- bare 'dans' also matched dance courses and artist fees; keep
danstillställning/dansband and add danskväll
- rate table states the boundary explicitly: 25% through 30 June 2026
- atom seed regenerated from the tree that now includes #1489, emitted as
20260810121001
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>
Cross-checked all 86 booking templates (26 packs + 60 static) against BAS 2026.
The pack catalogue is clean; the static registry had three defects, none of
which any existing check could see.
Two named accounts that do not exist in BAS 2026, so account-backfill.ts cannot
seed them and EVERY booking through them failed with AccountsNotInChartError:
vehicle_parking 5614 -> 5619 the BAS 561x run skips 5614; 5619 is the
sibling "Ovriga kostnader for personbilar"
it_cloud_hosting 5421 -> 5420 BAS has no 5421; both sibling SaaS templates
already use 5420 Programvaror
One that posted successfully to the wrong account, which is worse because
nothing complained:
travel_hotel 5820 -> 5830 5820 is Hyrbilskostnader. The Hotell template
filed hotel nights under car hire, balanced,
and looked fine. 5830 is "Kost och logi".
An existing test asserted the 5820 behaviour, so the bug was pinned as
expected output. That assertion now reads 5830 and carries the reason.
Adds template-accounts-exist.test.ts so the 5614 class cannot return: every
account any static template names must resolve in BAS 2026, account numbers
must be strings, and a VAT-bearing purchase may not debit equity or revenue.
That last check is deliberately narrow: class 1 is legitimate, equipment_capital
debits 1250 Inventarier and reclaims VAT, which is how capex is booked. Verified
the guard fails on 5614 and passes on 5619 rather than trusting it.
The pack catalogue already had this check via scripts/validate-packs.ts. The
static registry had nothing, which is how it drifted.
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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: add user locale preference to user_preferences table
- Introduced a new column 'locale' in the user_preferences table to store per-user UI language preferences.
- Added a CHECK constraint to ensure only supported locales ('sv', 'en') are allowed.
- Triggered a schema reload notification for the changes.
chore: declare CSS module support in TypeScript
- Added a declaration for CSS modules in globals.d.ts to enable TypeScript support for importing CSS files.
* feat: add Swish as an invoice payment method in company settings
* fix(currency): use transaction-date FX rate and post mall bookings in SEK (#442)
Three related defects in non-SEK transaction handling:
1. Ingest priced every transaction in a batch at today's rate because
fetchMultipleRates() was called without a date. Switch to a per-
(currency, date) fetch via fetchExchangeRate() + Promise.allSettled,
so each row stores Riksbanken's rate as of its own transaction date.
2. buildMappingResultFromTemplate emitted VAT/reverse-charge/output-VAT
lines from Math.abs(transaction.amount) — the source-currency amount.
Mall bookings on USD/EUR transactions therefore posted the journal
entry in the wrong currency. Resolve to SEK once via resolveSekAmount()
and reuse for every line, matching what createTransactionJournalEntry
already does for the settlement line.
3. The Granska bokföring modal and the journal-entry detail page lacked
a clear audit trail. The verifikation preview now renders in SEK with
a separate original-currency subtitle in the header; the detail page
gains a dedicated "Valutaomräkning" card above Kontorader showing the
rate and original total.
Also: replace truncate with break-all on the modal description so long
UUID-style strings (e.g. BALANCE_CASHBACK-…) wrap instead of forcing
horizontal overscroll.
Tests: 4 new booking-templates cases (USD 25%, EUR fallback via
exchange_rate, EU reverse-charge on USD, output VAT on USD income) and
2 new ingest cases (per-pair fetch dedup; no fetch for SEK rows).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(currency): wire SEK conversion into SwipeCategorizationView (#442 follow-up)
Address Greptile review:
- SwipeCategorizationView rendered JournalEntryPreview without amountSek,
so the swipe flow had the same defect QuickReviewDialog had — a USD
transaction showed the verifikation in source currency labeled SEK.
Now resolves SEK once via resolveSekAmount, passes it to the preview,
and shows SEK as the headline with the original-currency subtitle
(parallel to QuickReviewDialog).
- Drop the now-dead `currency` prop from JournalEntryPreviewProps and
its remaining call site in QuickReviewDialog — the component
hardcodes 'SEK' on display, so the prop was misleading.
- Correct the misleading inline comment in ingest.ts: network failures
resolve inside fetchExchangeRate to getFallbackRate() (non-null),
so the key only stays unset on empty-observation responses or
outright rejections.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(settings): add option for company name position in invoice PDF
* feat(migrations): add backfill for VAT account labels to correct bad seed data
* feat(migrations): add backfill for VAT account labels to correct bad seed data
* fix(ui): improve accessibility for company name position toggle in PDF settings
* fix(bookkeeping): align BAS 2026 reference data with official PDF
Reconciled lib/bookkeeping/bas-data/ against the BAS 2026 v1.1 official
chart (1286 accounts). All real discrepancies fixed:
- 2089 Fond för utvecklingsutgifter: k2_excluded → true
- 8417 Räntekostnader för dold räntekompensation: k2_excluded → true
- 1250, 1260 renamed to "(Fritt konto för Inventarier, verktyg och
installationer)" — BAS 2026 freed these slots
- Periodiseringsfond 2120-2139: added year suffixes (2120 = "...2020"
etc.) and added 8 missing accounts (2121-2127, 2129) for years
2019, 2021-2027. Dropped phantom 2022/2024 prior-parser garbage.
- 4075-4078: EUland → EU-land
- 8411: förlagsoch → förlags- och
Verified: 1282 of 1286 PDF accounts match exactly after edits (remaining
4 are PDF-parser artifacts, not real data). Build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(migrations): backfill BAS 2026 account labels in chart_of_accounts
Companion to the TS reference fix. Updates existing companies' rows where
they still carry seed-data typos or generic names that don't match BAS 2026:
- 4075-4078: EUland → EU-land (hyphenation)
- 8411: förlagsoch → förlags- och (hyphenation)
- 2120, 2130-2137, 2139: rename "Periodiseringsfond" (generic, no year) to
the BAS 2026 canonical name with year suffix
Defensive: every WHERE clause matches an EXACT current value. Rows that
have been manually renamed by users — including those with a wrong year
that may reference legacy fonds from an earlier BAS numbering cycle — are
left untouched. No row is deleted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(migrations): fix wrong-year labels on Periodiseringsfond accounts
Follow-up to 20260513140000. The first backfill only renamed accounts whose
name was the generic "Periodiseringsfond" (no year). Many customers were
seeded from an older BAS numbering cycle where 2126 = "2016", 2127 = "2017",
etc. — BAS 2026 reuses those account numbers for years 2026/2027.
This migration aligns the year tag with the BAS 2026 meaning of each
account number across 2120-2127, 2129, 2130-2137, 2139. Only rows whose
name still starts with "Periodiseringsfond" are touched — customers who
renamed the account to something custom keep their name.
Verified on staging: all 18 accounts now carry BAS 2026 canonical names.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(data): update account names and descriptions for clarity and consistency
* fix(migrations): backfill account names for BAS 2026 freed accounts and refine Periodiseringsfond name matching
* feat: enhance VAT handling with reverse charge logic and supplier type support
* feat: implement VAT declaration validation rules and enhance moms box mapping
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Update BAS account catalog with comprehensive SRU codes and K2 flags
- Add currency revaluation service with tests and API route
- Add expenses page and account deletion API
- Enhance booking templates with new patterns and improved tests
- Improve transaction categorization with template picker and description matching
- Polish dashboard, onboarding, import, and transaction UIs
- Refactor year-end service for multi-step closing
- Move SRU generator to ne-bilaga, remove standalone SRU export
- Remove unused dev docs, mock data, and extension hooks
- Add invoice delivery note sequences migration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce booking template library to eliminate duplicate suggestions when
users describe transactions. Templates with identical accounting treatment
(same account + VAT) are merged, keywords consolidated, and the entire
subscriptions group is eliminated. Also includes prior work on reports,
extensions, and transaction improvements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add auto-detecting CSV parsers for Länsförsäkringar, ICA Banken, Skandia,
and Lunar. Refine SEB detection to avoid false matches. Update bank file
upload UI with new bank options and export instructions. Include booking
templates, improved AI categorization, and transaction review enhancements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>