#!/usr/bin/env python3 """List/revert risky fiscal suggestions auto-applied from domain-only matches.""" from __future__ import annotations import argparse import json from sqlalchemy import text from app.db import engine DOMAIN_MATCHES = ( "email_principal_dominio", "email_dominio_empresa_associada", "contacto_email_dominio", "dominio", "email_dominio", "website_dominio", ) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--apply", action="store_true", help="revert accepted domain-only suggestions and unlink matching opportunity customer") args = parser.parse_args() with engine.begin() as conn: rows = conn.execute(text(""" SELECT s.id::text, s.opportunity_id::text, s.suggested_customer_id::text, s.suggested_name, s.suggested_nif, s.match_type, s.confidence, o.customer_name, o.customer_email, o.local_customer_id::text AS current_customer_id FROM fiscal_customer_suggestions s JOIN opportunities o ON o.id = s.opportunity_id WHERE s.status = 'accepted' AND s.auto_applied = TRUE AND s.match_type = ANY(:matches) ORDER BY s.updated_at DESC """), {"matches": list(DOMAIN_MATCHES)}).mappings().all() print(f"Sugestões por domínio auto-aplicadas: {len(rows)}") for r in rows: print(f"- {r['customer_name']} <{r['customer_email']}> -> {r['suggested_name']} / {r['suggested_nif']} | {r['match_type']} | {r['confidence']} | opp={r['opportunity_id']}") if not args.apply: print("Dry-run. Para reverter: repetir com --apply") return updated = 0 with engine.begin() as conn: for r in rows: # Only unlink if the opportunity is still linked to the same customer suggested by this risky suggestion. if r["suggested_customer_id"] and r["current_customer_id"] == r["suggested_customer_id"]: conn.execute(text(""" UPDATE opportunities SET local_customer_id = NULL, metadata = COALESCE(metadata, '{}'::jsonb) || jsonb_build_object( 'domain_match_auto_apply_reverted', true, 'domain_match_reverted_suggestion_id', :suggestion_id, 'domain_match_reverted_customer_name', :customer_name, 'domain_match_reverted_at', now() ), updated_at = now() WHERE id = CAST(:opportunity_id AS UUID) """), { "opportunity_id": r["opportunity_id"], "suggestion_id": r["id"], "customer_name": r["suggested_name"], }) conn.execute(text(""" UPDATE fiscal_customer_suggestions SET status = 'rejected', auto_applied = FALSE, resolved_by = 'domain_match_safety_review', resolved_at = now(), reason = COALESCE(reason, '') || ' | reverted: domain-only auto-apply is unsafe', updated_at = now() WHERE id = CAST(:id AS UUID) """), {"id": r["id"]}) updated += 1 print(f"Revertidas: {updated}") if __name__ == "__main__": main()