First production sweep of /import-documents (921 Bokio receipts) surfaced
two defects that together dropped 7 of 666 resolvable receipts:
- The idempotency key was company-wide (company_id, sha256), but the same
file content legitimately backs several verifikat (one arrende contract
attached to each year's arrende voucher, one insurance letter on two
vouchers). The second and later verifikat silently lost their underlag.
The key is now (company_id, sha256, journal_entry_id).
- Bokio's uploads list occasionally declares the wrong contentType (a JPEG
stored as image/png); magic-byte validation then correctly rejects the
mismatch, failing a perfectly good receipt. The importer now sniffs the
real format from the bytes (detectFileMagic, now exported from the
document service) and only falls back to the declared type when no
signature is recognised. The synthesised filename extension follows the
effective type.
Signed-off-by: Jonas Hagberg <jonas@lindan.se>
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>
* feat(arcim): import Bokio underlag and link to verifikat
Adds an optional, re-runnable step that pages the Bokio /uploads, resolves
each receipt's target verifikat via the SIE-preserved voucher number, and
archives it through the document service linked to the journal entry. Closes
the gap where neither the SIE GL import nor the entity import carries the
receipts/underlag attached to each verifikat.
- lib/providers/bokio: getBytes() binary download + an attachments resource
module (uploads list, GUID->voucher index, per-upload download); pageSize
capped at 100, file type taken from the upload's contentType since the
download is octet-stream
- importProviderDocuments: bulk in-memory resolution keyed on (fiscal period,
series, number) — scoped per fiscal year because Bokio restarts numbering at
V1 each year; idempotent on (company_id, sha256) so re-runs don't duplicate
the undeletable BFL-linked rows
- POST /import-documents route, kept off the migration critical path because
the Bokio document API is rate-limited (200 req/60s)
- journal_entry_id link only for v1; reuses the document-service link path
(same module as #804) rather than forking it
Closes#786
Signed-off-by: Jonas Hagberg <jonas@lindan.se>
* fix(arcim): stable pagination order + account for unresolvable receipts
Addresses two findings from a Codex review pass on the import step:
- Add .order('id') to the paged journal_entries / document_attachments /
fiscal_periods reads. fetchAllRows pages with .range(), and PostgREST paging
without a deterministic order can skip/repeat rows once a table exceeds one
page (journal_entries crosses 1000 across several migrated years), which
would defeat both voucher resolution and the sha256 dedup.
- Keep every upload carrying a journalEntryId in scope instead of pre-filtering
on a resolvable voucher ref, so a receipt whose Bokio entry number didn't
parse (or resolves to no verifikat) is counted as unmatched rather than
silently dropped from the best-effort report.
Tests: add unresolvable-ref and zero-uploads cases; mock now supports .order().
Signed-off-by: Jonas Hagberg <jonas@lindan.se>
---------
Signed-off-by: Jonas Hagberg <jonas@lindan.se>