Primer commit
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user