Merge pull request '[session 3] docs(journal): reparse verify recipe + admin external-path trap' (#63) from docs/journal-reparse-verify-path into develop
CD - Develop (guide → bcos.dev) / build-and-deploy (push) Successful in 1m1s

This commit was merged in pull request #63.
This commit is contained in:
2026-07-24 11:27:51 -04:00
@@ -91,7 +91,7 @@ Protected (`/api/journal`, `JournalApiController`, `@PreAuthorize("isAuthenticat
Admin (`/api/journal/admin`, `AdminController`, all `@PreAuthorize("hasAnyRole('MSP_ADMIN', 'BCOS_ADMIN')")`):
- `POST /reload-rules`, `GET|PUT /rules`, `GET|PUT /rules/structured`, `GET /types`
- `POST /reparse-all`, `POST /parse-sync`, `POST /archive-now`
- `POST /reparse-all` (optional `?atmId=X&journalType=ATM|NH_TCR`) — **deletes** existing transactions for the matched files, resets them to PENDING, and re-queues async parse. This is how you **behaviourally verify a parser fix on prod**: re-parse one ATM, then check the rows (see the verify recipe in Gotchas). `POST /parse-sync?fileId=X` (synchronous, single file, diagnostics), `POST /archive-now`
- `POST /reconcile` — re-queue stranded PENDING/PARSING files (**run after every deploy**)
- `POST /migrate-storage`, `POST /sync-cassette-inventory`
- `GET|PUT /tcr-rules`, `POST /tcr-rules/reload`, `GET|PUT /tcr-rules/raw`
@@ -102,7 +102,9 @@ Also: `/api/journal/cheques/...` (`JournalImageQueryController`), `/api/simulato
- **`DB_URL` is a full JDBC string** — unlike sibling services that use split `DB_HOST/DB_PORT/DB_NAME`.
- **Admin endpoints require `hasAnyRole('MSP_ADMIN', 'BCOS_ADMIN')`**; read/query endpoints only require `isAuthenticated()`; agent upload routes require **no JWT** (agent-facing).
- **Post-deploy reconcile is mandatory** — `POST /api/journal/admin/reconcile` re-queues files stranded in PENDING/PARSING when the instance restarted; skipping it leaves files stuck forever. A scheduled recovery also re-queues files stuck in PARSING > 30 min (`JournalParsingService`).
- **Post-deploy reconcile is mandatory** — `POST /api/journal/admin/reconcile` re-queues files stranded in PENDING/PARSING when the instance restarted; skipping it leaves files stuck forever. A scheduled recovery also re-queues files stuck in PARSING > 30 min (`JournalParsingService`). Run it after **every** recreate, including a rolling worker recreate (returns `{total, queued}`; `0/0` = nothing stranded).
- **Admin external path drops the `/api/journal` segment.** nginx maps external `/journal/` → internal `/api/journal/`, so the admin endpoints are reached at `https://api.bcos.cloud/journal/admin/<x>` — **not** `/journal/api/journal/admin/<x>` (that double-prefixes → 404). Same for `reconcile`, `rules`, etc. (See platform.nginx-routing double-prefix trap.)
- **How to behaviourally verify a parser fix on prod** (the parse runs in the **worker** role, so recreate the workers first, on the new image): `POST /journal/admin/reparse-all?atmId=<ATM>` (BCOS_ADMIN) → poll `SELECT parse_status FROM journal_files WHERE atm_id=…` until all `PARSED` → check `journal_transactions` rows for the expected `amount_cents`/`fee_cents`. Dev usually can't exercise real receipt shapes (simulator data + broken dev uploads, devops#53), so a parser fix's first real check is often this prod reparse. Precedent: #47/#48 (masked JPR amount) verified 2026-07-24 — XB7270 SEQ 5049 `0`→`20000`/`400`.
- **Per-institution routing** — parse requests go to `journal.parse-requests.{INSTITUTION}` (uppercase); unknown/unmapped ATMs fall back to `...UNASSIGNED` so no data is lost before a dedicated worker exists.
- **`institution` is resolved once at parse time** from `atm_institution_map` — an ATM unmapped *then* leaves `institution` NULL (invisible to scoped reads). A scheduled re-resolve heals NULL rows once the device is provisioned (#52). See the Gotchas tab.
- **Dedup happens at two layers**: upload-time guard skips files already `PENDING`/`PARSING` in flight (`JournalUploadService`); parse-time `deduplicateSubTransactions` merges two-phase records that share a SEQ# (`JournalParser`).