c90ccc9c429277299fd4ecec0652cd8b6fa080be
12
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
26e29f47bc |
feat(company): ideell förening as a third legal form, behind a flag (#2072 step 1) (#2423)
* feat(company): ideell förening as a third legal form, behind a flag (#2072 step 1) Why the problem occurred: the legal form was modelled as a binary flag in ~300 files. `EntityType` was a two-member union, but nothing dispatched on it exhaustively: 28 sites defaulted `?? 'enskild_firma'` (invoice, categorize, match, stripe, invoice-inbox) or `?? 'aktiebolag'` (year-end, bokslut, MCP), and every form-dependent choice was an `=== 'aktiebolag' ? A : B` ternary. Widening the union compiled everywhere and changed nothing, so a förening would have booked as an enskild firma in the app and as an aktiebolag in bokslut and MCP, with no error anywhere. The lookup refused föreningar at the door (mapEntityType returned null), which is what the tester hit. What was removed or simplified: the silent defaults. One module, lib/company/entity-type.ts, now holds the list (ENTITY_TYPES), the parser (never defaults), the resolver (settings hint, then companies.entity_type, then throw) and `byEntityType`, whose Record arms make the compiler refuse the next widening until each site has an answer. The form-dependent facts (closing account, owner settlement account, calendar-year lock, default method, K1/K2 label, personnummer vs 16-prefix) live there once instead of in the ternaries. On the SQL side supported_entity_types() replaces four copies of the literal list in the create RPCs. Why this shape and not the proposed one: the tracker asked for the enum widening plus a chart; that alone was the dangerous version (compiles, books wrong). Bundling stiftelse was considered and dropped: identical plumbing but no chart block. Creation sits behind NEXT_PUBLIC_IDEELL_FORENING_ENABLED so the CHECK, RPCs and seed can ship now and the first partner is switched on without a migration; the flag goes when Phase 2 (packs, INK3, årsbokslut, Swish) lands on the tracker. Domain choices (DECISIONS.md 2026-09-08, verify with an accountant before Phase 2): result closes to 2069 with 2068 as prior-year carry; no owner accounts, member settlement on 2890; accrual default; brutet räkenskapsår allowed; K1 label for the 5 000 kr accrual threshold (BFNAR 2010:1); org number gets the 16 prefix. Migration 20260908110835 widens the three CHECK constraints, adds supported_entity_types(), re-creates the three create RPCs with the widened guard and adds the förening block to seed_chart_of_accounts. Applied to staging and covered by ideell-forening-entity-type.pg.test.ts. Part of #2072 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdGafpUA7jVV1oYjkwfQCh * fix(company): close the förening paths the skeptic refuted (#2072) Five refutations from the /skeptic pass on 7a05c54d2, each fixed at the shared definition rather than the reported site: 1. Privately paid supplier invoices and the utlägg dialog resolved the owner account in lib/expenses/payer.ts with its own AB/EF ternary, so a förening member's invoice was built on 2893 and then refused by the expense-claim service (which already said 2890), burning an ankomstnummer. The helper now uses ownerSettlementAccount. 2. Booking templates substitute their `_ab` accounts only for an aktiebolag; the `private_expense` template kept its base 2013 for a förening. Template accounts now resolve through templateAccountForForm: EF base, AB override, förening base with owner accounts translated to 2890 (booking-templates.ts and proposal-lines.ts share it). 3. A VAT-registered förening with helårsmoms got no momsdeklaration deadline: the annual VAT rule bailed on anything but AB/EF. A förening is a juridisk person and follows the räkenskapsår schedule (SFL 26 kap 33 §), so the rule now keys on fiscalYearLockedToCalendar instead of the two literals; same in the MCP VAT report. 4. 2069 would have accumulated across years: the year-open omföring was AB-only with 2099/2098 hard-coded. planResultAppropriation now takes the pair from resultClosingAccounts (AB 2099 -> 2098, förening 2069 -> 2068) and skips forms with no carry (EF). 5. With the flag off, a registry lookup that returned "Ideell förening" was prefilled into the onboarding journey, the form picker was skipped and the create step answered "Ogiltig företagsform" with no way back. The journey, the BankID picker, the onboarding page and the MCP lookup now use mapSetupEntityType, which maps only creatable forms, so a flagged-off form falls through to the picker as before. Also: form picker keeps its AB-first order; tests for each fix. Part of #2072 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdGafpUA7jVV1oYjkwfQCh * chore(migrations): move ideell förening migration after main's latest version (20260908143051) Two migrations landed on main after the branch forked; a lower version would be skipped by the merge-time apply. Staging history row renamed to match. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdGafpUA7jVV1oYjkwfQCh * chore(skills): regenerate accounted-api reference for the widened entity_type enum Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdGafpUA7jVV1oYjkwfQCh --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
cd46d936f3 |
fix(bokforing): account search results in the booking dialog search field (#1877) (#1889)
* fix(bokforing): account search results in the booking dialog search field (#1877) The search field in the "Bokfor transaktion" dialog only matched template metadata, so typing an account number or name (e.g. active 5460 Forbrukningsmaterial) gave zero hits and no path to booking; the real account search was only reachable via the discreet "Bokfor manuellt" link. - TemplatePicker now also searches the company's active chart of accounts (reusing lib/bookkeeping/account-search.ts) and shows hits as a "Konton" result group; picking one routes into the same manual booking flow as "Bokfor manuellt" with the account prefilled on the counter line. - New buildActiveAccountIndex helper indexes chart rows active-only, so deactivated accounts never surface as bookable results. - searchTemplates additionally prefix-matches all-digit tokens against a template's business account (debit for expense, credit for income, both legs for transfers, AB variants included); the settlement leg is deliberately excluded so "1930" does not light up every template. - Library template search prefix-matches line accounts on all-digit queries. - Empty search results show a hint pointing at "Bokfor manuellt" for accounts outside the active chart; placeholder now says "Sok mall eller konto...". New strings in both messages/sv.json and messages/en.json. Fixes #1877 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bokforing): match only business lines when digit-searching library templates (#1877) CodeRabbit review on PR #1889: the all-digit library-template match also hit settlement and VAT lines, so searching "1930" lit up every user template with a bank settlement leg. Restrict the predicate to business lines, mirroring the static catalog's settlement-leg exclusion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0d0415e19c |
fix(vat): danstillställningar 25% → 6% from 2026-07-01 in guidance and suggestions (#1491)
* 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> |
||
|
|
f55bfc478b |
fix(bookkeeping): three static templates named accounts that break or mis-book (#1397)
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>
|
||
|
|
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> |
||
|
|
e4488a900b |
feat: add user locale preference to user_preferences table (#555)
* 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
|
||
|
|
cad83d180d |
fix(currency): transaction-date FX rate + mall bookings in SEK (#442) (#483)
* 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> |
||
|
|
da39eb2d43 |
Bug/momsdek overflow (#465)
* 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> |
||
|
|
66a4027f1e |
feat: BAS data overhaul, currency revaluation, expenses, UI polish, and cleanup
- 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> |
||
|
|
6956a757f3 |
feat: consolidate booking templates from 100 to 48 and improve codebase
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> |
||
|
|
6cef4e11eb | New classification logic etc | ||
|
|
bbb82866ee |
feat: add 4 new Swedish bank CSV parsers and improve transaction categorization
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> |