7bcd46d5038e052607bb7bdb38d376679d2bf486
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
32d9978f1b |
Fix/chrome pdf preview csp (#572)
* feat: add option to exclude year-end closing entries in SIE export and related reports * delete docs * fix: allow Chrome's PDF viewer in verifikat document preview The /api/documents/:id/inline route shipped with `object-src 'none'` in its CSP, which blocked Chrome's built-in PDF viewer (it renders inline PDFs via an internal <embed>). Users on Chrome saw "Det här innehållet har blockerats" when expanding a PDF attachment in the bookkeeping view; Firefox (PDF.js) and Edge (own viewer) were unaffected, and JPGs worked because <img> isn't subject to object-src. Drops the CSP for this route to the minimum needed for embeddability: `frame-ancestors 'self'`. X-Content-Type-Options: nosniff plus the fixed Content-Type from the handler already block MIME confusion; X-Frame-Options: SAMEORIGIN + frame-ancestors still block clickjacking. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(auth): add webmail deep link to email confirmation screens Mirrors Stripe's signup UX: after asking the user to verify their email, detect their webmail provider from the domain and show a button that opens the inbox in a new tab. Gmail gets a from:<sender> search pre-populated; Outlook/Yahoo/iCloud/Proton open the inbox directly. Unknown / custom domains fall back to the existing copy. Sender address is configurable via NEXT_PUBLIC_BRANDING_AUTH_EMAIL_FROM (default noreply@gnubok.se) so white-label installs can match their Supabase Auth SMTP config. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(auth): unblock first-time password set for BankID users with MFA Supabase rejects updateUser({password}) and mfa.unenroll with "AAL2 session is required" whenever a TOTP factor is enrolled. BankID magic-link logins produce AAL1, and middleware skips MFA enforcement for bankid_linked users, so they had no path to AAL2 — leaving them unable to set a backup password or disable MFA without going through the email-recovery escape hatch. - /api/account/password: branch on app_metadata.has_password. First-time set writes via service.auth.admin.updateUserById (no existing credential to protect, AAL2 guard does not apply). Change-password keeps the user-session updateUser so AAL2 still fires for credential rotation. - /mfa/verify: accept a safeReturnTo query param and route there after successful verify, so step-up flows can land back where they came from. - SecuritySettings: detect the AAL2 error from both change-password and mfa.unenroll and redirect through /mfa/verify?returnTo=/settings/account instead of toasting a dead-end error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add tests and rounding utility for öre precision in bokslut calculations - Implemented `roundOre` function for rounding SEK amounts to two decimal places, ensuring consistent monetary calculations. - Introduced `ORE_TOLERANCE` constant for comparing rounded amounts, facilitating invariant checks in financial entries. - Created comprehensive tests for `roundOre`, covering typical cases, edge cases, and idempotency. - Added year-end invariants tests to verify database-level guarantees for closing entries, ensuring they balance to the öre and reject discrepancies. - Developed end-to-end tests for the dispositions chain, validating the correctness of calculations across various scenarios. * fix: update PDF rendering to remove Swish QR code generation and set default to disable Swish visibility * fix: enhance security by rejecting data URIs in safeReturnTo function tests * fix: improve rounding logic in roundOre function and add customer_type migration * fix: add customer_type column to customers and enforce CHECK constraint --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8a6ce7093e |
feat: implement skattekonto drift detection and alerting (#525)
* feat: implement skattekonto drift detection and alerting - Add skattekonto drift computation logic to compare Skatteverket's saldo with GL 1630 sum. - Implement alerting mechanism for significant drift changes, with throttling to prevent alert spamming. - Introduce database functions to sum GL 1630 entries and list unbooked skattekonto rows. feat: create own account transfer detection - Develop logic to detect transfers between a company's own cash accounts based on counterparty IBAN. - Implement tests to validate detection logic under various scenarios, including matching and non-matching IBANs. feat: establish cash accounts as a first-class entity - Create cash_accounts table to manage routable cash accounts, replacing ad-hoc JSONB structures. - Implement functions for listing, upserting, and managing cash accounts, including primary account designation. feat: enhance GL line reconciliation functionality - Modify get_unlinked_1930_lines RPC to accept any account number for reconciliation, improving flexibility for different currencies. - Update related functions to ensure compatibility with the new cash_accounts structure. feat: capture counterparty IBAN in transactions - Add counterparty_iban column to transactions table to facilitate intra-account transfer detection. - Create index for efficient lookups based on counterparty IBAN. * feat: Enhance cash account handling and reconciliation processes - Updated reconciliation routes to enforce cash account validation for all account numbers, including '1930'. - Improved error handling for unknown cash accounts in reconciliation status and unmatched entries routes. - Changed CashAccountSelector to use sessionStorage instead of localStorage for better data privacy. - Fixed mapping for employer payroll taxes to route to the correct account (2730 instead of 2731). - Added safety checks for company IDs in the guessCounterAccount function to prevent injection vulnerabilities. - Introduced atomic RPC for setting primary cash accounts to avoid intermediate states during updates. - Seeded default cash accounts for new companies to ensure reconciliation routes are accessible from day one. - Updated email notifications for drift detection to avoid exposing sensitive financial data. - Enhanced bank reconciliation logic to handle multi-currency transactions correctly. - Renamed and updated tests to reflect changes in the underlying RPCs and ensure accurate coverage. - Migrated existing cash account rules to correct mappings in compliance with Swedish accounting standards. |
||
|
|
5eee06a56d |
fix(reconciliation): exclude opening balance from unmatched-1930 set (#443) (#485)
* fix(reconciliation): exclude opening balance from unmatched-1930 set (#443) Reconciliation incorrectly counted IB (source_type='opening_balance') vouchers on 1930 as unmatched bank transactions and included them in the GL period total. Result: an SIE-imported book always showed a phantom unmatched voucher and a difference equal to the IB amount, even when every real bank transaction was matched. This was the root of the "reconciliation says broken but numbers look right" complaint. The fix is two-sided: - get_unlinked_1930_lines RPC now skips source_type='opening_balance' rows. IB has no counterpart in the bank feed by definition. - getReconciliationStatus splits glOpeningBalance out of the period movement and computes difference = bankTotal − glPeriodMovement (was bankTotal − glBalance). gl_1930_balance is preserved unchanged for back-compat; new gl_1930_period_movement and gl_1930_opening_balance fields let the UI show both perspectives. - BankReconciliationView shows period movement vs bank in the diff and folds IB into a small caption ("Ingående balans … räknas inte i avstämningen") only when non-zero. Mirrors Fortnox's "Ingående saldo vid avstämningsstart" pattern — IB is verified separately, never surfaced as unmatched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reconciliation): use IS DISTINCT FROM for NULL-safe source_type filter `source_type <> 'opening_balance'` evaluates to NULL (not TRUE) when `source_type IS NULL` under SQL three-valued logic. journal_entries.source_type is NOT NULL today so the practical difference is zero, but if that constraint is ever relaxed `<>` would silently drop NULL rows from the unmatched-1930 set, making them invisible to reconciliation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(test): use valid source_type 'bank_transaction' in pg-real fixture CI pg-real failed because 'bank_import' is not in the journal_entries source_type CHECK constraint. The valid value for bank-imported transactions is 'bank_transaction' (per migration 20260513170001 + earlier). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(reconciliation): deprecate gl_1930_balance on ReconciliationStatus Mark the IB-inclusive balance field as @deprecated on both the server-side ReconciliationStatus interface and the component-side mirror. Downstream consumers that read this field expecting it to drive the diff will be off by the IB amount whenever a SIE-imported opening balance exists on 1930. The new gl_1930_period_movement field is the correct basis for the diff and is what the difference field is computed against. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(reconciliation): tighten review feedback on PR #485 Three tweaks from the Swedish-accounting bot review: 1. Drop the gl_1930_period_movement ?? gl_1930_balance fallback in BankReconciliationView. gnubok has no persisted reconciliation snapshots and Vercel deploys atomically, so the old-server/new-client case can't occur — the fallback was defensive code for an impossible state. Per CLAUDE.md: don't add back-compat shims when you can just change the code. gl_1930_period_movement and gl_1930_opening_balance are now required on the type. 2. Clarify in the migration comment that source_type='opening_balance' is reserved for the fiscal-year IB voucher (always at period_start). The bot questioned whether the unconditional filter could hide a mid-year corrective IB entry; document the invariant — mid-year corrections use source_type='correction' or 'manual', never 'opening_balance'. 3. Test fixture 2099 → 2091. 2099 is årets resultat (current year); 2091 is balanserad vinst/förlust, the realistic counterpart for a carried-forward bank IB. RPC filters on 1930 only so this is cosmetic but matches what a real SIE import would produce. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d0fbc2b616 |
refactor(ui): app-wide UI/UX consistency pass (#436)
* refactor(ui): app-wide UI/UX consistency pass
Net: +1,159 / −1,373 LOC across 77 files. No new features, no behavior
changes. Locks in a uniform design system across every dashboard surface.
What changed:
- **Foundation**: sidebar width 232→256px (md:w-64), spacing scale locked
(Tailwind 1/2/3/4/6/8/10/12; 2.5/5 forbidden), card padding p-6 default
(p-4 for compact metric cards), space-y-8 between page sections.
- **Tables unified**: all 33 thead blocks now share the Resultatrapport
pattern via shadcn Table primitive (text-[11px] font-medium uppercase
tracking-wider text-muted-foreground). Hand-rolled <table> instances
converted where they were data tables; form/edit grids kept distinct.
- **Status badges unified**: every status indicator routes through
shadcn <Badge variant>. Eliminated raw Tailwind colors
(bg-amber-100, bg-emerald-500/10, bg-blue-100, bg-purple-100, etc.)
in favor of the gnubok semantic palette (success=sage, warning=ochre,
destructive=terracotta).
- **Empty states unified**: list pages migrated from hand-rolled
"flex flex-col items-center py-12" divs to the EmptyState primitive.
- **Loading skeletons unified**: hand-rolled bg-muted rounded animate-pulse
divs replaced with shadcn <Skeleton> across 15 files.
- **Touch targets**: 6 back-buttons + edit-pencil + inbox delete bumped
from 24/32/36px to shadcn's 40px icon default. Added aria-labels on
9 icon-only navigation buttons.
- **Date formatting**: formatDate() for accounting data (ISO yyyy-MM-dd,
table-friendly) vs formatDateLong() for metadata (Swedish long form).
Raw {x.invoice_date} renderings routed through formatDate() in 18 sites.
- **Toast titles**: eliminated 33 generic "Fel" titles. Each toast title
now carries the action ("Kunde inte skapa lönekörning" etc.) with
description carrying the error detail.
- **Page-level cleanups**:
- Dashboard: dropped greeting hero + Snabbåtgärder/Att hantera nav
duplicates + Visa detaljer collapsible.
- Reports: 5-col mega-menu replaced with left-rail layout
(new ReportsNav component).
- Bookkeeping: fixed layout jump between Verifikationer/Ny verifikation
tabs (moved FiscalYearSelector inside journal tab).
- Bookkeeping: added voucher sort (A1 first / latest first) alongside
existing date sort. Required matching API param sort_by.
- KPI page: FiscalYearSelector instead of raw <select>; InfoTooltip
instead of inline info-button toggle; bigger numbers.
- Salary section: enum values translated to Swedish labels, mobile
table collapses to Anställd+Netto on <md, KPI typography aligned
with dashboard.
- Invoice forms: styled RequiredMark + aria-required, tabular-nums
on amount inputs.
- **CLAUDE.md**: new "Design System Tokens" subsection documents the
locked spacing scale, primitives table, typography rules, date helpers,
and forbidden patterns so future contributors don't drift.
Tests: 2,906 passing (unchanged). Lint: unchanged from main baseline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address PR review feedback (Greptile + compliance bot)
- **formatDate / formatDateLong timezone fix**: switch from new Date() to
parseISO. Bare yyyy-MM-dd strings are now parsed as local midnight rather
than UTC midnight, eliminating the off-by-one display in west-of-UTC
timezones flagged by Greptile.
- **DashboardContentProps cleanup**: removed unused firstName and settings
fields from the interface, and the corresponding fetch (profiles table)
+ computation in app/(dashboard)/page.tsx. The greeting was dropped in
the dashboard cleanup; these props were dead weight.
- **Voucher sort behavior documented**: extended the comment in the journal
entries API route to explain why voucher sort intentionally uses strict
fiscal_period_id filtering (BFL 5 kap 6–7 §§ — voucher numbers are
series-scoped within a fiscal year). The row-count delta between date
sort and voucher sort is now a documented design choice.
- **delete_last_voucher migration + draft-delete test included**: the UI
already shipped the "Radera utkast" path in the previous commit; this
pulls in the backing RPC migration that allows draft deletes (with the
full safety logic — drafts skip series/period checks since they have
voucher_number=0, posted entries go through the existing unchanged
path). This was originally meant for a separate PR but the UI shipped
half the feature without it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(migration): rename to match applied version
The function delete_last_voucher is already applied to the production DB
under version 20260509103736 (verified via pg_get_functiondef — exact
byte-for-byte match to file content). The previous file timestamp
20260509120000 would cause a fresh `supabase db push` to attempt re-applying
under a different version row. Renaming the file aligns local tracking
with what the database actually has.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: address compliance bot findings (payroll label + VAT visibility)
- sick_karens label: drop "(första sjukdagen)" qualifier. Per sjuklönelagen
6 §, karensavdrag is a single calculated amount (20% of one week's
sjuklön) deducted from the first sick day's pay — not bounded to the
first day. The qualifier could mislead users when the first sick day
and return-to-work span a weekend. Swedish-payroll bot recommendation.
- Omvänd skattskyldighet badge: variant outline → warning. The reverse-
charge indicator is compliance-critical (ML 16 kap) — missing it leads
to incorrect input VAT deduction. Outline was too subtle; warning's
ochre fill matches its semantic weight.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
2c03dac981 |
fix(reports): convert FX to SEK in supplier/AR ledger reconciliation (#396)
* fix(reports): convert FX to SEK in supplier/AR ledger reconciliation The supplier and AR ledger reports were summing remaining_amount directly without converting foreign-currency invoices, so a EUR/USD invoice would land in the aging total at face value while the corresponding 2440 / 1510 GL line was already posted in SEK. This produced false reconciliation discrepancies (e.g. 496,25 kr ledger vs 952,50 kr GL with four EUR/USD invoices). Apply resolveSekAmount(remaining, null, currency, exchange_rate) in supplier-ledger, supplier-reconciliation, ar-ledger, and ar-reconciliation. Per-invoice detail rows on the AR ledger keep the original currency for display; only aging buckets and totals become SEK. Adds mixed-currency test cases to all four files. Also bundles unrelated WIP from the working tree: - bank-reconciliation: log the swallowed catch error and drop the fallback path for the deleted get_unlinked_bank_lines RPC, using get_unlinked_1930_lines directly. - new GET /api/transactions list endpoint with unmatched/reconciled/ currency/date filters and full route tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reports): address PR #396 review findings Compliance and Greptile review identified four issues; all four are addressed here. - Reconciliation: surface unconverted_fx_count on ReconciliationResult and ARReconciliationResult. When > 0 the difference field may be a data gap (FX invoice with no exchange_rate) rather than a true reconciliation break. UI now renders a Swedish caveat below the Avstämd / Ej avstämd badge so users understand the cause. New tests assert the count is set on legacy FX rows. - Reconciliation: document the invoice-date-rate assumption explicitly in the JSDoc of both reconciliation generators. Per ML 8 kap 21–23 §, the report uses each invoice's stored exchange_rate; partial payments settled at a different rate produce a delta correctly booked to 3960/7960 as valutakursvinst/-förlust, but the GL will diverge from the report by that amount until a subledger-derived total is wired up (deferred follow-up). - fetchUnlinkedGLLines: drop the misleading bankAccount parameter. It was advertised as configurable but the function silently returned [] for any value other than '1930'. Now the signature is honest: 1930-only until proper multi-account support is built. - /api/transactions: query MAX_ROWS+1 rows so the response can include has_more and limit fields. Callers can now detect truncation, which matters once a company crosses 500 unmatched transactions in the selected range. New test asserts has_more=true when 501 rows are returned by the DB and the response is sliced to MAX_ROWS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reports): address PR #396 round-2 compliance review Compliance bot v2 review surfaced four findings on the previous commit; three are addressed here. The fourth (an fx_rate_diff_amount indicator distinguishing real reconciliation breaks from correctly-booked valutakursvinst/-förlust) requires a subledger-derived total against 3960/7960 — already documented as a deferred follow-up in the JSDoc. Changes: - Exclude unconvertible FX rows from SEK sums. resolveSekAmount's null-rate fallback returned the raw foreign amount, so a 100 EUR invoice with no rate was being added to a SEK total as if it were 100 SEK. All four generators (supplier-ledger, supplier-reconciliation, ar-ledger, ar-reconciliation) now skip rows where currency != SEK and exchange_rate is missing/zero, and count them in unconverted_fx_count. - Surface unconverted_fx_count on SupplierLedgerReport / ARLedgerReport (not just the reconciliation result). UI shows a Swedish caption beneath the "Totalt utestående" card whenever the count is positive, so users see the warning even if they don't enable the reconciliation panel. - Add outstanding_sek: number | null to ARInvoiceDetail. The per-invoice detail row keeps `outstanding` in invoice currency for display, but now also exposes the converted SEK value (or null when unconvertible). Defensive against future callers that sum across customers — they should use outstanding_sek to avoid mixing currencies (a real momsdeklaration foot-gun otherwise). - Add a 1930-only notice to BankReconciliationView. The reconciliation is scoped to account 1930; users with Plusgiro 1920, kreditkort 1940, or valutakonton now see a Swedish caption explaining those are reconciled separately. Tests: - supplier-ledger: previous "falls back to original amount" case flipped to assert the row is excluded and counted; assertion that the legacy supplier disappears from the list when their only row is unconvertible. - supplier-reconciliation: previous "1 100 ledger vs 1 000 GL" case flipped to assert ledger=1 000 and is_reconciled=true with unconverted_fx_count=1. - ar-reconciliation: same pattern. - ar-ledger: existing FX-mix test extended to assert outstanding_sek on each detail row; new test covering the null-rate exclusion path asserts the detail row is still pushed (with outstanding_sek=null) but excluded from buckets and the grand total. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reports): address PR #396 round-3 compliance review Compliance bot v3 surfaced four findings on the previous commit; two are addressed here, two are deliberately skipped (rationale in JSDoc / this message). Addressed: - is_reconciled now returns false whenever unconverted_fx_count > 0, even if the numeric difference is zero. Per BFL 5 kap, the reconciliation must cover all affärshändelser; if a row was excluded for a missing exchange rate, the calculation is incomplete by construction and the period cannot honestly be stamped Avstämd. The fix is for the user to fill in the missing rate, not for the system to claim balance on partial data. - AR reconciliation now sums account 1510 + 1513 in the GL balance comparison. Forward-looking defense for ROT/RUT fakturamodellen invoices that split AR receivables across the customer portion (1510) and the Skatteverket claim (1513). Today no production code posts to 1513 so the value is unchanged in practice; once fakturamodellen invoicing is added, the reconciliation will continue to balance without requiring another fix. UI label updated to "Kundfordringar (1510 + 1513) saldo" so the inclusion is visible. Field name and shape are unchanged for back-compat with the supplier-side parallel. Skipped: - "Block is_reconciled=true when any FX invoice exists in an open period" — overly aggressive; would block reconciliation for any FX-using company even when their books are correctly matched. The proper solution is the deferred subledger-derived total against 3960/7960 (already documented in JSDoc on both reconciliation generators), which can compute the *expected* FX rate difference and either subtract it from `difference` or expose it as `fx_rate_difference`. Not in scope for this PR. - Plusgiro 1920 wording in BankReconciliationView — bot itself marked this "not a hard finding". The 1920 reference is accurate per BAS 2026. Tests: - supplier-reconciliation: existing v3 exclusion test updated — asserts is_reconciled=false despite numbers matching, with comment explaining BFL rationale. - ar-reconciliation: same update + new test covering the 1510 + 1513 sum (1 200 on 1510, 300 on 1513, ledger total 1 500 → reconciled). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f3ec634a46 |
feat: open-source under AGPL-3.0, redesign UI to grayscale palette, add uncategorize API, fix VAT account names
Add LICENSE (AGPL-3.0-or-later), CONTRIBUTING.md, SECURITY.md, DCO, and NOTICE files. Rewrite README for open-source audience with self-hosting instructions. Redesign color palette to grayscale chrome theme across all components. Add transaction uncategorize API route with tests. Fix VAT account name mismatches in migration 052. Improve import page with SIE file support and loading skeleton. Co-Authored-By: Claude Opus 4.6 <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> |
||
|
|
6f4573f380 |
feat: add foreign currency support, refactor bookkeeping engine, and improve invoice inbox document classification
- Add currency-utils module for SEK conversion with exchange rates - Refactor createJournalEntry to use draft+commit flow preventing voucher number gaps (BFL 5 kap. 7§) - Add foreign currency support to invoice entries with per-line SEK conversion - Centralize category-to-account mapping into single source of truth - Refactor invoice inbox to use shared document analyzer with document type classification (receipt, supplier invoice, government letter) - Update mapping engine, supplier invoice entries, and transaction entries - Fix report component rendering issues - Add new validation schemas and tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
91e2c1705a |
feat: per-line VAT, invoice document types, ledger-based VAT declaration, bank reconciliation, and pagination
Per-line VAT rates: - Add generatePerRateLines() to group invoice items by vat_rate with separate revenue + VAT lines per rate group (invoice-entries.ts) - Add getAvailableVatRates() and getVatTreatmentForRate() (vat-rules.ts) - PDF template shows per-line VAT column and per-rate totals for mixed-rate invoices - Invoice create/review UI supports per-line rate selection - Types: add vat_rate/vat_amount to InvoiceItem, vat_rate to CreateInvoiceItemInput Invoice document types (proforma, delivery note): - Add InvoiceDocumentType, document_type and converted_from_id to Invoice type - PDF hides prices for delivery notes, adds proforma notice - Email templates support all document types - mark-paid skips journal entries for non-invoice document types - Migration 031: invoice_document_type Accounting method support: - Add AccountingMethod type (accrual/cash) - Migration 032: add_accounting_method column to company_settings VAT declaration rewrite: - Rewrite to read directly from general ledger (26xx/3xxx account lines) instead of aggregating invoices/transactions/receipts - ACCOUNT_RUTA mapping drives momsdeklaration boxes from GL balances Bank reconciliation: - Transaction ingest now pre-fetches unlinked GL lines and attempts auto-reconciliation during import - Add transaction.reconciled event type - Add ReconciliationMethod type and reconciliation_method on Transaction - Migration 030: bank_reconciliation - New reconciliation engine, API routes, and BankReconciliationView component Pagination (fetchAllRows): - New lib/supabase/fetch-all.ts overcomes PostgREST 1000-row limit - Adopted in all report generators, SIE/SRU export, account list APIs Fiscal period validation: - New validate-period-duration.ts enforces max 18 months per BFL 3 kap. - Applied in period-service.ts and fiscal-periods API Account mapper simplification: - Remove Levenshtein/fuzzy matching, use exact account number match only Swedbank parser improvements: - Support abbreviated headers (Clnr, Bokfdag, Radnr) - Use Referens column as counterparty Chart of accounts management: - Add DELETE endpoint with system account and usage protection - PUT uses partial updates - New AccountCombobox, AddAccountDialog, EditAccountDialog, ChartOfAccountsManager Tax deadline corrections: - Rewrite inkomstdeklaration_ab using Skatteverket lookup table - Rewrite arsredovisning deadline to 7 months after FY end per ÅRL 8:3 Onboarding first fiscal year: - Add first fiscal year toggle with date pickers and 18-month validation UI terminology: - Change "okategoriserad/kategorisera" to "obokförd/bokföra" throughout Report column fix: - Fix start_date/end_date to period_start/period_end in report queries Supplier invoice input: - CreateSupplierInvoiceItemInput uses amount field (legacy quantity/unit_price kept) Misc: - SIE import uses upsert for idempotent account creation - account-descriptions.ts falls back to BAS reference data - Add invoice_default_notes to CompanySettings - Update CLAUDE.md to reflect current project state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |