fix(migrations): re-issue the invoice payee migrations skipping migration-reset source companies (#2260)

* fix(migrations): re-issue the invoice payee migrations skipping migration-reset source companies

#2233 merged, but its first migration (20260903150000) failed on prod at
the backfill's INSERT into invoice_payee_defaults:

  ERROR: Archived migration reset source records are immutable (P0001)

The insert fires the SECURITY DEFINER mirror into company_settings, and
one of the companies with a legacy payment map is a migration-reset
source, whose rows are immutable by trigger. The migration rolled back as
a whole, prod has neither table nor column, and every migration merged
after it is queued behind the failure.

Same fix as #2249 used for the country backfill: both entry branches of
the backfill now skip companies present in company_migration_resets, and
both files are re-issued under fresh versions (20260904010000 and
20260904011000) so Supabase applies them in order after everything that
landed today. The failed versions never applied on prod, so no orphan;
staging applied them by hand and its schema_migrations rows must be
renamed to match (see DECISIONS.md).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(migrations): make the re-issued payee migrations rerunnable

pg-upgrade builds from main, where the first issue (20260903150000)
already ran, then applies the re-issued file on top: the composite UNIQUE
constraint already existed. Every statement in both files is now guarded
(constraint DO blocks, CREATE TABLE/INDEX IF NOT EXISTS, DROP POLICY /
DROP TRIGGER IF EXISTS before each CREATE), so staging and the preview
branches that applied the first issue take the re-issue cleanly too, and
prod, which never applied it, is unaffected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Mattsson
2026-09-04 06:45:56 +02:00
committed by GitHub
co-authored by Claude Fable 5.1
parent 91e2c66afc
commit 9ab823a43c
12 changed files with 52 additions and 24 deletions
+2 -2
View File
@@ -635,7 +635,7 @@ const CreateInvoiceBaseSchema = z.object({
.optional(),
received_date: optionalIsoDate,
// Which of the company's bank accounts the invoice asks the customer to pay
// to (migration 20260903193000). Omitted/null = the per-currency default.
// to (migration 20260904011000). Omitted/null = the per-currency default.
// The route checks the account belongs to the company, is flagged as a
// payee and is usable for the invoice currency.
payment_cash_account_id: z
@@ -2221,7 +2221,7 @@ const InvoicePaymentAccountSchema = z.object({
/**
* PATCH /api/cash-accounts/[id]: the verifikationsserie override plus the
* payee fields (migration 20260903150000). Payee keys share the field rules
* payee fields (migration 20260904010000). Payee keys share the field rules
* of InvoicePaymentAccountSchema so the settings form, the legacy settings
* writers and this route agree on what a valid bankgiro is.
*/
+1 -1
View File
@@ -99,7 +99,7 @@ export function normalizeInvoicePaymentAccount(
* written when the invoice chose a bank account and refreshed at issue).
* 2. The company's payment account for the invoice currency
* (company_settings.invoice_payment_accounts, mirrored from the default
* cash account per currency since migration 20260903150000).
* cash account per currency since migration 20260904010000).
* 3. For SEK only, the legacy flat bank columns.
*/
export function resolveInvoicePaymentAccount(
+1 -1
View File
@@ -706,7 +706,7 @@ async function commitUpdateCompanySettings(
}
// The bank columns mirror the default SEK payee account (migration
// 20260903150000): write the change through FIRST so a failure leaves
// 20260904010000): write the change through FIRST so a failure leaves
// nothing half-written, and the invoice PDF prints what the agent set.
try {
await propagateLegacyPayeeWrite(supabase, companyId, validated.changes)
+2 -2
View File
@@ -214,7 +214,7 @@ export const AUDITED_TABLES = [
// a behandlingsregel in the same sense.
'cash_accounts',
// Which bank account customer invoices pay to, per currency (migration
// 20260903150000).
// 20260904010000).
'invoice_payee_defaults',
] as const
@@ -425,7 +425,7 @@ const MAPPING_RULE_FIELDS: Record<string, string> = {
*/
const CASH_ACCOUNT_FIELDS: Record<string, string> = {
voucher_series: 'Verifikationsserie',
// Payee fields (migration 20260903150000): what customer invoices print.
// Payee fields (migration 20260904010000): what customer invoices print.
bank_name: 'Bank',
clearing_number: 'Clearingnummer',
account_number: 'Kontonummer',