From 19cc37d42a56478216653c7dd030bfaf9f9fda98 Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Tue, 1 Sep 2026 10:10:32 +0200 Subject: [PATCH] Add organization link repair script --- scripts/repair-organization-links.mjs | 127 ++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100755 scripts/repair-organization-links.mjs diff --git a/scripts/repair-organization-links.mjs b/scripts/repair-organization-links.mjs new file mode 100755 index 0000000..77e8ef2 --- /dev/null +++ b/scripts/repair-organization-links.mjs @@ -0,0 +1,127 @@ +#!/usr/bin/env node +import "dotenv/config"; +import pg from "pg"; + +const apply = process.argv.includes("--apply"); + +if (!process.env.DATABASE_URL) { + console.error("DATABASE_URL fehlt. Bitte .env setzen oder DATABASE_URL vor dem Aufruf exportieren."); + process.exit(1); +} + +const updates = [ + [ + "tickets", + ` + UPDATE tickets t + SET organization_id = old_org.zammad_id + FROM organizations old_org + WHERE t.organization_id = old_org.id + AND t.organization_id <> old_org.zammad_id + AND COALESCE(t.customer_name, '') = old_org.name + ` + ], + [ + "sessions", + ` + UPDATE sessions s + SET organization_id = old_org.zammad_id + FROM organizations old_org + WHERE s.organization_id = old_org.id + AND s.organization_id <> old_org.zammad_id + AND COALESCE(s.customer_name, '') = old_org.name + ` + ], + [ + "recurring_billings", + ` + UPDATE recurring_billings rb + SET organization_id = old_org.zammad_id + FROM organizations old_org + WHERE rb.organization_id = old_org.id + AND rb.organization_id <> old_org.zammad_id + AND ( + EXISTS ( + SELECT 1 + FROM sessions s + WHERE s.recurring_billing_id = rb.id + AND COALESCE(s.customer_name, '') = old_org.name + ) + OR EXISTS ( + SELECT 1 + FROM tickets t + WHERE t.ticket_number = COALESCE(rb.ticket_number, 'Fix#' || rb.id) + AND COALESCE(t.customer_name, '') = old_org.name + ) + ) + ` + ] +]; + +const remainingSql = ` + SELECT 'tickets' AS table_name, COUNT(*)::int AS repairable + FROM tickets t + JOIN organizations old_org ON old_org.id = t.organization_id + WHERE t.organization_id IS NOT NULL + AND old_org.zammad_id <> t.organization_id + AND COALESCE(t.customer_name, '') = old_org.name + UNION ALL + SELECT 'sessions' AS table_name, COUNT(*)::int AS repairable + FROM sessions s + JOIN organizations old_org ON old_org.id = s.organization_id + WHERE s.organization_id IS NOT NULL + AND old_org.zammad_id <> s.organization_id + AND COALESCE(s.customer_name, '') = old_org.name + UNION ALL + SELECT 'recurring_billings' AS table_name, COUNT(*)::int AS repairable + FROM recurring_billings rb + JOIN organizations old_org ON old_org.id = rb.organization_id + WHERE old_org.zammad_id <> rb.organization_id + AND ( + EXISTS ( + SELECT 1 + FROM sessions s + WHERE s.recurring_billing_id = rb.id + AND COALESCE(s.customer_name, '') = old_org.name + ) + OR EXISTS ( + SELECT 1 + FROM tickets t + WHERE t.ticket_number = COALESCE(rb.ticket_number, 'Fix#' || rb.id) + AND COALESCE(t.customer_name, '') = old_org.name + ) + ); +`; + +const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL }); +const client = await pool.connect(); + +try { + await client.query("BEGIN"); + + const resultRows = []; + for (const [table, sql] of updates) { + const result = await client.query(sql); + resultRows.push({ table, [apply ? "updated" : "would_update"]: result.rowCount ?? 0 }); + } + + if (apply) { + await client.query("COMMIT"); + console.log("Organisationszuordnungen wurden korrigiert."); + } else { + await client.query("ROLLBACK"); + console.log("Dry-Run: Es wurde nichts gespeichert. Mit --apply wirklich ausfuehren."); + } + + console.table(resultRows); + + const remaining = await pool.query(remainingSql); + console.log("Noch eindeutig reparierbare Alt-Zuordnungen:"); + console.table(remaining.rows); +} catch (error) { + await client.query("ROLLBACK").catch(() => undefined); + throw error; +} finally { + client.release(); + await pool.end(); +}