gigafibre-fsm/docs/ARCHITECTURE-COMPARE.md
louispaulb 93dd7a525f feat: migration legacy → ERPNext phases 1-4 complete
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>
2026-03-28 14:35:02 -04:00

386 lines
28 KiB
Markdown

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