c90ccc9c429277299fd4ecec0652cd8b6fa080be
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bf7773d74b |
feat(settings): standard verifikationsserier for new companies, opt-in action for existing (#2358)
* feat(settings): standard verifikationsserier for new companies, opt-in action for existing (#2184) A new company_settings row now defaults to the standard series set (A manual/bank, B kundfakturor, C inbetalningar, D leverantörsfakturor, E utbetalningar, H periodisering, I bokslut, K lön, L kontantfaktura, M moms) instead of everything on A. The set lives once, as the exhaustive STANDARD_VOUCHER_SERIES_MAP in the resolver; a pg-real test holds the column default equal to it and to the source_type CHECK. Existing rows are not remapped: the per-type settings form gets an "Använd standarduppsättningen" action that fills the set for review and save through the existing PUT, so the switch is a deliberate, audited act rather than a mid-year numbering change nobody decided. Payment rows bound to the other bokföringsmetod are dimmed, not hidden. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvMaHcTnwAfxzgYD1fGYX1 * test(bookkeeping): fresh company_settings row asserts the standard series set, not all-A voucher-series-defaults.pg.test.ts codified the pre-#2184 column default (every source type on A). Migration 20260906210500 replaces that default with the standard set, so the "freshly inserted row" case now asserts the representative letters and full equality with STANDARD_VOUCHER_SERIES_MAP. The explicit-override case keeps proving a company's own layout replaces the default wholesale. No other pg or tool test asserted on the old map. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvMaHcTnwAfxzgYD1fGYX1 --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
0c854ac54f |
feat(settings): let a company name each verifikationsserie letter (#2336)
* feat(settings): let a company name each verifikationsserie letter
The series pickers show a fixed preset label next to every letter (A
Redovisning ... M Momsrapport, Fortnox's layout). A byrå that lays its
series out differently sees a wrong or missing name in every dropdown: a
partner running löner on L saw "Kontantfaktura" in the verifikat form and
asked for the series name.
- company_settings.voucher_series_labels JSONB ({"L": "Lön"}), keys A-Z,
values 1 to 40 chars, CHECK on the JSON shape. Display only; the engine
never reads it.
- UpdateSettingsSchema validates the map, trims names and strips empty
values so a cleared field removes the name.
- voucherSeriesLabel(letter, labels) is the one place that decides what a
letter is called: company name, then preset, then empty.
buildVoucherSeriesOptions replaces the three near-identical option
builders in the verifikat form and the two settings pickers.
- The Verifikationsserier list in settings edits the names: rows are the
union of used, configured and named letters, one save button.
- The SIE import review's two series pickers show the name too.
Migration applied to staging (metjnjrhvujscngnpzdv) as 20260906131300.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBj3hzDUb8sgtxTvyAjFWC
* fix(settings): keep imported series in the list and unsaved names through a refetch
Skeptic pass on the series-name editor refuted two things:
- The rewritten list filtered voucher_sequences to single letters, dropping
multi-character series (FT, LB, SKV, ...) that 54 production companies
carry over from Fortnox and Bokio imports; the old list showed them with
their highest number. Rows are now every used series plus the configured
and named letters; only single-letter series get a name input, since
those are what the pickers offer and the schema accepts.
- The draft re-seeded on the identity of settings.voucher_series_labels,
and the settings hook revalidates on window focus with a fresh object, so
unsaved typing was wiped after any earlier save on the page. The re-seed
is now keyed on the serialized content of the saved names.
Also folds the "new series are created on first use" footnote back into
the group help, which the rewrite had dropped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBj3hzDUb8sgtxTvyAjFWC
* fix(settings): name the default-series options and enforce the label shape in the database
Review pass on #2336:
- CodeRabbit: the Standardserie selector under Bokföring still rendered
bare letters; it now shows the same name the other pickers do, through
voucherSeriesLabel.
- Compliance swarm (SOC 2 PI1.1, low): the key and length rules for
voucher_series_labels lived only in UpdateSettingsSchema. Migration
20260906134700 adds voucher_series_labels_valid(jsonb) and swaps the
object-only CHECK for one that mirrors the Zod rules (keys A-Z, values
non-blank strings of at most 40 characters), so a write that bypasses
/api/settings cannot store a map the pickers cannot handle. Applied to
staging with its schema_migrations row; verified against good, empty,
lowercase, blank, over-long, numeric and array inputs.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBj3hzDUb8sgtxTvyAjFWC
* test(pg): cover the voucher_series_labels CHECK against real Postgres
The coverage gate refuses a migration that adds a function without a
*.pg.test.ts. voucher_series_labels_valid(jsonb) and the constraint that
wraps it now have one: accepts the empty map and single-letter keys with
names of 1 to 40 characters, rejects lowercase and multi-letter keys,
blank, over-long, numeric and null values, arrays and scalars, and leaves
the row untouched after a refused write.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBj3hzDUb8sgtxTvyAjFWC
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
|
||
|
|
e5fba9471e |
feat(bookkeeping): verifikationsserie dropdown, wider Ny verifikat modal, balancing amount on focus (#2120)
Three fixes to the Ny verifikation modal. 1. Verifikationsserie is a closed dropdown, not a one-letter free-text field. Ships Fortnox's table verbatim from their Systemdokumentation (A Redovisning, B Kundfakturor, C Inbetalningar från kunder, D Leverantörsfakturor, E Utbetalningar till leverantörer, F Kassa, G Avskrivning, H Periodisering, I Bokslut, J Revisor, K Lön, L Kontantfaktura, M Momsrapport). A is deliberately Redovisning, not Kundfakturor: it is the general series manual entries land in, and migration 20260526120700 ships every source_type defaulting to 'A', so every existing company's A series already holds everything. Any letter the company already configured is appended so no existing value can fall out of the picker. 2. The modal goes from sm:max-w-3xl to sm:max-w-5xl lg:max-w-6xl. 3. Tabbing or clicking into an untouched amount proposes the outstanding difference, pre-selected so typing replaces it. Only fires when the row has an account, both amounts are empty, and the difference belongs on that side. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DnoyZCbfm2geRbwtUpi6XN |
||
|
|
d4c42fd8db |
fix(bookkeeping): journal search finds a voucher by its label, with a spinner while searching (#1808)
"Sök verifikationstext" only matched journal_entries.description, so typing A209 never returned voucher A209 itself: only other vouchers whose text mentioned it. Users read that as "the voucher is missing". A label-shaped needle (A209, a 209, A-209) now also matches voucher_series + voucher_number via a PostgREST OR (needle double-quoted so commas/parentheses stay literal); other needles keep the plain description ILIKE. parseVoucher accepts one space or hyphen between series and number. The search box shows a spinner while a server-side search is in flight; before, the only signal was the list dimming. Co-authored-by: Claude Fable 5 <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> |
||
|
|
f9ea9c0082 |
Add/pdf and templates (#705)
* fix(invoices): apply configured voucher series to payments + preview next voucher The booking engine resolves the series from default_voucher_series_per_source_type, but the global "Standardserie" dropdown wrote a separate field the engine ignored, and cash-method invoice payments (invoice_cash_payment) weren't exposed in settings — so configured series were silently dropped to "A". - Expose cash/private payment source types in the per-source-type form - Write the global default through to the map on save, keeping overrides - Resolve voucher-sequences/next by source_type (+date) to match the engine - Show the upcoming voucher (V2) in the payment dialog title - Share resolveInvoicePaymentSourceType so preview and booking can't drift Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(salary): keep AGI panel in sync with Skatteverket signing state The AGI panel mixed run-scoped generation state (agi_generated_at, agi_declarations) with period-scoped submission state (extension_data agi_submission_{period}), so the two could drift and present contradictory UI. Reconcile them: - Auto-detect a Mina Sidor BankID signature: while awaiting_signing, poll /agi/kvittenser on mount and on tab refocus so the panel flips to "signed" (hiding the signing actions) without a manual "Hamta kvittens" click. - Warn instead of offering to sign when the locked granskningsunderlag predates the run's latest AGI generation (draftIsStale) — avoids filing superseded figures. - Self-heal a stale "AGI-XML saknas" error once the run's AGI is (re)generated out-of-band (MCP/API/other tab). - Refetch the salary run on tab focus so agi_generated_at reflects out-of-band generation without a hard reload. - /agi/lasUpp now clears the cached agi_submission_{period} record, so unlocking drops the panel back to the pre-submission state instead of stranding it on a released "redo att signeras" draft. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: Implement VAT registration handling and invoice item line types - Added VAT registration check in commitCreateInvoice to set VAT rate to 0% for non-VAT registered companies. - Updated invoice creation logic to reflect 'exempt' VAT treatment and adjusted related fields accordingly. - Introduced support for free-text and blank spacer rows in invoice items by adding a new line_type field. - Enhanced invoice and credit note handling to accommodate new line types. - Added new localized messages for text rows in English and Swedish. - Created tests for salary run approval logic, ensuring bank details are validated correctly. - Implemented effective net payout calculation for salary runs, considering tax overrides. - Added SQL migrations to support new invoice item line types and accounting method awareness for linking invoices to vouchers. * feat(articles): artikelregister with revenue account + VAT rate per article Article register (non-inventory) with per-article VAT rate and optional BAS class-3 revenue-account override. Includes API routes, UI pages, MCP tools, pending-operation staging, and the activate-or-create account flow (ACCOUNTS_NOT_IN_CHART -> ActivateAccountsDialog, unknown numbers -> AddAccountDialog) reusing the journal entry UX. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bookkeeping): no-doc-required batch + bulk-missing endpoints Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(payments): supplier payment lines + cash-method invoice matching Shared payment-line proposal for supplier invoices, improved match-invoice/match-supplier-invoice flows (kontantmetoden-aware), and voucher-link support without requiring a 151x clearing entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bookkeeping): new journal entry dialog, SIE import tweaks, misc New journal entry dialog component, journal list/page updates, invoice editor updates, SIE import adjustments, transaction ingest and api-key tweaks, pr-agent workflow update. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(invoices): implement tax reduction features and localization updates * feat(tests): add VAT registration gate to pending operations commit tests --------- Co-authored-by: Claude Opus 4.8 (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> |