# 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.**