15af0cd7eadcecafd6d17618d04bf67b4f736fd2
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
db8983ba9e |
Add/bokslut (#718)
* feat(arcim-migration): Briox provider with SIE-over-API import - Briox auth via account ID + application token (no app-level credentials); both tokens rotate on refresh and are persisted - New sie-fetcher pulls the general ledger as SIE through the provider API for Fortnox, Briox and Bjorn Lunden - Wizard stops on a failed SIE import and surfaces the real errors instead of proceeding to the misleading migrate-guard message - PROVIDER_SIE_ONLY_FORTNOX renamed to PROVIDER_SIE_NOT_SUPPORTED; new PROVIDER_TOKEN_INVALID for rejected provider credentials Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bookkeeping): per-line accruals (periodisering) on invoices and supplier invoices Defer revenue/costs per invoice line to 29xx/17xx interim accounts with automatic monthly dissolution (nightly cron + catch-up at registration), schedule cancellation on credit, year-end auto-detect exclusion for already-scheduled invoices, invoice-inbox service-period extraction for prefill, and an MCP tool to list schedules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bokslut): iXBRL arsredovisning generation and Bolagsverket digital filing Generate the annual report as iXBRL from a generated taxonomy registry (K2 element lists, taxonomy:generate/check scripts + CI guard), expose it via the fiscal-period API, and add the bolagsverket extension for digital submission to eget utrymme with webhook-driven status tracking (submissions table + pg tests, lifecycle events, year-end wizard UI). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(mcp): raise origin-guard test timeout to 20s The dynamic import pulls in the full server module; the parse alone flirts with the 5s default under full-suite parallel load. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add new scripts and documentation for K2 AB taxonomy generation and validation - Introduced `generate-taxonomy-registry.ts` to automate the generation of the iXBRL taxonomy concept registry from official element lists and tuple models. - Added `validate-ixbrl.mjs` for validating generated iXBRL reports against the official taxonomy package using Arelle. - Included new documentation files: - `k2-ab-arsredovisning-elementlista-2024-09-12_rev20250312_sv.xlsx` - `tuple-innehallsmodell-arsredovisning-k2-2024-09-12.xlsx` - `taxonomi-paket-2024-09-12_rev20250312.zip` * Add tests for bookkeeping accruals dissolution and supplier invoices - Implement tests for the POST /api/bookkeeping/accruals/[id]/dissolve route, covering success and error scenarios. - Add tests for the DELETE /api/supplier-invoices/[id] route, including authentication checks and validation of invoice deletion conditions. - Introduce tests for the Arcim migration provider client, ensuring token handling and error classification. - Create tests for the Bolagsverket extension, validating submission role enforcement and environment settings. - Add Zod schemas for Bolagsverket response payloads to ensure proper validation. - Implement tests for MCP server's list accrual schedules, confirming registration and scope mapping. - Add consistency tests for IXBRL document generation, ensuring duplicate facts and XML escaping are handled correctly. - Introduce typed domain errors for accrual schedules to improve error handling in the service. - Add tests for resolving consent with Briox token refresh concurrency, ensuring proper token management and error handling. * fix(tests): update payload size guard comments to reflect recent changes in tool descriptions and ceiling adjustments * fix(gitattributes): mark generated JSON files in bokslut taxonomy as linguist-generated * feat(migrations): add backfill for invoices.journal_entry_id and fallback for next_voucher_number user_id * feat(bokslut): enhance compliance and financial processing features with new submission details and security measures --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
205610d200 |
feat(mcp): distribution-channel client marker in MCP telemetry (#706)
* feat(mcp): distribution-channel client marker in MCP telemetry
Record an optional client marker on mcp.tool_called, mcp.tools_list_called
and mcp.resource_read events so per-channel adoption (e.g. the OpenClaw
skill) is measurable in event_log (180-day TTL).
- Server reads X-Gnubok-Client header, falling back to a ?client= query
param on the endpoint URL. Sanitized ([A-Za-z0-9._-]{1,64}, lowercased),
telemetry-only — same trust level as Mcp-Session-Id, never auth.
- The query param works with the already-published gnubok-mcp 1.0.1 via
GNUBOK_URL, so no npm release is required to start measuring.
- Bridge 1.1.0 additionally forwards GNUBOK_CLIENT as X-Gnubok-Client.
OAuth-path attribution via DCR client_name is a possible follow-up — DCR
is stateless today, so client_name isn't recoverable at token time.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(mcp): address PR #706 compliance findings
- ropa.yaml: declare the distribution-channel marker in the mcp.telemetry
processing activity (GDPR Art. 30 — RoPA was drifting from actual flow)
- bridge: mirror the server's allow-list on GNUBOK_CLIENT so an invalid
value degrades to no header instead of fetch() rejecting every request
- lib/events/types.ts: annotate client as client-supplied/telemetry-only
- test: pin that the allow-list runs on the percent-decoded ?client= value
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
bc61862e76 |
feat(agent): telemetry + CI-gate quick wins from the "AI systems that ship" audit (#677)
* feat(agent): telemetry completeness + durability, CI gates, commit_method provenance Quick wins from the "Building AI systems that ship" audit: - mcp.tool_called gains errorMessage (message_sv, truncated 500 chars) on all failure exits; new mcp.skill_loaded event on every gnubok_load_skill (all tiers) so atom usage is finally measurable - event_log: (event_type, created_at) index; cleanup cron keeps mcp.*/agent.* telemetry 180 days (delivery events stay 30) - CI: lint ratchet (npm run check:lint — 60 legacy errors baselined, fails only on NEW errors) and a pg-real coverage gate (migrations touching trigger/RPC/RLS/DEFERRABLE require a *.pg.test.ts change; escape hatch: -- pg-test: covered-by/skip) - journal_entries.commit_method CHECK widened with 'api_key'/'agent'; the MCP approve path records 'api_key' truthfully instead of 'user_accept' (agent_first_vision §8 P0-1). 'agent' is reserved — ALL MCP traffic (incl. claude.ai OAuth, whose access_token is a minted API key) authenticates as api_key today Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(import): derive opening balances from prior-year #UB when SIE lacks #IB (#675) SIE files exported without #IB 0 rows (only #UB -1) previously imported with zero opening balances. getEffectiveOpeningBalances() now derives IB from prior-year UB for balance-sheet accounts when explicit #IB is absent, surfaces the derivation as an info issue in the import preview, and excludes share-capital vouchers from opening-balance detection. Detection regexes are shared between parser and importer so the two checks cannot drift. 507 lib/import tests pass. (Authored in a parallel session in this checkout; included per request.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(review): address PR #677 bot findings — RoPA entry, execFileSync, gate scope note Triage of the compliance-swarm + Greptile findings: Applied: - .compliance/ropa.yaml: new mcp.telemetry processing activity declaring the 180-day mcp.*/agent.* retention, lawful basis, data categories, and the no-args/no-results minimisation (ISO A.8.10, GDPR Art.5(1)(c) — the retention split is now formally documented, referenced from the cron) - check-pg-test-coverage.mjs: execFileSync with argv array — no shell, so a hostile base-ref can't inject (ASVS V13.2.1); verified an injection attempt exits 2 without executing - check-pg-test-coverage.mjs: documented the PR-level (not per-migration) scope of the gate so reviewers know to check coverage per migration when a PR carries several risky migrations (Greptile P2) Acknowledged, no change: - errorMessage PII risk: messages are domain-mapped strings; event_log already persists far richer delivery payloads under the same RLS; now declared in ropa.yaml - cron error envelope: errorResponse maps to the canonical safe envelope and the endpoint is CRON_SECRET-gated - two-pass delete "partial state": TTL deletes are idempotent — the next daily run sweeps whatever a failed pass left behind - skill_loaded actorLabel/sessionId: mirrors the pre-existing mcp.tool_called payload; sessionId is the join key the analytics exist for Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
953980c875 |
Per-account bank reconciliation + overdue/inbox/privacy fixes (#619)
* feat(reconciliation): scope bank reconciliation per cash account via transactions.cash_account_id A company with two same-currency cash accounts (e.g. checking 1930 + a savings account) saw every SEK transaction on every account, and the status card summed across both — reconciliation filtered transactions by CURRENCY while filtering GL lines by ACCOUNT (issue #604). Bind each bank transaction to the cash_accounts row it settled on: - New nullable transactions.cash_account_id FK (ON DELETE SET NULL — a bank transaction is räkenskapsinformation, BFL 7 kap, and must survive cash-account deletion) + a best-effort 4-pass backfill. - All reconciliation/transaction queries scope to the selected account with a NULL->currency fallback, so legacy/un-backfilled rows never disappear mid-backfill. - ingestTransactions stamps cash_account_id from the batch's settlementAccount; categorize + manualLink resolve and use it. - Bank leg now books to the transaction's actual settlement account via applySettlementAccount (no-op for 1930), so interest/fees on a savings/EUR account reconcile instead of mis-booking to 1930. - manualLink cross-checks the transaction's account and requires a voucher line on the selected account (no silent cross-account links). - BankReconciliationView: quick-book menu for any settlement account, in-flight request abort on account/date switch, 500-row truncation notice, per-account state reset. - pg-real coverage for the FK, all backfill passes, account-scoped query isolation, and cross-company isolation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(supplier-invoices): stop marking paid invoices and credit notes as overdue update_overdue_supplier_invoices() (the daily pg_cron job) flipped every past-due 'registered'/'approved' row to 'overdue' without looking at the outstanding balance. Credit notes — created 'registered', remaining 0, due today — got flipped the next day, surfacing as "Förfallen" with "kvar att betala 0 kr"; so did any fully-paid invoice left in 'registered'/'approved'. Guard the cron on remaining_amount > 0.005 (the "fully paid" threshold used by the payment/match paths) and is_credit_note = false, and backfill the rows already mis-flagged (credit notes -> 'registered', paid -> 'paid' with paid_at stamped only when missing). pg-real coverage for the guarded function and the one-off backfill. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(invoice-inbox): refresh dokumentinkorg on realtime row changes The InvoiceInboxWorkspace only refetched on mount and on explicit in-component actions. When an inbox item was resolved out of band — the in-app agent sheet committing a staged create_supplier_invoice_from_inbox / book-direct op, the /pending page approving one, or another tab booking it — none of those paths called fetchItems(), so the booked underlag stayed in "Att göra" until a manual reload (issue #600). Add invoice_inbox_items to the supabase_realtime publication (mirrors the /pending fix in 20260520120100) and subscribe in the workspace, refetching the whole list on any change so derived status/counts/ordering stay authoritative. RLS scopes the channel to the user's company. fetchItems now preserves optimistic upload placeholders so a refetch firing mid-upload can't drop an in-flight row. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(privacy): disclose EU AI inference via Amazon Bedrock (eu-north-1) Update the privacy policy and DPA to state that AI inference, when AI features are enabled, runs inside the EU via Amazon Bedrock (eu-north-1, Stockholm) using Anthropic's Claude models — no transfer to a third country, prompts not retained after the call or used for model training. Add AWS as a subprocessor row and refresh the "last updated" dates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(migrations): rename invoice_inbox_realtime to avoid version collision main's #617 shipped 20260605120000_transactions_original_description.sql — the same version this branch used for the inbox-realtime publication. The Supabase migration tracker keys on the numeric version, not the filename, so the preview branch failed with a duplicate-key error on supabase_migrations.schema_migrations (version 20260605120000 already exists). Rename to the unique version 20260605120500; the body (ALTER PUBLICATION) is order-independent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reconciliation): align run guard with status; harden filter interpolation Addresses PR review (greptile + compliance swarm): - The v1 and core bank/run routes rejected an unknown account uniformly, including the default '1930', while the status routes were lenient for '1930'. A company reconciling its primary SEK account without a cash_accounts row got 200 from status but 400 from run. Make run match status: '1930' falls back to currency-only scoping (cashAccountId undefined); non-default unknown accounts are still rejected. Adds a test. - /api/transactions accepts a user-supplied `currency` query param that was interpolated raw into a PostgREST .or() filter. Reject anything that isn't a 3-letter ISO code — RLS already scopes to the company, but an unsanitized value could otherwise malform/widen the filter. Assert currency/cashAccountId shape in scopeTransactionsToAccount as well. - categorize: log (instead of silently swallowing) a cash_accounts settlement-account lookup error, so a fall-back-to-1930 mis-booking is observable in the audit log. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(migrations): correct backfill UPDATE..FROM join; idempotent realtime publication Two SQL errors that only surface on real Postgres (CI pg-real + Supabase preview) — the unit suite mocks Supabase, so neither was caught locally. - Backfill pass (a): `UPDATE transactions t ... FROM journal_entry_lines jel JOIN cash_accounts ca ON ca.company_id = t.company_id` referenced the UPDATE target `t` inside the FROM join's ON clause, which Postgres rejects ("invalid reference to FROM-clause entry for table t"). Move the company match to WHERE; the JOIN now relates jel<->ca only. Semantics unchanged. - invoice_inbox_realtime: `ALTER PUBLICATION ... ADD TABLE` is not idempotent (SQLSTATE 42710 if the table is already a member). The earlier version-collision push partially applied it on the Supabase preview branch, so the re-apply errored. Guard with a pg_publication_tables existence check. Both statements validated against a real Postgres: the single-line tx binds, the two-bank-line transfer stays NULL, and the publication add runs twice cleanly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(migrations): backfill pass (c) uses array_agg, not min(uuid) Postgres has no min() aggregate for uuid, so pass (c)'s min(id) raised "function min(uuid) does not exist" on apply (CI pg-real + Supabase). The HAVING count(*) = 1 already guarantees one row per group, so (array_agg(id))[1] returns that single id. Validated the full backfill (all four passes) and the overdue migration against a real Postgres: every pass binds / falls through as intended, and the overdue guard + backfill produce the right statuses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(compliance): add RoPA entry for Amazon Bedrock AI inference (GDPR Art.30) The privacy policy now discloses AI inference (transaction categorization + document/receipt OCR) via Amazon Bedrock as a processing activity, but .compliance/ropa.yaml had no matching Art.30 record. Add it: opt-in consent basis, EU-region (eu-north-1) inference with no third-country transfer, prompts not retained or used for model training. Mirrors the privacy-page disclosure shipped in this PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (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. |
||
|
|
a652dcae1a |
Skv/e2e overview (#515)
* feat(agi): refactor AGI XML generation and data handling - Remove deprecated AGI field codes from field-codes.ts. - Update generate-declaration.ts to include new employee fields and handle absence data with stable specification numbers. - Enhance XML generation in xml-generator.ts to support new flags for housing benefits and adjusted benefits. - Introduce new database migrations to support: - `removed_from_agi` flag for tombstoning individuppgifter. - `benefits_adjusted` flag for tracking adjustments to benefits. - `franvaro_specifikationsnummer` for stable absence event identification. - `housing_benefit_type` to differentiate between housing benefit types. * feat: Implement strict validation for AGI employee data and introduce pre-flight validation schemas - Added Zod schemas for validating employee data in AGI declarations to ensure all required fields are present and correctly typed, preventing silent errors during processing. - Introduced AGI pre-flight validation schemas for Skatteverket endpoints to validate individual and head unit submissions before sending to the API. - Created a new audit log table for tracking all outbound calls to Skatteverket, ensuring compliance and traceability for AGI and moms submissions. - Implemented advisory locks in the database to manage concurrent updates to absence specification numbers, enhancing data integrity. - Added compliance documentation for GDPR processing activities related to AGI and moms submissions, detailing data handling and retention policies. * feat: Extend DELETE RLS policy to protect 'declined' signatures in årsredovisning * fix: Update date handling in salary absence migrations to use immutable year-month key * fix: Update SELECT policy in skatteverket_api_audit_log to use IN clause for company_id * feat: Add skatteverket_api_audit_log and salary_absence_franvaro_audit tables with RLS policies and immutable triggers |