Phase 1: 833 Items + 34 Item Groups + custom fields (ISP speeds, RADIUS, legacy IDs) Phase 2: 6,667 Customers + Contacts + Addresses via direct PG (~30s) Phase 3: Tax template QC TPS+TVQ + 92 Subscription Plans Phase 4: 21,876 Subscriptions with RADIUS data CRITICAL: ERPNext scheduler is PAUSED — do not reactivate without explicit go. Includes: - ARCHITECTURE-COMPARE.md: full schema mapping legacy vs ERPNext - CHANGELOG.md: detailed migration log - MIGRATION-PLAN.md: strategy and next steps - scripts/migration/: idempotent Python scripts (direct PG method) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
28 KiB
28 KiB
Architecture comparative — Legacy vs ERPNext
1. Schéma relationnel Legacy (gestionclient / MariaDB)
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENTS & ADRESSES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ account (15,303) delivery (17,114) │
│ ├─ id (PK) ├─ id (PK) │
│ ├─ customer_id (ex: DR2) ├─ account_id → account.id │
│ ├─ first_name, last_name ├─ name (label) │
│ ├─ company ├─ address1, city, zip │
│ ├─ email, tel_home, cell ├─ longitude, latitude │
│ ├─ address1, city, zip └─ contact, email │
│ ├─ status (1=actif,4=terminé) │
│ ├─ group_id (5=rés, 8=com) │
│ ├─ ppa, ppa_code, ppa_account ← PPA AccesD Desjardins │
│ ├─ stripe_id ← Paiement carte │
│ ├─ tax_group │
│ └─ language_id │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ SERVICES & FORFAITS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ service (66,879) product (833) │
│ ├─ id (PK) ├─ id (PK) │
│ ├─ delivery_id → delivery.id ├─ sku (ex: FTTH150I) │
│ ├─ device_id → device.id ├─ price │
│ ├─ product_id → product.id ├─ active │
│ ├─ status (1=actif, 0=inactif) ├─ category → product_cat.id │
│ ├─ date_orig, date_next_invoice ├─ download_speed (Kbps) │
│ ├─ payment_recurrence ├─ upload_speed (Kbps) │
│ ├─ hijack (prix spécial) ├─ quota_day, quota_night │
│ ├─ hijack_price, hijack_desc ├─ fibre_lineprofile │
│ ├─ radius_user, radius_pwd └─ fibre_serviceprofile │
│ ├─ date_end_contract │
│ └─ actif_until product_cat (34) │
│ ├─ id, name │
│ Note: service EST l'abonnement. ├─ num_compte (→ compta) │
│ Un client a N services actifs. └─ combo_dispo │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ FACTURATION & PAIEMENTS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ invoice (629,944) payment (540,522) │
│ ├─ id (PK) ├─ id (PK) │
│ ├─ account_id → account.id ├─ account_id → account.id │
│ ├─ date_orig (unix timestamp) ├─ date_orig │
│ ├─ total_amt, billed_amt ├─ amount │
│ ├─ billing_status (0=draft,1=ok) ├─ type (chèque,PPA,CC...) │
│ ├─ process_status ├─ reference │
│ ├─ due_date └─ applied_amt │
│ ├─ notes, template_message │
│ └─ ppa_charge payment_item (684,778) │
│ ├─ payment_id → payment.id │
│ invoice_item (1,859,260) ├─ invoice_id → invoice.id │
│ ├─ invoice_id → invoice.id └─ amount │
│ ├─ service_id → service.id │
│ ├─ sku → product.sku invoice_tax (par facture) │
│ ├─ quantity, unitary_price ├─ invoice_id │
│ └─ product_name ├─ tax_id → tax.id │
│ └─ amount │
│ tax (4) │
│ ├─ TPS 5%, TVQ 8.925/9.975% │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ COMPTABILITÉ │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ compta_comptes (48) compta_journal_ecriture (1.2M)│
│ ├─ id, num_compte (ex: 4020) ├─ id │
│ ├─ category (Actif/Passif/Rev) ├─ date │
│ └─ desc ├─ compta_id → comptes.id │
│ ├─ debit, credit │
│ compta_setup └─ desc │
│ └─ month_closed (fermeture) │
│ │
│ LOGIQUE: product_cat.num_compte lie chaque catégorie │
│ de produit à un compte de revenus (ex: cat 32 → 4020) │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ RÉSEAU & ÉQUIPEMENTS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ device (10,377) fibre (16,057) │
│ ├─ id (PK) ├─ id (PK) │
│ ├─ delivery_id → delivery.id ├─ service_id → service.id │
│ ├─ category, name ├─ terrain, rue, ville │
│ ├─ sn (serial number) ├─ frame, slot, port │
│ ├─ mac ├─ vlan_manage/internet/tel │
│ ├─ model, manufacturier ├─ ontid, sn │
│ ├─ manage (IP admin) ├─ info_connect (IP OLT) │
│ ├─ user, pass ├─ latitude, longitude │
│ └─ parent (device hiérarchie) └─ placemarks_id │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ TICKETS & SUPPORT │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ticket (242,618) ticket_msg (784,290) │
│ ├─ id (PK) ├─ id │
│ ├─ account_id → account.id ├─ ticket_id → ticket.id │
│ ├─ delivery_id → delivery.id ├─ msg (mediumtext, base64) │
│ ├─ subject └─ unread_csv │
│ ├─ dept_id → ticket_dept.id │
│ ├─ assign_to → staff.id ticket_dept (21) │
│ ├─ status (open/pending/closed) ├─ Support, Facturation │
│ ├─ due_date, priority ├─ Installation, Réparation │
│ ├─ wizard (JSON install data) └─ Vente, SysAdmin, etc. │
│ └─ wizard_fibre │
│ │
│ bon_travail (14,472) staff (155) │
│ ├─ account_id, tech1, tech2 ├─ username, password │
│ ├─ heure_arrive/depart ├─ first_name, last_name │
│ └─ subtotal, tps, tvq, total ├─ email, cell, ext │
│ ├─ rights (serialized PHP) │
│ └─ dept_list │
└─────────────────────────────────────────────────────────────────────┘
2. Schéma ERPNext cible
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENTS & ADRESSES │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Customer Address │
│ ├─ name (PK, auto) ├─ name (PK, auto) │
│ ├─ customer_name ├─ address_title │
│ ├─ customer_type (Ind/Company) ├─ address_line1, city │
│ ├─ customer_group ├─ state, pincode, country │
│ ├─ territory (Canada) ├─ latitude, longitude │
│ ├─ *legacy_account_id ← NEW ├─ links[] → Customer │
│ ├─ *legacy_customer_id ← NEW └─ address_type │
│ ├─ *ppa_enabled ← NEW │
│ ├─ *stripe_id ← NEW Contact │
│ └─ disabled ├─ first_name, last_name │
│ ├─ email_ids[], phone_nos[] │
│ Note: Customer est SÉPARÉ de └─ links[] → Customer │
│ Address et Contact (pattern ERPNext) │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ SERVICES & FORFAITS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Item (type=Service) Subscription │
│ ├─ item_code (= legacy SKU) ├─ party_type = Customer │
│ ├─ item_name ├─ party → Customer │
│ ├─ item_group → Item Group ├─ plans[] → Subscription Plan│
│ ├─ standard_rate ├─ start_date, end_date │
│ ├─ *legacy_product_id ← NEW ├─ status (Active/Cancelled) │
│ ├─ *download_speed ← NEW ├─ generate_invoice_at │
│ ├─ *upload_speed ← NEW ├─ sales_tax_template │
│ ├─ *quota_day_gb ← NEW ├─ *radius_user ← NEW │
│ ├─ *quota_night_gb ← NEW ├─ *radius_pwd ← NEW │
│ ├─ *fibre_lineprofile ← NEW └─ *legacy_service_id ← NEW │
│ └─ *fibre_serviceprofile ← NEW │
│ Subscription Plan │
│ Item Group (34) ├─ plan_name │
│ ├─ Services/ ├─ item → Item │
│ │ ├─ Mensualités fibre ├─ cost (prix fixe) │
│ │ ├─ Mensualités sans fil ├─ billing_interval = Month │
│ │ ├─ Téléphonie └─ currency = CAD │
│ │ └─ ...16 sous-groupes │
│ ├─ Products/ │
│ │ ├─ Équipement fibre Note: hijack_price du legacy │
│ │ ├─ Quincaillerie → additional_discount sur │
│ │ └─ ...8 sous-groupes la Subscription │
│ └─ Frais et ajustements/ │
│ └─ ...10 sous-groupes │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ FACTURATION & PAIEMENTS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Sales Invoice Payment Entry │
│ ├─ customer → Customer ├─ party → Customer │
│ ├─ posting_date ├─ posting_date │
│ ├─ items[] (child table) ├─ paid_amount │
│ │ ├─ item_code → Item ├─ references[] (child) │
│ │ ├─ qty, rate │ ├─ reference_name → SINV │
│ │ └─ amount │ └─ allocated_amount │
│ ├─ taxes[] → Tax Template ├─ mode_of_payment │
│ ├─ grand_total └─ reference_no │
│ ├─ status (Draft/Submitted/Paid) │
│ └─ is_return (pour crédits) Payment Reconciliation │
│ └─ auto-match paiements │
│ Sales Taxes Template │
│ ├─ TPS 5% Journal Entry │
│ └─ TVQ 9.975% └─ Opening balance │
│ │
│ NATIF: Subscription génère auto les Sales Invoice chaque mois │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ RÉSEAU & ÉQUIPEMENTS (FSM) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Service Location (custom) Service Equipment (custom) │
│ ├─ name (LOC-#####) ├─ name (EQP-#####) │
│ ├─ customer → Customer ├─ location → Service Loc │
│ ├─ address → Address ├─ serial_number (SN) │
│ ├─ gps_lat, gps_lon ├─ mac_address │
│ ├─ connection_type (FTTH/Wireless) ├─ equipment_type │
│ ├─ olt_ip, olt_frame/slot/port ├─ status │
│ └─ vlan_internet/tele/manage └─ model, manufacturer │
│ │
│ Dispatch Job (custom) Dispatch Technician (custom) │
│ ├─ customer, service_location ├─ full_name, phone │
│ ├─ job_type (Install/Repair/...) ├─ traccar_device_id │
│ ├─ assigned_tech └─ status │
│ ├─ scheduled_date/time │
│ └─ equipment[], checklist[] │
│ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ TICKETS & SUPPORT │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Issue (natif ERPNext HD) Employee (natif ERPNext HR) │
│ ├─ subject ├─ employee_name │
│ ├─ customer → Customer ├─ department │
│ ├─ issue_type → ticket_dept ├─ user_id → User │
│ ├─ priority └─ cell_phone, email │
│ ├─ status (Open/Replied/Closed) │
│ ├─ description (HTML) Note: staff.rights (PHP) │
│ └─ resolution_details → Rôles ERPNext │
│ │
│ Issue peut contenir des Comments (Communication) │
│ → ticket_msg.msg migré ici (base64 images → fichiers) │
│ │
└─────────────────────────────────────────────────────────────────────┘
3. Mapping table par table
| Legacy Table | Records | ERPNext DocType | Custom Fields | Notes |
|---|---|---|---|---|
| account | 15,303 | Customer | legacy_account_id, legacy_customer_id, ppa_enabled, stripe_id | status 1→enabled, 4→disabled |
| account (contact) | — | Contact | — | email, tel, cell séparés |
| delivery | 17,114 | Address + Service Location | latitude, longitude | GPS dans Address natif |
| product | 833 | Item | legacy_product_id, download/upload_speed, quota_gb, fibre_profiles | ✅ FAIT |
| product_cat | 34 | Item Group | — | ✅ FAIT |
| service | 66,879 | Subscription | radius_user, radius_pwd, legacy_service_id | hijack → discount |
| invoice | 629,944 | Sales Invoice | — | Opening balance pour historique |
| invoice_item | 1,859,260 | Sales Invoice Item | — | Idem |
| payment | 540,522 | Payment Entry | — | Opening balance pour historique |
| payment_item | 684,778 | Payment Entry Reference | — | Idem |
| tax | 4 | Sales Taxes Template | — | TPS 5% + TVQ 9.975% |
| compta_comptes | 48 | Account (Chart) | — | Map → plan comptable canadien existant |
| compta_journal | 1.2M | Journal Entry | — | Opening balance seulement |
| device | 10,377 | Service Equipment | — | FSM custom doctype |
| fibre | 16,057 | Service Location (champs OLT) | — | FSM custom doctype |
| ticket | 242,618 | Issue | — | AI knowledge base |
| ticket_msg | 784,290 | Communication | — | base64 → File uploads |
| bon_travail | 14,472 | Dispatch Job | — | FSM custom doctype |
| staff | 155 | Employee | — | rights → Roles |
| ticket_dept | 21 | Issue Type | — | Support, Facturation, etc. |
4. Différences d'architecture clés
Ce qui change fondamentalement
| Aspect | Legacy | ERPNext |
|---|---|---|
| Nommage | ID numérique auto-inc | Naming series (CUST-.YYYY.-, etc.) |
| Client/Adresse | Tout dans account |
Séparé : Customer + Address + Contact |
| Abonnement | service = ligne active |
Subscription génère auto les factures |
| Prix spécial | hijack_price sur service |
additional_discount sur Subscription ou Pricing Rule |
| Taxes | Calculées dans PHP | Template TPS+TVQ auto-appliqué |
| Comptabilité | Écritures manuelles PHP | GL Entry auto depuis Sales Invoice |
| Permissions | staff.rights (PHP serialize) |
Rôles + DocType permissions |
| Fichiers | Base64 dans mediumtext | File doctype → /files/ directory |
| RADIUS | Champs sur service |
Custom fields sur Subscription |
| Dates | Unix timestamp (bigint) | ISO date (YYYY-MM-DD) |
Ce qui reste pareil
- SKU des produits
- Structure client → N adresses → N services
- Facturation mensuelle récurrente
- Système de tickets/support
5. Flux opérationnels comparés
Facturation mensuelle
Legacy :
CRON → task_charge_recurrent.php
→ SELECT services WHERE date_next_invoice < NOW()
→ INSERT invoice + invoice_items
→ UPDATE service.date_next_invoice + 1 month
→ task_generate_statement.php (PDF)
→ task_mail.php (envoi email)
ERPNext :
Scheduler → Subscription.process()
→ Auto-génère Sales Invoice (draft ou submitted)
→ Taxes auto-appliquées via template
→ Email via Print Format + Email Account
→ Payment Entry (PPA/Stripe) auto-réconcilié
Nouveau client
Legacy :
account_add.php → INSERT account
delivery_add.php → INSERT delivery
service_add.php → INSERT service + UPDATE radius
invoice_add.php → première facture manuelle
ERPNext :
Customer (create) → Address (link) → Contact (link)
Subscription (create, plans=[Item]) → auto-generates invoices
Webhook → n8n → FreeRADIUS (create user)
Ticket support
Legacy :
ticket_new.php → INSERT ticket
ticket_view.php → INSERT ticket_msg (avec base64 images)
Assignation manuelle (assign_to → staff.id)
ERPNext :
Issue (create) → Communication (messages)
Assignment Rule (auto-assignation par type)
SLA tracking natif
File upload pour images (pas de base64)
6. État de la migration
| Phase | Statut | Détail |
|---|---|---|
| 1a. Item Groups | ✅ Fait | 34 groupes, 3 parents |
| 1b. Items | ✅ Fait | 833 items, vitesses, quotas, legacy IDs |
| 1c. Custom Fields | ✅ Fait | Item (8), Customer (4), Subscription (3) |
| 2. Customers + Contacts + Addresses | ✅ Fait | 6,667 Customers + ~6,600 Contacts + ~6,700 Addresses via direct PG (migrate_direct.py, ~30s) |
| 3. Tax Templates + Subscription Plans | ✅ Fait | QC TPS 5% + TVQ 9.975%, 92 Subscription Plans |
| 4. Subscriptions | ✅ Fait | 21,876 Subscriptions avec RADIUS data. Scheduler PAUSED |
| 5. Opening Balance | ⏳ Planifié | Soldes calculés legacy — prochaine étape |
| 6. Tickets → Issues | ⏳ Phase 2 | 242K tickets, extraction base64 |
| 7. Sync bidirectionnelle | ⏳ Phase 2 | n8n ETL nightly |
⚠ ATTENTION : Le scheduler ERPNext est PAUSED. Ne pas réactiver sans instruction explicite — cela déclencherait la facturation automatique des 21,876 abonnements.