Skip to content

feat(bank): activate Bank Frick payout accounts (prd data migration)#4221

Open
TaprootFreak wants to merge 7 commits into
developfrom
feat/activate-bank-frick-prd
Open

feat(bank): activate Bank Frick payout accounts (prd data migration)#4221
TaprootFreak wants to merge 7 commits into
developfrom
feat/activate-bank-frick-prd

Conversation

@TaprootFreak

Copy link
Copy Markdown
Collaborator

Production data migration that activates the two new Bank Frick payout accounts, per docs/bank-frick-operations.md section 3.

What it does (prd only)

Guarded to ENVIRONMENT === 'prd' — a complete no-op on dev/loc/CI (there the Bank Frick registry comes from migration/seed/bank.csv). On prd it:

  • Inserts the two new Bank Frick rows (EUR/CHF) active (receive=true, send=true, sctInst=false, sendPriority=500), idempotent per IBAN (WHERE NOT EXISTS).
  • Renames the dormant legacy Bank Frick rows to Bank Frick (legacy) (§3.2) so (name, currency) no longer collides.
  • Seeds lastBankFrickDate:<bankId> per new row (§1) so polling starts at now, not epoch.
  • Removes the two default-off Frick process sentinels from disabledProcess — exactly the inverse of 1783944000000's seed, preserving any unrelated entries.

down() reverses every step, guarded the same way.

Routing

sendPriority=500 makes Frick the primary sender for EUR and CHF (below the incumbents' backfilled 1000), so new EUR+CHF payouts route to Frick. Set 2000 for fallback-only. Never 1000 — a top-priority tie that includes Frick is treated as misconfiguration and throws in getPayoutAccount.

Deliberate deviation from the manual-registry convention

§3 documents these as manual production steps and 1783944000000 intentionally never touches bank rows. Encoding them here is a deliberate decision for this activation; the migration is prd-guarded and fully idempotent so it cannot double-apply. The §6 sandbox checklist is handled operationally as part of this rollout, with the credential chain verified live on the first inbound poll after deploy.

Verification

Because the SQL is prd-guarded, CI does not exercise it — so it was validated directly against Postgres 17 with a prod-shaped fixture (legacy Frick rows + Olkypay/Yapeal): up() yields the two active rows, renames the three legacy rows, seeds both watermarks, strips only the two Frick sentinels (unrelated preserved), and leaves Olkypay/Yapeal untouched; down() reverses cleanly. node --check passes.

Activation

Merging + deploying this, together with enabling the payout flag in the deployment config, arms the Frick outbound rail. The first inbound poll after deploy is the live credential check; a bad credential fails closed (payouts remain pending, no funds move).

Prd-guarded data migration (ENVIRONMENT === 'prd', no-op elsewhere) that follows docs/bank-frick-operations.md section 3: inserts the two new Bank Frick rows (EUR/CHF) active, renames the dormant legacy rows, seeds the per-bank import watermark, and removes the two default-off Frick process sentinels. Idempotent (WHERE NOT EXISTS per IBAN, ON CONFLICT DO NOTHING). sendPriority=500 makes Frick the primary EUR/CHF sender. Validated against Postgres 17 with a prod-shaped fixture (up + down); CI does not exercise the prd-guarded SQL.
…k activation migration

Translate the sendPriority comment to English (CONTRIBUTING English-only, public repo) and document that down() is a hard IBAN-scoped removal that only reverts cleanly before the new rows are referenced by a payout. Comment-only; no SQL change.
…n() legacy rename

Replace the hard-coded 'bank_id_seq' with pg_get_serial_sequence('bank','id') (matching migration/seed/seed.js) so the prd-guarded migration cannot crash on boot if the production id sequence carries a different name after the MSSQL->PG cutover. Scope the down() legacy-rename symmetrically to up()'s selection (dormant rows, excluding the new IBANs). Re-validated against Postgres 17 (up + down). Comment/SQL-only.
…ict symmetry

The scoped down() rename is not a strict inverse of up() (up() only renames rows named exactly 'Bank Frick'); document the pre-existing '(legacy)'-row edge honestly instead of asserting it cannot occur. Comment-only.
… runbook named sequence

Two fixes for the activation migration: (1) INSERT ... ON CONFLICT (iban,bic) DO UPDATE so a row pre-created inactive by the manual runbook §3.1 procedure converges to the intended active state instead of silently no-op'ing; (2) keep the runbook §3.1 named-sequence setval ('bank_id_seq' + GREATEST) rather than pg_get_serial_sequence, which returns NULL (and crashes setval) if the post-cutover prod sequence is not OWNED by the column. Re-validated against Postgres 17: fresh insert, pre-existing-inactive convergence, and idempotent re-run all pass.
…entation

The step-3 header still claimed pg_get_serial_sequence after the code was reverted to the named 'bank_id_seq' sequence; align the header with the code and the inline rationale. Comment-only.
…revert explicitly

up() is defensively idempotent for the manual §3.1 pre-created path (ON CONFLICT DO UPDATE/DO NOTHING); document at the top of down() that the code-owned rollout makes down() a clean inverse, and that on the pre-created path down() is an intentional lossy hard-removal (rollback of a used rail is an Ops procedure). Trim the now-redundant bank-delete comment. Comment-only.
@TaprootFreak

Copy link
Copy Markdown
Collaborator Author

Ready-review preparation complete: 8 review passes (conformity + logic) iterated to zero findings.

Key changes made across the passes:

  • English-only comments (public repo).
  • Kept the runbook §3.1 named sequence bank_id_seq for the setval instead of pg_get_serial_sequence('bank','id') — the latter returns NULL (and setval(NULL,...) crashes this prd-only migration on boot) if the post-cutover production sequence is not OWNED BY the column; restored the GREATEST guard so the sequence only ever moves forward.
  • Row inserts use INSERT ... ON CONFLICT (iban,bic) DO UPDATE so a row pre-created inactive by the manual §3.1 procedure converges to the active state rather than silently no-op'ing.
  • Documented the down() rollout assumption and its intentional lossy hard-removal on the pre-created path.

Because the SQL is ENVIRONMENT === 'prd'-guarded, CI does not exercise it, so it was validated directly against Postgres 17 with a production-shaped fixture: fresh insert, pre-existing-inactive convergence, idempotent re-run, and down() reverse all pass.

@TaprootFreak TaprootFreak marked this pull request as ready for review July 15, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant