gigafibre-fsm/scripts/migration/cleanup_customer_status.py
louispaulb 101faa21f1 feat: inline editing, search, notifications + full repo cleanup
- InlineField component + useInlineEdit composable for Odoo-style dblclick editing
- Client search by name, account ID, and legacy_customer_id (or_filters)
- SMS/Email notification panel on ContactCard via n8n webhooks
- Ticket reply thread via Communication docs
- All migration scripts (51 files) now tracked
- Client portal and field tech app added to monorepo
- README rewritten with full feature list, migration summary, architecture
- CHANGELOG updated with all recent work
- ROADMAP updated with current completion status
- Removed hardcoded tokens from docs (use $ERP_SERVICE_TOKEN)
- .gitignore updated (docker/, .claude/, exports/, .quasar/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 07:34:41 -04:00

93 lines
3.8 KiB
Python

"""
Disable customers with no active Service Subscriptions.
These are legacy accounts (moved, cancelled, etc.) that were imported as enabled.
Run inside erpnext-backend-1:
/home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/cleanup_customer_status.py
"""
import frappe
import os
import time
from datetime import datetime
os.chdir("/home/frappe/frappe-bench/sites")
frappe.init(site="erp.gigafibre.ca", sites_path=".")
frappe.connect()
frappe.local.flags.ignore_permissions = True
print("Connected:", frappe.local.site)
T_TOTAL = time.time()
# ═══════════════════════════════════════════════════════════════
# PHASE 1: Identify customers to disable
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 1: IDENTIFY INACTIVE CUSTOMERS")
print("="*60)
# Customers that are active but have NO active subscriptions
to_disable = frappe.db.sql("""
SELECT c.name FROM "tabCustomer" c
WHERE c.disabled = 0
AND NOT EXISTS (
SELECT 1 FROM "tabService Subscription" ss
WHERE ss.customer = c.name AND ss.status = %s
)
""", ("Actif",))
to_disable_names = [r[0] for r in to_disable]
print("Customers to disable (no active subscriptions): {}".format(len(to_disable_names)))
# ═══════════════════════════════════════════════════════════════
# PHASE 2: Disable them
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 2: DISABLE CUSTOMERS")
print("="*60)
if to_disable_names:
# Batch update in chunks of 1000
for i in range(0, len(to_disable_names), 1000):
batch = to_disable_names[i:i+1000]
placeholders = ", ".join(["%s"] * len(batch))
frappe.db.sql("""
UPDATE "tabCustomer" SET disabled = 1
WHERE name IN ({})
""".format(placeholders), tuple(batch))
frappe.db.commit()
print(" Disabled batch {}-{}".format(i+1, min(i+1000, len(to_disable_names))))
print("Disabled {} customers".format(len(to_disable_names)))
# ═══════════════════════════════════════════════════════════════
# PHASE 3: VERIFY
# ═══════════════════════════════════════════════════════════════
print("\n" + "="*60)
print("PHASE 3: VERIFY")
print("="*60)
by_status = frappe.db.sql("""
SELECT disabled, COUNT(*) as cnt FROM "tabCustomer"
GROUP BY disabled ORDER BY disabled
""", as_dict=True)
print("Customer status after cleanup:")
for r in by_status:
label = "Active (Abonné)" if r["disabled"] == 0 else "Disabled (Inactif)"
print(" {}: {}".format(label, r["cnt"]))
# Cross-check: all active customers should have at least one active sub
active_no_sub = frappe.db.sql("""
SELECT COUNT(*) FROM "tabCustomer" c
WHERE c.disabled = 0
AND NOT EXISTS (
SELECT 1 FROM "tabService Subscription" ss
WHERE ss.customer = c.name AND ss.status = %s
)
""", ("Actif",))[0][0]
print("\nActive customers with no active subscription: {} (should be 0)".format(active_no_sub))
elapsed = time.time() - T_TOTAL
print("\n" + "="*60)
print("DONE in {:.1f}s".format(elapsed))
print("="*60)