15af0cd7eadcecafd6d17618d04bf67b4f736fd2
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c74b19df1b |
Accounted rebrand + swarm-skill cleanup + bank-reconciliation fixes (#643)
* feat(reconciliation): close the bank-feed loop on voucher links and re-tag mis-typed opening balances
Two related fixes to bank reconciliation correctness:
1. Auto-reconcile on voucher link. Linking an invoice or supplier invoice to
an existing voucher previously advanced only the invoice — the bank
transaction that paid it kept sitting in the Transactions inbox with a null
journal_entry_id. linkInvoiceToVoucher / linkSupplierInvoiceToVoucher now
call autoReconcileTransactionForLinkedVoucher (lib/reconciliation), which
links the bank transaction to the same verifikat when exactly one unbooked
line matches it. Best-effort and post-commit: a failure here never fails the
link. The result surfaces reconciledTransactionId; the inbox row leaves the
list and the UI shows link_success_tx_reconciled.
2. Re-tag mis-typed opening balances. getReconciliationStatus and the GL-line
matching RPCs identify a cash account's ingående balans solely by
journal_entries.source_type='opening_balance'. Companies migrated from other
systems often booked the bank IB as an ordinary voucher (source_type
'import' or 'manual'), so it was never excluded and surfaced as a phantom
reconciliation difference equal to the opening balance. Adds:
- migration mark_entry_as_opening_balance: a GUC-gated carve-out in the
immutability trigger plus a SECURITY DEFINER RPC that validates the entry
(balance-sheet lines only, dated on a fiscal-period boundary), flips the
source_type, and writes an audit row — no blanket data sweep.
- POST /api/reconciliation/bank/mark-opening-balance + MarkOpeningBalanceSchema.
- BankReconciliationView action to trigger it from the IB diff.
The gnubok_create_voucher executor now accepts a typed is_opening_balance flag
and derives source_type='opening_balance' only after validating class 1/2 lines
on the period start, so new IBs land correctly typed.
Covered by lib/reconciliation auto-reconcile tests, voucher-executors tests,
and a mark-entry-as-opening-balance pg-real test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: rebrand gnubok → Accounted and prune swarm agent skills
Product rebrand and skills housekeeping. No runtime behaviour change.
Rebrand: replace user-visible "gnubok" with "Accounted" across docs, READMEs,
in-code comments, doc-site content, MCP skill/resource prose, and the
gnubok-mcp package description. The MCP resource URI scheme is moved gnubok://
→ Accounted:// consistently across resource registrations, the event-type
comment, and the resource/skill tests. Deliberately preserved as stable
identifiers (NOT rebranded): the gnubok-company-id cookie, gnubok_sk_ / gnubok_inv_
token prefixes, the gnubok-mcp npm bridge name, and the AGI <gem:Programnamn>
value (kept 'gnubok' per its source comment — it is the software identifier sent
to Skatteverket and must not churn across visual rebrands).
Skills: remove the 27 swarm-* agent SKILL.md atoms (no longer used; already
absent from the agent_atom_registry in prod), refresh the remaining skill docs,
add the .claude/rules/ path-scoped rule set, and regenerate the
seed_agent_atom_bodies migration + .skill-body-manifest.json via
`npm run skills:generate` so the DB-backed skill bodies match the trimmed set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
f0a2577b8b |
feat(vat-declaration): implement RC basis gap detection and correctio… (#466)
* feat(vat-declaration): implement RC basis gap detection and correction functionality * fix(vat-declaration): improve error handling and validation for RC basis account selection |
||
|
|
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> |
||
|
|
980f29dae8 |
Bug/momsdeklaration skv (#449)
* fix(salary): show birthdate in masked personnummer, hide the 4-digit suffix Flip the personnummer display format from XXXXXXXX-NNNN to YYYYMMDD-XXXX so the sensitive 4-digit suffix is hidden while the (public) birthdate stays visible. Affects the employees list/detail, salary run, payslip PDF, payslip email, and the MCP server tools (list_employees, get_salary_run). Each call site now decrypts the stored personnummer before masking. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(transactions): allow deleting unbooked transactions from "Alla transaktioner" The history list only let users delete via the inbox card; once a category or mall was picked but the verifikation hadn't been created, the row showed "Ej bokförd" with no way to remove it. The API already permits delete while journal_entry_id is null, so the gap was purely a missing UI affordance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(vat): populate ruta 20-24 for reverse charge + dishonest "Validera OK" Three connected issues caused Skatteverket to reject momsdeklarationer with FK004 even after our local "Validera"-knapp returned OK. 1. supplier-invoice-entries booked fiktiv moms (2614/2624/2634 + 2645/2647) on reverse-charge invoices but never the underlying basbelopp on 44xx/45xx. Ruta 30-32 filled up at SKV while ruta 20-24 stayed at 0 — SKV's FK004 ("silent netting prohibited", ML 13 kap kräver båda sidor). Fix: generateReverseChargeBasisLines in vat-entries.ts emits parallel 45xx/44xx debit + 4598 motkonto credit per rate group. Engine calls it from registration, cash, and credit-note paths. Skipped when the user booked the expense directly on a basis account to avoid double-counting. 4598 added to BAS reference (no migration needed; account_number is plain text on journal_entry_lines). 2. rutorToMomsuppgift rounded each ruta independently but computed summaMoms from the unrounded ruta49. SKV recomputes the sum from integer rutor on their side, so fractional öres caused ±1 SEK drift and SKV rejected with FK009. Fix: derive summaMoms from the already-rounded VAT-amount rutor. 3. "Validera"-knappen only confirmed SKV's internal arithmetic — a declaration with ruta 30-32 populated and ruta 20-24 empty validated fine until /utkast hit FK004. Users got a false green light. Fix: vat-declaration-checks.ts runs locally before the SKV call, blocks Validera/Spara when ERROR-level findings exist, and surfaces them in a separate "Lokala kontroller"-section. Success message reworded so SKV's OK is no longer presented as filing-ready. Tests: 4535/4536/4531/4425 lines + 4598 motkonto on EU/non-EU/byggtjänster RC, credit-note reversal, fractional-öres summaMoms, all four pre-flight codes (RC_BASIS_MISSING, RC_OUTPUT_MISSING, RC_INPUT_VAT_MISMATCH, SUMMA_MOMS_DRIFT). Backfill for already-posted entries follows in the next commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add skattekonto matching functionality - Enhance TransactionInboxCard to display a warning for potential 1930↔1630 transfers. - Implement match suggestions for skattekonto transactions in the backend. - Create SkattekontoMatchDialog component for linking skattekonto rows to existing journal entries. - Develop SkattekontoInboxCard component to handle skattekonto transactions in the inbox. - Introduce skattekonto-match utility functions for candidate matching and linking. - Update types to include match suggestions and enriched transaction responses. * refactor: reorganize skattekonto types and implement bank counterpart matching logic * docs: update CLAUDE.md to streamline integrations and clarify architecture details * refactor: enhance reverse charge logic to handle non-basis accounts and prevent double-counting --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cd64c0e3fb |
feat(skatteverket): production-ready momsdeklaration submission (#380)
* feat(skatteverket): production-ready momsdeklaration submission
Brings the Skatteverket extension up to a state where it can ship moms
declaration submission to Vercel production. Verified end-to-end against
SKV's Komplett testtjänst — all 8 momsdeklaration operations tested
(kontrollera, spara/hämta/radera utkast, lås/lås upp, hämta inlämnade,
hämta beslutade) plus signing-link return.
Bundles three coherent changes:
1. Skatteverket extension (the main work)
- extensions.config.json: enable `skatteverket`, drop `invoice-inbox`
and `ai-agent` (those were enabled in config but lacked AWS env vars
in prod, so they loaded but failed at runtime)
- lib/reports/vat-declaration.ts: extend ACCOUNT_RUTA to populate
Ruta 06 (uttag 3401–3403), Ruta 20–24 (reverse-charge bases from
4xxx cost accounts), Ruta 50 (import 4545–4547), and Ruta 42
(3404/3994/3980); delete the supplier-type heuristic that made
Ruta 20 and Ruta 23 always 0
- extensions/general/skatteverket/lib/token-store.ts: work around
three real prod schema-drift issues — wrong column on read/delete
(was `company_id`, schema only has `user_id`), missing
UNIQUE(user_id) constraint that makes UPSERT fail (switched to
DELETE+INSERT), missing RLS policies (switched to service-role
client). Refresh path now reuses existing row's company_id when
none is passed.
- extensions/general/skatteverket/index.ts: 9 sites switched from
ctx.companyId to ctx.userId for the token-store key; pass
companyId from the OAuth callback
- extensions/general/skatteverket/types.ts + components/reports/
SkatteverketPanel.tsx: align field names with v1.0.24 RAML
(signeringsLank/kontrollResultat/resultat/kod/status/beskrivning).
Without this, the signing link never displayed.
- SkatteverketPanel: add Lås upp + Radera utkast + Hämta utkast +
Hämta beslut buttons so the full lifecycle is reachable from the UI
- lib/reports/__tests__/vat-declaration.test.ts: rewritten to match
the refactored calculator; new fixtures for cost-account-based
reverse charge (Ruta 20/21/22/23/24), Ruta 50 import, Ruta 06
uttag, Ruta 42 expansion; SKV §4.1.1.4 cross-field contract checks
- supabase/migrations/20260428120000_skatteverket_tokens_user_id_unique.sql:
idempotently adds the missing UNIQUE(user_id) constraint
- scripts/*: dev-only helpers used during the prod-of-test
verification (create test company, seed VAT data, inspect token
state, etc.)
2. Journal-entries cancelled-status filter
- app/api/bookkeeping/journal-entries/route.ts: when no status filter
is supplied, exclude `cancelled` entries by default
- supabase/migrations/20260428153500_journal_entries_with_related_exclude_statuses.sql
3. Swedish e-invoicing skill (reference docs only — no runtime code)
- .claude/skills/swedish-e-invoicing/
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(skatteverket): address PR review findings
- panel: handleFetchDraft read `result.data?.last` (typo) — switched to
`result.data?.locked` to match the field defined in
SkatteverketUtkastResponse and the v1.0.24 RAML. The "(låst)" suffix on
the success message would silently never appear before this fix.
- api-client: getValidToken had no concurrency guard, so two parallel
SKV requests from the same user could both call /token with the same
refresh_token. SKV rotates the refresh_token on first use, so the
second call would 401 with REFRESH_EXHAUSTED-adjacent failures. With
the new 6-button UI on SkatteverketPanel, rapid clicks made this a
realistic trigger. Added an in-process Promise map keyed on userId
that coalesces concurrent refresh attempts; cross-process races are
mitigated by re-reading tokens inside the critical section before
calling refreshAccessToken (if another process refreshed already, we
use the newer token instead of burning the old refresh_token).
- migration 20260428120000: dedup query used `created_at < max(...)`,
which failed to remove duplicates inserted in the same second. The
subsequent ALTER TABLE … ADD CONSTRAINT would then abort. Switched
to ctid (Postgres physical row identifier) to break timestamp ties.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(skatteverket): throw on token-store SELECT error before destructive DELETE
The company_id pre-read in storeTokens used destructuring that discarded
the error field. If the service-role SELECT failed for any reason (network
blip, overloaded DB, transient permissions issue), `existing` became null,
`resolvedCompanyId` stayed undefined, and execution fell through to the
DELETE. The old row got deleted successfully, then the INSERT omitted
company_id and failed with the NOT NULL constraint violation — leaving
the user with no token row at all and forcing a fresh BankID handshake.
Now we capture the SELECT error and throw before the DELETE runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
d8e0a22495 |
feat: counterparty templates, Skatteverket extension, complete VAT form (#117)
* feat: separate AR/AP/accounting into distinct nav groups (#92) Split the flat "Finans" sidebar group into three visually distinct sections — Försäljning (AR), Inköp (AP), and Redovisning — so users coming from Fortnox immediately find customer invoicing and supplier invoices as top-level concepts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: journal entry detail view, correction chain, and account name display - Add journal entry detail page at /bookkeeping/[id] with full entry view - Add correction chain API and component showing storno relationships - Add JournalEntryStatusBadge component for entry status display - Show debit/credit account names in template picker and review dialogs - Expand client-side BAS account name mapping with additional accounts - Show account codes on transaction inbox suggestion buttons Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback — N+1 query, duplicate name, nav dedup - Batch reverse-lookup into single query per BFS iteration (was N+1) - Differentiate account 2393 from 2893 in display names - Extract shared loop for desktop/mobile nav group rendering Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: counterparty templates, Skatteverket extension, VAT form completeness, and UI cleanup - Add counterparty-based categorization templates (learned from user approvals and auto-ingestion) with fuzzy matching in the mapping engine - Add Skatteverket extension for direct VAT declaration submission via API - Complete VAT declaration form with all 30 SKV 4700 boxes (ruta 08, 35-42, 50, 60-62) - Fix ruta 49 formula to include import VAT (ruta 60+61+62) - Simplify dashboard UI: remove redundant icons from stat cards, customer cards, invoice list, supplier invoices; use Badge variants consistently - Add SkatteverketPanel component to reports page - Add categorization_templates and skatteverket_tokens migrations - Update tests and helpers for new types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback — VAT detection, migration timestamps, dedup - Fix detectVatTreatment to derive actual rate (12%/6%) from VAT line description instead of hardcoding standard_25 - Rename skatteverket_tokens migration to 20260324120001 to avoid duplicate timestamp with categorization_templates (fixes Supabase deployment failure) - Make refreshAccessToken accept previousRefreshCount param to enforce refresh limit contract at the type level - Fix rate limiter TOCTOU by claiming slot before await - Extract formatRedovisare/formatRedovisningsperiod to shared lib/skatteverket/format.ts — eliminates duplication between mappers.ts and SkatteverketPanel.tsx Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |