Primer commit

This commit is contained in:
2026-05-30 14:31:19 -06:00
commit a35d26fac0
277 changed files with 265240 additions and 0 deletions
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env python3
"""Genera un XLSX con el resumen de sucursales con drift de custom fields
que fueron resueltas durante la sesión 2026-05-23."""
import datetime
import os
import sys
import openpyxl
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
HEADER_FONT = Font(bold=True, color="FFFFFF", size=11)
HEADER_FILL = PatternFill("solid", fgColor="305496")
THIN = Side(border_style="thin", color="B4B4B4")
BORDER = Border(top=THIN, bottom=THIN, left=THIN, right=THIN)
def style_header(ws, cols):
for cell in ws[1]:
cell.font = HEADER_FONT
cell.fill = HEADER_FILL
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
cell.border = BORDER
ws.row_dimensions[1].height = 36
ws.freeze_panes = "A2"
for col_letter, width in cols.items():
ws.column_dimensions[col_letter].width = width
def style_body(ws):
for row in ws.iter_rows(min_row=2):
for cell in row:
cell.alignment = Alignment(vertical="top", wrap_text=True)
cell.border = BORDER
def main():
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
wb = openpyxl.Workbook()
# === Sheet 1: Sucursales reales con problemas únicos ===
ws1 = wb.active
ws1.title = "Sucursales reales"
ws1.append([
"#", "Nombre Sucursal", "Location ID", "Categoría",
"Detalle del problema", "Resolución aplicada", "Records tocados",
])
style_header(ws1, {"A": 4, "B": 28, "C": 24, "D": 22, "E": 70, "F": 65, "G": 18})
sucursales = [
(1, "85960 - MP - Cd. Carmen", "XkduzafvwsrWcEFg6Qlj", "Canary inicial",
"6 fieldKeys divergentes (patrón estándar) + duplicado Fuente de Prospecto con 2 fieldKeys distintos.",
"Sucursal piloto de Fase 2. Migración masiva + force-delete del duplicado fuente_del_prospecto.",
"6 fields + 3 records"),
(2, "85935 - MP - Pilares", "uZnMH5bO6MXTHcgHeyq9", "Orphan con twin",
"Orphan opportunity.fuente_del_prospecto coexistía con twin clean opportunity.fuente_de_prospecto.",
"Force-delete del orphan vía migrate_branch_fieldkeys.py (twin tenía los valores replicados).",
"1 field"),
(3, "85966 - MP - Uruapan", "FoQWuksh4wQjPbVVZ8ZQ", "Conflicto case",
"Orphan contact.fuente_del_prospecto con 1 record (gabriela ramos), valor 'Sucursal' (title case) vs 'SUCURSAL' en twin.",
"Delete del orphan. Valor canónico ya existía en clean (mayúsculas, alineado con Marca).",
"1 field + 1 record"),
(4, "85931 - MP - Marina Nacional", "HvDw9Eg3rjrwkbQJXqfi", "Merge contactcontact*",
"4 orphans con doble prefijo contactcontact* (marca/año/version/modalidad del vehiculo), cada uno con 1 record único del contacto 'aide montes'. Plus campo regional legítimo 'Canciones'.",
"Merge orphan→clean para 'aide montes' (4 PUTs). Delete de 4 orphans. 'Canciones' borrado manualmente por el usuario.",
"5 fields + 4 records"),
(5, "85938 - MP - SENDERO", "UsHXqoj2l6ND7Uc7sEo2", "Missing field",
"Faltaba completamente opportunity.fuente_de_prospecto (no había ni clean ni divergent).",
"Field creado from-scratch copiando metadata (dataType SINGLE_OPTIONS + opciones) de Marca.",
"1 field creado"),
(6, "85976 - MP - Cancún", "uJEn2iuUficuml9zxAnt", "Caso complejo",
"Vehículo: 7 records con valores complementarios distintos en orphan vs clean. Modalidad: NO tenía clean target, había 2 fields divergentes (21+4 records con valores).",
"Vehículo: regenerar desde contact (marca+version+año) o concatenar donde contact vacío. Modalidad: crear clean from-scratch, 23 PUTs con prioridad contact>div>orphan, delete de los 2 divergentes.",
"2 fields + 4 records (Veh) + 1 field nuevo + 2 fields borrados + 23 records (Mod)"),
]
for row in sucursales:
ws1.append(row)
style_body(ws1)
# === Sheet 2: Cuentas DEMO ===
ws2 = wb.create_sheet("DEMOs")
ws2.append(["#", "Cuenta", "Location ID", "Problema", "Resolución"])
style_header(ws2, {"A": 4, "B": 28, "C": 24, "D": 70, "E": 65})
demos = [
(7, "Monte Providencia DEMO", "Vf7qQl3L9vakJ8hDtQ8e",
"Orphan NBSP _copy (CHECKBOX vacío). Campo de prueba 'Quoted Price' (TEXT). opportunity.canal_de_origen TEXT huérfano (el mismo que se borró en Marca al inicio). opportunity.opportunityvehiculo orphan doble prefijo. Display name desactualizado 'Fuente de Posible Cliente' vs Marca 'CANAL DE ORIGEN'.",
"NBSP + Quoted Price + canal_de_origen TEXT + opportunityvehiculo borrados (todos sin records con valor). Rename de Fuente de Posible Cliente → CANAL DE ORIGEN."),
(8, "0001 - MP - Qro DEMO", "Z64WQKORPVwXb5mn68Ef",
"4 orphans contactcontact* con valores únicos del contacto 'nombre test' (mismo patrón que Marina). Faltaba opportunity.fuente_de_prospecto (mismo caso que Sendero).",
"Merge 'nombre test' orphan→clean (4 PUTs + 4 deletes). Field creado from-scratch."),
]
for row in demos:
ws2.append(row)
style_body(ws2)
# === Sheet 3: Resumen ejecutivo ===
ws3 = wb.create_sheet("Resumen")
ws3.append(["Métrica", "Valor"])
style_header(ws3, {"A": 50, "B": 50})
resumen = [
("Audit inicial", "506 hallazgos"),
("Audit final", "0 hallazgos"),
("Reducción", "-100%"),
("Sucursales totales auditadas", "49 (47 producción + 2 DEMO)"),
("Sucursales reales con tratamiento individual", "6 (Cd. Carmen, Pilares, Uruapan, Marina Nacional, SENDERO, Cancún)"),
("Cuentas DEMO con problemas", "2 (Monte Providencia DEMO, 0001 Qro DEMO)"),
("", ""),
("Total fields migrados (mass migration)", "281 production + 2 DEMO = 283"),
("Total orphans eliminados", "52"),
("Total PUTs de merge orphan→clean", "35"),
("Total PUTs de regeneración canónica (Cancún)", "27"),
("Total fields creados from-scratch", "2 (Sendero, Qro DEMO opportunity.fuente_de_prospecto)"),
("Total renames cosméticos", "3"),
("Snapshots JSON generados", "Uno por cada operación destructiva, en /migrations/"),
("Data loss", "CERO"),
("", ""),
("Fecha sesión", datetime.datetime.now().strftime("%Y-%m-%d")),
]
for row in resumen:
ws3.append(row)
style_body(ws3)
for row in ws3.iter_rows(min_row=2):
row[0].font = Font(bold=True)
# === Sheet 4: Patrón base masivo ===
ws4 = wb.create_sheet("Patron base masivo")
ws4.append(["Nota"])
style_header(ws4, {"A": 100})
ws4.append([
"Las 48 sucursales MP completas tenían los 6 fieldKeys divergentes históricos "
"(cotizacin, cundo, visita_a_sucursal, _ltima_, atendi, de_empeo). Se resolvieron "
"con la migración masiva (281 fields) sin requerir tratamiento individual. Por eso no "
"aparecen en la pestaña principal — el problema era idéntico en todas y la solución "
"fue el mismo script (migrate_branch_fieldkeys.py)."
])
ws4["A2"].alignment = Alignment(vertical="top", wrap_text=True)
ws4.row_dimensions[2].height = 80
ws4["A2"].border = BORDER
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT_DIR not in sys.path:
sys.path.insert(0, ROOT_DIR)
from paths import REPORT_DRIFT
os.makedirs(REPORT_DRIFT, exist_ok=True)
out_path = os.path.join(
REPORT_DRIFT,
f"sucursales_con_drift_resuelto_{datetime.datetime.now().strftime('%Y%m%d')}.xlsx",
)
wb.save(out_path)
print(f"XLSX generado: {out_path}")
if __name__ == "__main__":
main()