15af0cd7eadcecafd6d17618d04bf67b4f736fd2
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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> |
||
|
|
74de71f7be |
feat(reports): PDF download for Resultatrapport and Balansrapport (#366)
* feat(reports): PDF download for Resultatrapport and Balansrapport User feedback after merging #363: "Ladda ner PDF saknas för de nya resultat- och balansrapporterna." The previous PR deferred PDFs to a follow-up; this is the follow-up. New operational PDF template (`operational-report-pdf-template.tsx`) with two exports — ResultatrapportPDF and BalansrapportPDF. Mirrors the visual style of the formal FinancialStatementPDF but **omits the yellow "Arbetsutkast – ej undertecknat" disclaimer**, which only belongs on draft årsredovisning per ÅRL 2:7 §. These are löpande reports, never an årsredovisning at any stage. Resultatrapport PDF: account / name / current period / prior period (prior column hidden when no previous fiscal period exists), grouped by BAS account class with subtotals and a "Beräknat resultat" summary line. Balansrapport PDF: account / name / IB / UB / förändring per class 1 and class 2, with the same Balanscheck card the on-screen view shows (Summa tillgångar, Summa eget kapital + reserver + skulder, Beräknat resultat ej bokslutsjusterat, Balanserar / Balanserar ej verdict). Wired up "Ladda ner PDF" buttons on both ResultatrapportView and BalansrapportView. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(reports): prevent PDF row truncation; align Balansrapport filename Two crucial fixes from the PR review: - Drop wrap={false} from the outer group <View> in both PDFs. With wrap=false on a group exceeding one A4 page, @react-pdf/renderer silently clips overflow rows. Large class 1 (80+ active accounts on a real company) was at risk of dropping rows from the rendered file with no warning. Outer group now wraps; wrap={false} retained on individual rows and the subtotal so neither breaks mid-line. - Balansrapport filename anchor changed from period.end to period.start to match the convention used by resultatrapport, balance-sheet, and income-statement PDF routes. The Swedish compliance bot preferred period.end (snapshot semantics), Greptile preferred period.start (cross-route consistency); the latter wins because predictable sorting/renaming matters for archived räkenskapsinformation. 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> |