Files
MP-Manager/scripts/fuente_prospecto_workflow.py
2026-05-30 14:31:19 -06:00

151 lines
6.0 KiB
Python

#!/usr/bin/env python3
"""Step 2: update Fuente de Prospecto from Canal de Origen."""
import argparse
from tag_canal_origen_workflow import (
MAIN_LOCATION_ID,
build_contact_match_index,
contact_display_name,
find_exact_main_contact,
get_all_contacts,
get_custom_field_value,
get_main_account,
get_schemas,
load_locations,
safe_update_contact_field,
script_audit,
select_locations,
)
from canal_origen_resolver import classify_source
SUCURSAL_VALUE = "SUCURSAL"
LEAD_DIGITAL_VALUE = "LEAD DIGITAL"
def process_location(account, dry_run=False, skip_correct=True, run_id=None, sync_main=False):
location_id = account["location_id"]
token = account["token"]
name = account["nombre"]
contact_schema = get_schemas(location_id, token, "contact")["contact"]
canal_field_id = contact_schema.get("Canal de Origen")
fuente_field_id = contact_schema.get("Fuente de Prospecto")
main_account = get_main_account() if sync_main and location_id != MAIN_LOCATION_ID else None
main_contact_schema = {}
main_index = None
if main_account:
main_contact_schema = get_schemas(main_account["location_id"], main_account["token"], "contact")["contact"]
main_contacts = get_all_contacts(main_account["location_id"], main_account["token"])
main_index = build_contact_match_index(main_contacts)
if not canal_field_id or not fuente_field_id:
print(
f"\n{name} - missing required fields "
f"(Canal: {bool(canal_field_id)}, Fuente: {bool(fuente_field_id)})"
)
return 0
updated = 0
skipped = 0
contacts = get_all_contacts(location_id, token)
inconsistencies = 0
for contact in contacts:
if not script_audit.wait_if_paused_or_stopped(run_id):
print("\nDetención segura solicitada. Saliendo antes del siguiente contacto.")
break
canal_value = get_custom_field_value(contact, canal_field_id)
current_fuente = get_custom_field_value(contact, fuente_field_id)
source_value = contact.get("source")
source_tag = classify_source(source_value)
if (canal_value == SUCURSAL_VALUE) and source_tag in ("facebook-ads", "formulario"):
inconsistencies += 1
print(
f" INCONGRUENTE {contact_display_name(contact)} | Canal=SUCURSAL pero "
f"source='{source_value}' sugiere '{source_tag}' (se respeta Canal actual; "
"revisar Step 1)"
)
target_fuente = SUCURSAL_VALUE if canal_value == SUCURSAL_VALUE else LEAD_DIGITAL_VALUE
branch_changed = False
if skip_correct and current_fuente == target_fuente:
skipped += 1
else:
branch_changed = safe_update_contact_field(
run_id,
location_id,
contact,
fuente_field_id,
"Fuente de Prospecto",
target_fuente,
token,
dry_run,
)
if branch_changed:
updated += 1
print(
f" OK {contact_display_name(contact)} | "
f"Canal: {canal_value or '(vacio)'} -> Fuente: {target_fuente}"
)
if main_account and main_index and main_contact_schema.get("Fuente de Prospecto"):
main_contact, match_type = find_exact_main_contact(contact, main_index)
if main_contact:
main_canal_id = main_contact_schema.get("Canal de Origen")
main_canal_value = get_custom_field_value(main_contact, main_canal_id) if main_canal_id else canal_value
main_target_fuente = SUCURSAL_VALUE if main_canal_value == SUCURSAL_VALUE else LEAD_DIGITAL_VALUE
if safe_update_contact_field(
run_id,
main_account["location_id"],
main_contact,
main_contact_schema["Fuente de Prospecto"],
"Fuente de Prospecto",
main_target_fuente,
main_account["token"],
dry_run,
):
print(f" OK marca principal ({match_type}) {contact_display_name(main_contact)} -> Fuente: {main_target_fuente}")
print(
f"\n{name}: {updated} contactos actualizados, {skipped} ya correctos, "
f"{inconsistencies} incongruencias canal/source"
)
return updated
def main():
parser = argparse.ArgumentParser(description="GHL Step 2: Canal de Origen -> Fuente de Prospecto")
parser.add_argument("--location", help="Specific location ID to process")
parser.add_argument("--all", action="store_true", help="Process all branch locations from the CSV")
parser.add_argument("--include-main", action="store_true", help="Include the main brand account when using --all")
parser.add_argument("--dry-run", action="store_true", help="Preview changes without writing to GHL")
parser.add_argument("--no-skip", action="store_true", help="Update even if Fuente de Prospecto already matches")
parser.add_argument("--sync-main", action="store_true", help="Also sync exact matching contact in the main brand account")
parser.add_argument("--run-id", help="Audit run ID supplied by the dashboard")
args = parser.parse_args()
# Keep the imported helpers explicit so packaging/static checks see they are intentional.
_ = load_locations
print("\n" + "=" * 60)
print("GHL WORKFLOW STEP 2 - FUENTE DE PROSPECTO")
print("=" * 60)
if args.dry_run:
print("DRY RUN - no changes will be made\n")
total = 0
for account in select_locations(args):
try:
total += process_location(account, args.dry_run, not args.no_skip, args.run_id, args.sync_main)
except Exception as exc:
print(f"\nERROR {account['nombre']}: {exc}")
print(f"\n{'=' * 60}")
print(f"TOTAL: {total} contactos actualizados")
if __name__ == "__main__":
main()