feat(inbox): say what an underlag would be booked as (#1514)
Nothing proposed a kontering for a document. The receipt hunt attaches paper and stops; "klart att bokföra" existed only as an idea. The right pane had extracted fields and no answer to the question the user is actually there to settle. POST /items/:id/suggest-booking answers it, read-only. The lines come from buildTransactionEntryLines, the same function the commit path and the pending-operations preview use, so what is shown cannot drift from what gets posted. That is the whole reason to compose the existing chain rather than write a second one. Derived on demand rather than stored on the row. A stored proposal goes stale against a corrected amount, a re-matched transaction or a template the company taught itself yesterday. The hunt deliberately does not compute it either: the nightly run is already at its time ceiling and a proposal nobody opens is wasted work. Five honest outcomes instead of one optimistic guess: - already_booked, checked against the transaction and not only the inbox row. Booking from Transaktioner, bulk-book or MCP stamps the transaction and leaves the inbox row untouched, so trusting the row alone proposed a second verifikat for money that already had one. - no_transaction, because without one there is no trusted amount, no settlement account and no learned counterparty. - no_mapping, which now includes the engine's own 6991 placeholder at confidence 0.1. Rendering that dressed "no idea" up as an answer one click from the ledger. - currency_unsupported on a foreign row matched by a mapping rule. mapping-engine's rule branch computes VAT from the transaction's own currency while every other line is SEK, so 100 EUR at 11.5 shows 20 kr of moms instead of 230. The entry balances, so nothing downstream catches it. The counterparty and static-template paths convert properly and are not withheld. - a proposal, with the provenance named correctly: template_id marks a static library template, and a learned konteringskarta match sets neither field. Read the other way round, the company's most trusted suggestion was labelled 'default', the same word the placeholder gets. The entity type is now resolved and passed. Left undefined it silently proposed enskild-firma accounts to aktiebolag. The settlement account is no longer applied twice, since evaluateMappingRules applies it on every return path and a second pass rewrote a legitimate 1930 leg. Both queries report a database failure as a failure instead of as "Posten hittades inte". Twenty-one tests, mostly about what the route must not do. Every guard was removed in turn to confirm a test fails without it. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Opus 5
parent
1e2b952f9c
commit
8b1e90abcc
@@ -861,3 +861,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-08-10] Validex round 3 falsified the round-2 reading: rule 237 (creditor PstlAdr mandatory) fires on the BGNR-to-BGNR path too, so rule 020's "required unless DbtrAcct is BGNR" only governs the Ctry element, not the address as a whole. Creditor PstlAdr is now emitted on every payment, TwnNm from the payee_city snapshot when known, and the preview warns (payee_city_missing) when the supplier register lacks a city, since 237 + 222 together make a town effectively mandatory at Swedbank and a Ctry-only address is rejected today, not from November.
|
||||
[2026-08-10] Tax table percent rows (>80 000 kr/month) drop ore via Math.floor: whole-krona rule (oretal bortfaller) per SFF 2011:1261 22 kap. 1 § as applied by Skatteverket's tabellavdrag guidance (the statute governs stated amounts; Skatteverket's tables and guidance apply the same truncation to computed skatteavdrag); an API response missing either table section (30B or 30%) is treated as API failure so the bundled fallback serves complete data instead of clamping. Incomplete bracket data (gaps, failed pagination pages, malformed kolumn values) fails loudly rather than withholding 0.
|
||||
[2026-08-10] Staging DB reconcile (metjnjrhvujscngnpzdv): the tracker had skipped everything from 20260721101500 to 2026-08-10 (105 local-only versions) while 25 rows existed only remotely. Renamed 10 remote rows to their repo versions (same name, MCP apply-time version drift: sandbox-cleanup consolidation, shopify, tax-depreciation, JEL index), deleted 7 superseded sandbox-iteration rows with no local file, and left 8 rows from unmerged branches (white-label brands/teams, vacation columns, agent-atom product tier) untouched since their content is deliberately live for the byra rigs. Older seed_agent_atom_bodies files register version-only: each seed is a full idempotent upsert with a version guard, so only the newest seed's content needs to run.
|
||||
[2026-08-11] suggest-booking derives the proposed kontering on demand rather than storing it on the inbox row or computing it in the receipt hunt: a stored proposal goes stale against a corrected amount, a re-matched transaction or a template the company taught itself since, and the nightly hunt is already at its 300 s ceiling for a proposal most rows never open. It composes the existing evaluateMappingRules -> buildTransactionEntryLines chain rather than a second one, so the shown lines cannot drift from the posted lines. It withholds the proposal entirely on a foreign-currency row that matched via the mapping_rules branch: mapping-engine.ts buildResult computes VAT from the transaction's own currency while every other line is SEK (its own NOTE tracks this), which understates ingaende moms by the exchange rate and still balances, so nothing downstream catches it. Guarding the surface was chosen over fixing buildResult in this PR because that changes posted VAT amounts across every caller; the counterparty and static-template paths already convert correctly and are not withheld.
|
||||
|
||||
Reference in New Issue
Block a user