feat(transactions): steer private marking in locked periods to ignore, with v1 and MCP ignore verbs (#1661) (#2031)
Decision (option a): a private marking stays a real booking (eget uttag/insattning), so it remains blocked in a locked or closed period; the legal escape for rows that are not affarshandelser is ignore. Private + locked now returns TX_CATEGORIZE_PRIVATE_PERIOD_LOCKED with remediation naming the ignore paths instead of a bare PERIOD_LOCKED, on all four categorize surfaces and the bulk driver. New v1 POST/DELETE /transactions/{id}/ignore (isTransactionBooked-based 409, idempotent) and a staged MCP gnubok_ignore_transaction (+ accounted_ alias, search visibility to respect the tools/list payload ceiling) with operation_type ignore_transaction; the CHECK pair 20260831070000/070001 rebuilds the constraint from main's newest list plus the new value. Dashboard toast gains an Ignorera i stallet action. Closes #1661
This commit is contained in:
@@ -53,6 +53,7 @@ Backfilled 2026-08-26 from merged PRs. Every item is additive (new endpoints, op
|
||||
- **Filed VAT declarations** (#1773, 2026-08-21): \`GET /skatteverket/vat-declarations?period_type=&year=&period=\` (scope \`compliance:read\`) reads one period's momsdeklaration as Skatteverket has it on file: \`submitted\` (SKV inlämnat) and \`decided\` (SKV beslutat), each \`null\` when nothing is on file. Live read, requires an active Skatteverket connection on the company.
|
||||
- **Company settings write** (#1405, 2026-08-04): \`PATCH /companies/{companyId}/settings\` (scope \`companies:write\`, risk medium, dry-run supported) updates bank details (\`bank_name\`, \`clearing_number\`, \`account_number\`, \`bankgiro\`, \`plusgiro\`, \`swish\`, \`iban\`, \`bic\`), \`contact_person\`, \`email\`, \`phone\`, \`website\` and \`invoice_email_texts\`, with the same validation as the MCP tool.
|
||||
- **Customer \`personal_number\`** (#1724 2026-08-20, #1788 2026-08-21): \`personal_number\` on customer create, bulk-create, detail, PATCH and list. Accepted only for \`customer_type=individual\`, stored encrypted, always returned masked (\`********-1234\`); sending the masked form on PATCH means "leave unchanged", \`null\` clears it. A personnummer-shaped \`org_number\` on a business customer type is refused (\`400 CUSTOMER_ORG_NUMBER_IS_PERSONAL\`); on an individual it is moved into \`personal_number\` and \`org_number\` is cleared, or refused with \`400 CUSTOMER_PERSONAL_NUMBER_CONFLICT\` when it differs from a \`personal_number\` in the same body.
|
||||
- **Ignore a transaction** (#1661, 2026-08-29): \`POST /transactions/{id}/ignore\` (scope \`transactions:write\`, risk low, idempotent, dry-run supported) marks an unbooked bank transaction as ignored without writing a verifikat, so it is allowed in a locked or closed period: the path for rows that are not business events (PSD2 ghost rows, duplicates, transfers that never executed). \`DELETE\` on the same path restores the row. A booked transaction (directly, via a payment allocation, or via a voucher link) is refused with \`409 TX_IGNORE_ALREADY_BOOKED\`. In the same change, \`/categorize\` and \`/batch-categorize\` answer \`is_business: false\` in a locked period with \`400 TX_CATEGORIZE_PRIVATE_PERIOD_LOCKED\` (\`details.suggested_action: "ignore"\`) instead of a bare \`PERIOD_LOCKED\`: a private marking is a real eget uttag / insättning booking, so the lock still applies to it.
|
||||
- **Transactions by bank account** (#1809, 2026-08-23): transaction list and detail carry \`cash_account_id\`; \`GET /transactions?cash_account_id=<uuid>\` filters to one bank account.
|
||||
|
||||
### Bookkeeping primitives + AP + compliance (Phase 4)
|
||||
|
||||
@@ -216,6 +216,7 @@ The ledger is SEK-denominated; foreign amounts are converted to SEK automaticall
|
||||
- **Settlement account selection matters.** The wrong settlement account silently breaks bank reconciliation later. \`1930\` (företagskonto) is the SEK default; a foreign-currency bank account uses its own asset account (e.g. \`1932\` for USD). \`/imports/bank\` resolves the settlement account automatically — to set it explicitly, ingest via \`POST /transactions/ingest\` with \`settlement_account\`.
|
||||
- **Cash-method companies and partial payments don't mix.** If \`company_settings.accounting_method = 'cash'\` and you try to match a partial payment, the response is \`VALIDATION_ERROR\` rather than booking accrual entries: cash-method cannot model the per-installment moms event correctly (bokslutsmetoden reports moms at payment, per installment). Either book the partial payment as a separate categorisation or switch to accrual.
|
||||
- **Batch-categorize is partial-success by default.** If one item hits a locked period, the others still commit. The summary block tells you the totals; check per-item \`ok\` flags.
|
||||
- **Private is a booking; ignore is not.** \`is_business: false\` books eget uttag / insättning, so in a locked or closed period it returns \`TX_CATEGORIZE_PRIVATE_PERIOD_LOCKED\`. A row that is not a business event at all (a PSD2 ghost row, a duplicate from a reconnect, a transfer that never executed) should be ignored instead: \`POST /transactions/{id}/ignore\` writes no verifikat and is allowed in a locked period; \`DELETE\` on the same path restores the row. Real purchases and owner withdrawals still need the period unlocked.
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
Reference in New Issue
Block a user