""" Import additional customer details from legacy DB into ERPNext. Adds custom fields and populates: - invoice_delivery_method: Email/Paper/Both - is_commercial: Commercial account flag - is_bad_payer: Mauvais payeur flag - tax_category_legacy: Tax group - contact_name_legacy: Contact person - tel_home/tel_office/cell: Phone numbers - mandataire: Authorized representative - exclude_fees: Frais exclusion flag - notes_internal: Internal notes (misc) - date_created_legacy: Account creation date Run inside erpnext-backend-1: /home/frappe/frappe-bench/env/bin/python /home/frappe/frappe-bench/import_customer_details.py """ import frappe import pymysql import os from datetime import datetime, timezone os.chdir("/home/frappe/frappe-bench/sites") frappe.init(site="erp.gigafibre.ca", sites_path=".") frappe.connect() print("Connected:", frappe.local.site) # ═══════════════════════════════════════════════════════════════ # STEP 1: Create custom fields on Customer # ═══════════════════════════════════════════════════════════════ print("Creating custom fields on Customer...") CUSTOM_FIELDS = [ {"fieldname": "billing_section", "label": "Facturation", "fieldtype": "Section Break", "insert_after": "legacy_section"}, {"fieldname": "invoice_delivery_method", "label": "Envoi facture", "fieldtype": "Select", "options": "\nEmail\nPapier\nEmail + Papier", "insert_after": "billing_section"}, {"fieldname": "is_commercial", "label": "Compte commercial", "fieldtype": "Check", "insert_after": "invoice_delivery_method"}, {"fieldname": "is_bad_payer", "label": "Mauvais payeur", "fieldtype": "Check", "insert_after": "is_commercial"}, {"fieldname": "exclude_fees", "label": "Exclure frais", "fieldtype": "Check", "insert_after": "is_bad_payer"}, {"fieldname": "billing_col_break", "label": "", "fieldtype": "Column Break", "insert_after": "exclude_fees"}, {"fieldname": "tax_category_legacy", "label": "Groupe taxe", "fieldtype": "Select", "options": "\nFederal + Provincial (9.5%)\nFederal seulement\nExempté", "insert_after": "billing_col_break"}, {"fieldname": "contact_section", "label": "Contact détaillé", "fieldtype": "Section Break", "insert_after": "tax_category_legacy"}, {"fieldname": "contact_name_legacy", "label": "Contact", "fieldtype": "Data", "insert_after": "contact_section"}, {"fieldname": "mandataire", "label": "Mandataire", "fieldtype": "Data", "insert_after": "contact_name_legacy"}, {"fieldname": "tel_home", "label": "Téléphone maison", "fieldtype": "Data", "insert_after": "mandataire"}, {"fieldname": "contact_col_break", "label": "", "fieldtype": "Column Break", "insert_after": "tel_home"}, {"fieldname": "tel_office", "label": "Téléphone bureau", "fieldtype": "Data", "insert_after": "contact_col_break"}, {"fieldname": "cell_phone", "label": "Cellulaire", "fieldtype": "Data", "insert_after": "tel_office"}, {"fieldname": "fax", "label": "Fax", "fieldtype": "Data", "insert_after": "cell_phone"}, {"fieldname": "notes_section", "label": "Notes", "fieldtype": "Section Break", "insert_after": "fax"}, {"fieldname": "notes_internal", "label": "Notes internes", "fieldtype": "Small Text", "insert_after": "notes_section"}, {"fieldname": "email_billing", "label": "Email facturation", "fieldtype": "Data", "insert_after": "notes_internal"}, {"fieldname": "email_publipostage", "label": "Email publipostage", "fieldtype": "Data", "insert_after": "email_billing"}, {"fieldname": "date_created_legacy", "label": "Date création (legacy)", "fieldtype": "Date", "insert_after": "email_publipostage"}, ] for cf in CUSTOM_FIELDS: existing = frappe.db.exists("Custom Field", {"dt": "Customer", "fieldname": cf["fieldname"]}) if existing: print(" {} — already exists".format(cf["fieldname"])) continue try: doc = frappe.get_doc({ "doctype": "Custom Field", "dt": "Customer", **cf, }) doc.insert(ignore_permissions=True) print(" {} — created".format(cf["fieldname"])) except Exception as e: print(" {} — ERR: {}".format(cf["fieldname"], str(e)[:80])) frappe.db.commit() print("Custom fields done.") # ═══════════════════════════════════════════════════════════════ # STEP 2: Load legacy data # ═══════════════════════════════════════════════════════════════ print("\nLoading legacy data...") conn = pymysql.connect( host="10.100.80.100", user="facturation", password="VD67owoj", database="gestionclient", cursorclass=pymysql.cursors.DictCursor ) with conn.cursor() as cur: cur.execute(""" SELECT id, customer_id, invoice_delivery, commercial, mauvais_payeur, tax_group, contact, mandataire, tel_home, tel_office, cell, fax, misc, email, email_autre, date_orig, frais, ppa, notes_client, address1, address2, city, state, zip FROM account WHERE status = 1 """) accounts = cur.fetchall() conn.close() print("Active legacy accounts: {}".format(len(accounts))) # Customer mapping cust_map = {} custs = frappe.db.sql('SELECT name, legacy_account_id FROM "tabCustomer" WHERE legacy_account_id IS NOT NULL AND legacy_account_id > 0', as_dict=True) for c in custs: cust_map[c["legacy_account_id"]] = c["name"] print("Customer mapping: {}".format(len(cust_map))) # ═══════════════════════════════════════════════════════════════ # STEP 3: Update customers # ═══════════════════════════════════════════════════════════════ INVOICE_DELIVERY = {1: "Email", 2: "Papier", 3: "Email + Papier"} TAX_GROUP = {1: "Federal + Provincial (9.5%)", 2: "Federal seulement", 3: "Exempté"} def ts_to_date(ts): if not ts or ts <= 0: return None try: return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d") except (ValueError, OSError): return None print("\nUpdating customers...") updated = 0 skipped = 0 errors = 0 for a in accounts: acct_id = a["id"] cust_name = cust_map.get(acct_id) if not cust_name: skipped += 1 continue updates = {} if a["invoice_delivery"]: updates["invoice_delivery_method"] = INVOICE_DELIVERY.get(a["invoice_delivery"], "") if a["commercial"]: updates["is_commercial"] = 1 if a["mauvais_payeur"]: updates["is_bad_payer"] = 1 if a["frais"]: updates["exclude_fees"] = 1 if a["tax_group"]: updates["tax_category_legacy"] = TAX_GROUP.get(a["tax_group"], "") if a["contact"]: updates["contact_name_legacy"] = a["contact"] if a["mandataire"]: updates["mandataire"] = a["mandataire"] if a["tel_home"]: updates["tel_home"] = a["tel_home"] if a["tel_office"]: updates["tel_office"] = a["tel_office"] if a["cell"]: updates["cell_phone"] = a["cell"] if a["fax"]: updates["fax"] = a["fax"] if a["misc"]: updates["notes_internal"] = a["misc"] if a["email"]: updates["email_billing"] = a["email"] if a["email_autre"]: updates["email_publipostage"] = a["email_autre"] created = ts_to_date(a["date_orig"]) if created: updates["date_created_legacy"] = created if not updates: continue # Truncate long values for Data fields (varchar 140) for field in ["contact_name_legacy", "mandataire", "tel_home", "tel_office", "cell_phone", "fax", "email_billing", "email_publipostage"]: if field in updates and updates[field] and len(str(updates[field])) > 140: updates[field] = str(updates[field])[:140] # Build SET clause set_parts = [] values = [] for field, val in updates.items(): set_parts.append('"{}" = %s'.format(field)) values.append(val) values.append(cust_name) try: frappe.db.sql( 'UPDATE "tabCustomer" SET {} WHERE name = %s'.format(", ".join(set_parts)), values ) updated += 1 except Exception as e: errors += 1 if errors <= 5: print(" ERR {}: {}".format(cust_name, str(e)[:100])) frappe.db.rollback() if updated % 1000 == 0: frappe.db.commit() print(" Progress: {}/{}".format(updated, len(accounts))) frappe.db.commit() print("\nUpdated: {} customers".format(updated)) print("Skipped (no mapping): {}".format(skipped)) print("\n" + "=" * 70) print("DONE") print("=" * 70)