64ea0fef0200e38fdbd142ec2a646d2db5cec6f4
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> |
||
|
|
01dbef4015 |
feat(dimensions): PR4 reports — dimension-filtered P&L + Resultat per projekt/kostnadsställe (#862)
* feat(dimensions): PR4 reports — dimension-filtered P&L everywhere + Resultat per projekt/kostnadsställe The Project P&L milestone of the dimensions plan (dev_docs §7 PR4). One choke point lights up everything: generateTrialBalance gains options.dimensions (SIE dim → code map) pushed down as jsonb containment (dimensions @>, served by idx_jel_dimensions_gin) on both line queries, with company-wide opening balances dropped when filtered (they cannot be dimension-scoped; P&L-safe by whitelist). Resultatrapport, resultaträkning, huvudbok, monthly-breakdown and the TB drill-down inherit the filter; the KPI route filters only its P&L-side inputs (income statement, months, expense composition) — never cash/VAT. New report lib/reports/dimension-pnl.ts — "Resultat per projekt/ kostnadsställe" (Fortnox Resultatrapport projekt): value-as-column matrix over one dimension with an explicit "(Utan dimension)" bucket computed as the residual against the same trial-balance pass resultatrapport uses, so every row and the Totalt column reconcile with the unfiltered resultatrapport by construction. Registered in REPORT_CATALOG (visible only when dimensions_enabled), slug-routed view + xlsx export. UI: DimensionFilter (dimension + value picker, persistent "Filtrerad — ej fullständig rapport" chip) mounts in FocusedReport for catalog entries flagged dimensions: true; huvudbok rows show line dim codes. Statutory exclusion pinned by TEST, not convention: lib/reports/__tests__/dimension-statutory-guard.test.ts fails if the filter parser leaks into balance sheet, balansrapport, kassaflöde, VAT, SIE or full-archive routes/generators, or if the catalog whitelist widens. MCP: new gnubok_get_dimension_pnl (reports:read); dimensions filter arg on get_trial_balance/get_income_statement/get_general_ledger with resolve-don't-select (names → registry codes, resolution echoes); query_journal totals fixed to aggregate the FULL match set (was silently slice-scoped while claiming otherwise) with an honest totals_scope field, plus group_by / group_by_dimension aggregation. Also: voucher-detail dim-6 badge now uses the registry name instead of the non-standard "PR" abbreviation (#859 review follow-up). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(dimensions): address #862 review — export disclosure, prior-column suppression, period-label honesty, route hardening - Filtered XLSX/PDF exports now carry the partial-view disclosure past the file boundary (BFNAR 2013:2): filename suffix (-dim6-p001), a "Filtrerad … — ej fullständig rapport" row on every sheet, and a header note/title line in the PDFs. - Resultatrapport drops the prior-year column when a dimension filter is active — project codes are time-limited under K2/K3, so "this code last year" may be a different project (same rule as narrowed date ranges). - dimension-pnl no longer accepts fromDate: the matrix is cumulative from period_start by design (closing-balance semantics), and the period label now states exactly that instead of echoing a lower bound that was never applied. Routes/MCP tool updated to toDate-only. - dimension-pnl routes 404 on an unknown/foreign period id and cap dim_no to 4 digits (matching the MCP tool's PostgREST-path guard, which the generator now also enforces itself). - Statutory-guard test's generateTrialBalance call-site scan is paren-aware instead of a 300-char window; added fully-untagged and injection-guard test cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
241959513b |
Fix/mcp and req (#753)
* feat(api): test-mode API keys force dry-run on the v1 REST API A key created with mode='test' (prefix gnubok_sk_test_) binds to the real company, but the v1 wrapper forces dry_run on every write so nothing is persisted or sent. Mutations on endpoints that can't be simulated (dryRunSupported=false or unregistered) are refused with 403 TEST_KEY_WRITE_BLOCKED — fail-closed. Reads pass through unchanged and every test-key response carries X-Gnubok-Mode: test. Live keys are unaffected (mode defaults to 'live'). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(invoices): company default "Vår referens" + per-line sales-account override Add company_settings.default_our_reference (settings form, schema, type); the invoice editor pre-fills our_reference from it on new invoices only, never overwriting an edited draft. Separately, add an optional per-line försäljningskonto (class-3) override in the editor — left blank, the engine still derives the revenue account from the VAT rate, and reverse-charge/export lines ignore the override. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(invoices): render a Swish payment QR on invoice PDFs Build the Swish "Type C" QR payload offline (no Swish API call) and embed it as a PNG in the invoice PDF payment box when Swish display is enabled, the invoice is in SEK, and the amount is positive. Also surface the invoice number in the payment box. Wired through every PDF render path: send, mark-sent and pdf routes (both legacy and v1), the recurring-schedule sender, and the staged-send commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(bookkeeping): draft exclusion + correction-chain collapse on verifikationslista Extend list_fiscal_period_entries_with_related with two opt-in params: p_exclude_draft (keep drafts off the committed list — they get their own surface) and p_collapse_corrections (render a correction group as the single live correction, hiding the mechanical storno and the reversed original). Both default false; nothing is deleted, every voucher keeps its number, and a "show all" toggle exposes the full chain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reports): link multi-year SIE periods so resultatrapport shows the prior year SIE import now sets fiscal_periods.previous_period_id in both directions when creating a period, so multi-year files chain correctly regardless of #RAR order. A backfill migration repairs periods imported before this (idempotent; only touches NULL links on first-of-month periods). generateResultatrapport falls back to the date-adjacent prior period when the chain is still null, so the comparison column works for legacy data too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(articles): hide the VAT field for non-momsregistrerade companies The article form reads company_settings.vat_registered and, when false, hides the moms field and forces vat_rate to 0 on submit — mirroring the invoice editor so a non-VAT-registered company never sets a rate it can't charge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(import): allow file-based imports in the sandbox Bank-file, CSV/Excel and SIE imports run entirely on uploaded data with no external service, so they're now reachable in the sandbox. Only the API-backed options that need live third-party credentials (PSD2 bank connection, provider migration) stay disabled. Updates the sandbox notice copy to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(bookkeeping): add edit draft functionality for journal entries * feat(database): add default "Vår referens" column to company_settings for invoicing * fix(tests): set SHOW_SWISH_ON_INVOICE to false in PDF template mocks * @ fix(payments): use roundOre for Swish amount formatting Replace naive Math.round(x*100)/100 with roundOre from @/lib/money to satisfy the antipattern guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @ --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
a9b43ebeb7 |
Bug/vat selection warning (#583)
* refactor: update VAT handling logic for non-registered sellers and improve related comments * chore: gate automated email flows behind 503 responses Disables user-facing access to invoice payment reminders and salary payslip email sending. Underlying lib code (reminder-processor, PDF templates, notification_settings) is preserved for easy re-enable. - Invoice reminders cron route returns 503; settings UI section removed. - Payslip send route returns 503; original implementation kept as _sendPayslipsImpl for future re-enable. - Push notifications were already extension-disabled, no change needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: remove Recapt feedback widget Strips the third-party Recapt SDK and its floating feedback bubble from the app. The in-app contact form keeps working via the existing email channel (/api/support/contact). Drops the Recapt entries from the CSP and the subprocessor list in the privacy policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: reject meaningless rättelser in correctEntry Guard against zero-economic-effect corrections in the storno engine: - Reject when proposed lines net to zero on every account (e.g. 1930 debit 100 / 1930 credit 100), which would erase the original posting without representing any affärshändelse (BFL 5 kap. 5 §). - Reject when proposed lines are an exact multiset match of the original entry — a rättelse must actually change something. New MeaninglessCorrectionError wired through bookkeepingErrorResponse (HTTP 400) and the Swedish error translator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add date-range picker to resultat- and balansrapport Adds optional from/to date filtering to the four operational financial reports (resultatrapport, balansrapport, income-statement, balance-sheet) so users can view a month, quarter, or custom range inside a fiscal year without leaving the report. Defaults to YTD; "Hela året" preserves the prior full-period behaviour (URL-identical, cache-stable). - trial-balance engine accepts optional fromDate/toDate, rolling prior in-period activity into IB and clamping period activity to the window - 12 API routes accept and validate from_date/to_date query params - ReportDateRange chip picker persists preset per company, only renders on the four relevant tabs - FiscalYearSelector now emits the period object so the range picker has bounds without an extra fetch - PDF/XLSX filenames reflect the chosen range - Resultatrapport drops the prior-year column when narrowed (full-year vs partial-year would mislead) - 11 new tests (engine + parser); all existing report tests pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat: add support for marking journal entries as "no document required" - Introduced a new sidecar table `journal_entry_no_doc_required` to track entries that do not require separate documentation (e.g., bank fees, interest). - Implemented API routes for creating and deleting exemptions, including validation and authorization checks. - Added a toggle component in the UI to allow users to mark entries as exempt, with an optional reason. - Updated relevant tests to cover the new functionality, including RLS checks and cascading deletes. - Enhanced existing schemas and types to accommodate the new `vat_amount` field for supplier invoice items. * fix: address PR review findings on no-doc-required + VAT changes - pg-real cascade test wraps DELETE in gnubok.allow_delete='true' txn so the immutability trigger bypass fires (mirrors delete_last_voucher RPC). - Clamp supplier-invoice item vat_amount to <= line_total * vat_rate via Zod refinement (with 1-öre rounding tolerance) so the manual override can't inflate the 2641 debit beyond the statutory ceiling. - groupVatByRate falls back to line_total * rate when stored vat_amount is 0 with a positive rate, so legacy/import paths leaving the column at its NOT NULL DEFAULT 0 don't silently understate ruta 48. - ReportDateRange todayIso() and preset endpoints use local date components instead of toISOString() (UTC) — fixes the midnight-to-02:00 off-by-one that truncated a day from YTD / this-month / this-quarter for Swedish users. - NoDocRequiredToggle restores the previous reason on failed POST/DELETE so the rolled-back toggle state stays consistent with the rendered reason. - Document the company-scoped (not user-scoped) DELETE authorization policy on the no-document-required route. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4822649c26 |
feat(reports): split operational Resultatrapport/Balansrapport from formal Räkning views (#363)
* feat(reports): add Resultatrapport and Balansrapport (operational reports) Per user feedback (Anders Gengård): Swedish accounting practice (BFL 6 kap, ÅRL Bilaga 1-3) distinguishes operational reports (Resultatrapport / Balansrapport, used during the year for reconciliation, account-level detail with numbers) from formal statements (Resultaträkning / Balansräkning, part of årsbokslut/årsredovisning, ÅRL uppställningsform, no account numbers). Until now gnubok only had a hybrid version under "Bokslut" that did neither well. This adds the operational pair as their own reports under a new "Löpande rapporter" section on the Reports page. Resultaträkning and Balansräkning under "Bokslut" are kept untouched (their yellow ÅRL 2:7 § draft disclaimer stays — it's appropriate there). Saldobalans moves into the new operational section. Both new generators reuse generateTrialBalance — Balansrapport filters to classes 1-2 with IB/UB/förändring; Resultatrapport filters to classes 3-8, calls trial balance for the previous period (via fiscal_periods.previous_period_id) and joins per account so the user sees current vs prior side-by-side. Account 8999 is excluded the same way generateIncomeStatement excludes it. 13 new unit tests cover grouping, prior-period join, account-class exclusions, zero-row filtering, and the missing-period fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(reports): show Balanscheck on Balansrapport Addresses the most material PR review finding (raised by both the Swedish compliance bot and Greptile): BalansrapportReport returned total_assets_ub and total_equity_liabilities_ub but the UI never displayed them, so the user could not verify that books balance. generateBalansrapport now also returns: - beraknat_resultat = total_assets - total_eq_liab (Fortnox/Visma convention: residual on the balance side; equals current-year P&L during a running year, drops to 0 once year-end closing posts 8999 → 2099) - is_balanced from the underlying trial balance — that's the meaningful integrity check (a missing IB row or continuity break shows up as an imbalanced TB) UI gets a Balanscheck card showing the three totals plus a Balanserar / Balanserar ej verdict. Other PR review items (Föregående header polish, inline subtotal diff rounding, class-8 filter scope, 2099 caveat, terminology disclaimer) are non-blocking and deferred. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reports): correct BAS class labels and add bokslut caveat Addresses three findings from the Swedish compliance bot's review of the prior commit: - Class 6 label dropped the informal '(forts.)' marker — '6 Övriga externa kostnader' is the BAS-correct heading. - Balansrapport class 2 label expanded to 'Eget kapital, obeskattade reserver, avsättningar och skulder' to match ÅRL Bilaga 1. The old label hid 21xx (periodiseringsfond, överavskrivningar) and 22xx (avsättningar) which matter for AB users. - Beräknat resultat row in the Balanscheck card now reads 'Beräknat resultat (ej bokslutsjusterat)' so the residual is not misread as a confirmed profit figure pre-closing. Skipped the bot's 8910/8999 finding: 8910 is 'Skatt på årets resultat' (regular tax expense), not a closing account; 8999 is the only BAS closing account, so the existing exclusion is correct. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |