Files
clientflow_backend/scripts/repair_odoo_sale_order_customer_names.py
2026-06-09 22:55:58 +01:00

90 lines
3.0 KiB
Python

"""Repair Odoo sale-order references accidentally stored as customer names.
v4.9.25.3 fixes the source of the issue. This script repairs rows already
created by older v4.9.25.x builds where customers.name became S00xxx although
metadata.raw_customer_payload.partner_name contains the real fiscal customer.
Dry-run by default. Use --apply to update rows.
"""
from __future__ import annotations
import argparse
from typing import Any, Dict, List
from sqlalchemy import text
from app.db import engine
def _partner_name_from_metadata(metadata: Any) -> str:
if not isinstance(metadata, dict):
return ""
raw = metadata.get("raw_customer_payload")
if not isinstance(raw, dict):
return ""
partner_name = str(raw.get("partner_name") or "").strip()
if partner_name:
return partner_name
partner_id = raw.get("partner_id")
if isinstance(partner_id, (list, tuple)) and len(partner_id) > 1:
return str(partner_id[1] or "").strip()
return ""
def find_rows() -> List[Dict[str, Any]]:
with engine.begin() as conn:
rows = conn.execute(text("""
SELECT id::text, name, tax_id, email, metadata
FROM customers
WHERE name ~ '^S[0-9]{4,}'
AND metadata->>'source_system' = 'odoo'
ORDER BY updated_at DESC
""")).mappings().all()
result: List[Dict[str, Any]] = []
for row in rows:
partner_name = _partner_name_from_metadata(row.get("metadata"))
if partner_name and not partner_name.upper().startswith("S00"):
data = dict(row)
data["new_name"] = partner_name
result.append(data)
return result
def apply(rows: List[Dict[str, Any]]) -> int:
updated = 0
with engine.begin() as conn:
for row in rows:
conn.execute(text("""
UPDATE customers
SET name = :new_name,
metadata = COALESCE(metadata, '{}'::jsonb) || jsonb_build_object(
'odoo_sale_order_name_repaired', true,
'previous_customer_name', CAST(:old_name AS TEXT)
),
updated_at = now()
WHERE id = CAST(:id AS UUID)
"""), {"id": row["id"], "old_name": row["name"], "new_name": row["new_name"]})
updated += 1
return updated
def main() -> None:
parser = argparse.ArgumentParser(description="Repair Odoo S00xxx customer names.")
parser.add_argument("--apply", action="store_true", help="Apply updates. Default is dry-run.")
args = parser.parse_args()
rows = find_rows()
print(f"Clientes Odoo com nome S00xxx reparáveis: {len(rows)}")
for row in rows[:50]:
print(f"- {row['name']} -> {row['new_name']} | NIF {row.get('tax_id') or '-'} | email {row.get('email') or '-'}")
if len(rows) > 50:
print(f"... mais {len(rows)-50}")
if not args.apply:
print("Dry-run. Para aplicar: repetir com --apply")
return
print(f"Atualizados: {apply(rows)}")
if __name__ == "__main__":
main()