# Customer 360° — Complete Data Map & Business Flows ## Goal Every piece of customer data linked in one place. All business flows documented so they can be: 1. Displayed in the ops-app ClientDetailPage 2. Driven by natural language ("suspend this customer", "create a quote for fibre 100M", "set up a payment plan for $150/month") 3. Automated via rules/triggers --- ## Current State — What ClientDetailPage Shows | Section | Source | Status | |---------|--------|--------| | Customer header (name, status, group) | Customer | ✅ | | Contact (phone, email, cell) | Customer + Contact | ✅ | | Info card (PPA, commercial, VIP flags) | Customer custom fields | ✅ | | Locations + connection type + OLT | Service Location | ✅ | | Equipment per location (live status, signal, WiFi) | Service Equipment + GenieACS | ✅ | | Subscriptions per location (monthly/annual, price) | Service Subscription | ✅ | | Tickets per location | Issue | ✅ | | All tickets table | Issue | ✅ | | Invoices table | Sales Invoice | ✅ | | Payments table | Payment Entry | ✅ | | Notes (internal memos) | Comment on Customer | ✅ | | Activity timeline (sidebar) | Comment + audit | ✅ | --- ## Missing Data — To Import & Link ### 1. Soumissions (Quotes) — 908 records, 529 customers ``` Legacy: soumission id, account_id, name, po, date, tax materiel → PHP serialized array [{sku, desc, amt, qte, tot}, ...] mensuel → PHP serialized array [{sku, desc, amt, qte, tot}, ...] text → terms/notes 3 templates in soumission_template ERPNext target: Quotation party_type = Customer, party = C-{account_id} items[] from deserialized materiel + mensuel terms = text field legacy_soumission_id = id ``` **Flow**: Sales rep creates quote → customer approves → services activated → first invoice generated **Natural language**: "Create a quote for client X for fibre 100M + WiFi router" **UI**: New expandable section in ClientDetailPage between Subscriptions and Tickets ### 2. Accords de paiement (Payment Arrangements) — 7,283 records, 1,687 customers ``` Legacy: accord_paiement id, account_id, staff_id date_accord → date agreed (unix) date_echeance → due date (unix) date_coupure → cutoff/disconnect date (unix) montant → agreed payment amount method → 0=unset, 1=portal, 2=cheque, 3=phone, 4=PPA status → -1=pending, 0=open, 1=completed note → staff notes ("Par téléphone avec Gen") raison_changement → reason for change ERPNext target: Custom DocType "Payment Arrangement" linked_to Customer via party field OR import as structured Comments with type="Payment Arrangement" Status breakdown: 5,068 completed | 2,205 open | 10 pending ``` **Flow**: Customer has overdue balance → support calls → negotiates amount + date → cutoff if missed **Natural language**: "Set up a payment plan for client X: $150 by March 26, disconnect on April 2 if missed" **UI**: Badge on CustomerHeader showing active arrangement + section in sidebar ### 3. Bons de travail (Work Orders) — 14,486 records, 10,110 customers ``` Legacy: bon_travail id, account_id, date (unix) tech1, heure_arrive_t1, heure_depart_t1 tech2, heure_arrive_t2, heure_depart_t2 note, subtotal, tps, tvq, total Legacy: bon_travail_item (only 3 records — barely used) bon_id, product_id, qte, price, desc ERPNext target: Linked to Dispatch Job or custom "Work Order" child tech1/tech2 → Employee via staff mapping Time tracking: arrive/depart for labor costing ``` **Flow**: Ticket created → tech dispatched → arrives on site → completes work → bon de travail logged **Natural language**: "Show me all work done at this address" or "How many hours did tech X spend at client Y?" **UI**: Timeline entry on location, or section under Tickets ### 4. Payment Methods & Tokens — Multi-provider Active customer payment breakdown (6,681 active accounts): | Payment Method | Count | Notes | |----------------|-------|-------| | No payment method | 5,274 | Manual pay via portal/cheque | | Bank PPA (pre-authorized debit) | 655 | Legacy bank draft via Paysafe/Bambora token | | Stripe card (no auto) | 564 | Card on file, pays manually | | Stripe PPA (auto-charge) | 143 | Stripe auto-recurring | | Bank PPA + Stripe card | 45 | Both methods on file | ``` Legacy: account_profile (658 records — Paysafe/Bambora tokens) account_id, profile_id (UUID), card_id (UUID), token, initial_transaction Legacy: account table fields stripe_id → Stripe customer ID (cus_xxx) stripe_ppa → 1 = Stripe auto-charge enabled stripe_ppa_nocc → Stripe PPA without CC ppa → 1 = bank draft PPA enabled ppa_name/code/branch/account → bank info for PPA ppa_amount → PPA amount limit ppa_amount_buffer → buffer above invoice total ppa_fixed → fixed PPA amount ppa_cc → PPA via credit card ERPNext target: Custom DocType "Payment Method" linked to Customer type = stripe | paysafe | bank_draft token/profile_id for processor reference is_auto = PPA flag Stripe integration: use stripe_id to pull current payment methods via API ``` **Flow**: Customer adds card (portal or phone) → token stored → PPA auto-charges on invoice **Future**: Stripe as primary, migrate Paysafe tokens → Stripe, bank PPA stays for some **Natural language**: "Is client X set up for auto-pay?" / "Switch client X to Stripe auto-charge" **UI**: Payment method badge on CustomerHeader + card in sidebar ### 5. VoIP / DID / 911 — Complete Telephone Service Three linked tables form one VoIP service per DID: | Table | Records | Unique DIDs | Customers | Purpose | |-------|---------|-------------|-----------|---------| | `pbx` | 790 | 790 | 745 | SIP line config (creds, voicemail, routing) | | `phone_addr` | 1,014 | 1,014 | 909 | 911 address (provisioned to 911 provider) | | `phone_provisioning` | 786 | 779 | 739 | Device provisioning (ATA model, MAC, password) | All 790 PBX lines have a matching 911 address. 224 additional 911 addresses exist without an active PBX line (decommissioned lines that still have 911 registration). ``` Legacy: pbx (SIP line) account_id, delivery_id, service_id phone (10-digit DID), name (caller ID display) password (SIP auth), vm_password, has_vm, vm_email int_code (extension), language, call_911 max_calls, call_timeout, user_context (sip.targo.ca) country_whitelist, date_origin, date_update Legacy: phone_addr (911 address — provisioned to external 911 provider) account_id, phone (DID) street_number, apt, street_name, city, state, zip first_name, last_name, info enhanced_capable (Y/N), code_cauca (municipality code), class_service (RES/BUS) Legacy: phone_provisioning (ATA/device config) account_id, delivery_id, service_id phone (DID), app (device type: ht502, etc), mac (device MAC) password, internationnal (intl calling flag) ERPNext target: Custom DocType "VoIP Line" parent: Service Location (via delivery_id) linked_to: Subscription (via service_id) Fields: did (phone number), caller_id (display name) sip_password, voicemail_enabled, vm_password, vm_email extension, max_calls, call_timeout e911_street, e911_city, e911_state, e911_zip e911_cauca_code, e911_class (RES/BUS) e911_synced (bool — matches 911 provider) ata_model, ata_mac, ata_password ``` **Flow**: Order → provision SIP line in PBX → register 911 address with provider → configure ATA → service active Address change → update 911 with provider (MANDATORY) → verify sync **Reports needed**: - **DID Report**: All DIDs → linked customer → service location → 911 address → match status - **911 Audit**: DIDs where service address ≠ 911 address (compliance risk) - **Orphan 911**: 224 addresses with no active PBX line (cleanup or deregister) **Natural language**: - "Show all phone lines for client X" - "Update 911 address for 450-272-2408 to 123 Rue Principale" - "List all DIDs with mismatched 911 addresses" - "What's the voicemail password for 450-272-2408?" - "Generate the DID report for all active lines" **UI**: - Phone icon in equipment strip per location (click → VoIP detail panel) - 911 status badge (green = synced, red = mismatch or missing) - DID report page accessible from main nav ### 7. Account Suspension — 1,049 records ``` Legacy: account_suspension account_id, date_start, date_end, note (most records have date_start=0, date_end=0 — just flags) ERPNext target: Customer custom field "is_suspended" or Comment log ``` **Flow**: Overdue → auto-suspend → customer pays → reactivate **Natural language**: "Suspend client X" / "Reactivate client X" **UI**: Red badge on CustomerHeader, shown in activity timeline ### 8. IP History — 20,999 records, 5,769 customers ``` Legacy: ip_history account_id, delivery_id, service_id, ip, date (unix) ERPNext target: Comment on Service Location (audit trail) OR custom child table on Service Location ``` **Flow**: Service provisioned → IP assigned → IP changes logged **Natural language**: "What IP did client X have on March 15?" **UI**: Collapsible history under location details ### 9. Service Snapshots (Bandwidth Usage) — 45,977 records ``` Legacy: service_snapshot account_id, service_id, date (unix) quota_day (bytes), quota_night (bytes) ERPNext target: Skip import — historical analytics only Could feed a usage dashboard later ``` ### 10. Delivery History (Address Changes) — 16,284 records ``` Legacy: delivery_history account_id, date_orig (unix) address1, address2, city, state, zip (previous addresses before changes) ERPNext target: Comment on Customer or Service Location ``` --- ## Complete Business Flows ### Flow 1: Sales → Activation ``` Quote created (soumission) ↓ Customer approves Quote → Sales Order (or direct to Subscription) ↓ Address confirmed Service Location created/selected ↓ Equipment assigned Service Equipment linked to location ↓ Installation scheduled Dispatch Job (from ticket or bon_travail) ↓ Tech completes work Subscriptions activated ↓ First billing cycle Sales Invoice generated ``` **Natural language examples:** - "Create a quote for client X: fibre 100M at 123 Rue Principale" - "Convert quote SOQ-908 to active subscriptions" - "Schedule installation for next Tuesday" ### Flow 2: Billing → Collections ``` Subscription active ↓ Monthly/annual cycle Sales Invoice auto-generated ↓ PPA active? YES → Auto-charge via payment token (account_profile) NO → Invoice sent (email/portal) ↓ Payment received? YES → Payment Entry created, outstanding reduced NO → Overdue ↓ Collections process Payment arrangement negotiated (accord_paiement) ↓ Cutoff date passed? YES → Account suspended (account_suspension) ↓ Payment received Account reactivated ``` **Natural language examples:** - "What does client X owe?" - "Set up a payment plan: $150 by the 15th, cut off on the 20th" - "Suspend client X for non-payment" - "Client X just paid, reactivate their service" ### Flow 3: Support → Resolution ``` Customer calls / portal ticket ↓ Issue created Ticket assigned to department/tech ↓ Remote diagnosis? YES → Check equipment live status (GenieACS/TR-069) → Reboot device, check signal, WiFi clients NO → Schedule field visit ↓ Dispatch Job created Tech dispatched → arrives → works ↓ Bon de travail logged (tech, hours, parts) Issue resolved → closed ↓ Billable? YES → Invoice for labor/parts NO → Done ``` **Natural language examples:** - "Client X has no internet — check their ONT status" - "Send a tech to 123 Rue Principale tomorrow morning" - "How many times have we sent a tech to this address?" ### Flow 4: Provisioning → Service Management ``` Service ordered ↓ Equipment assigned OLT port configured (fibre table) ↓ ONT registered GenieACS discovers device → TR-069 parameters set ↓ VoIP ordered? YES → PBX line created (pbx table) → 911 address registered (phone_addr) → Voicemail configured ↓ IP assigned IP logged in ip_history ↓ Service active Subscription billing begins ``` **Natural language examples:** - "Provision fibre for client X at port 1/2/3 on OLT-EAST" - "Set up a phone line 819-555-1234 with voicemail" - "What's the 911 address for this line?" --- ## ERPNext Data Model — Complete Customer Graph ``` Customer (C-{id}) │ ├── SALES │ ├── Quotation ←── soumission (908) ◄ TO IMPORT │ │ └── Quotation Item (materiel + mensuel) │ └── (future: Sales Order) │ ├── BILLING │ ├── Sales Invoice (629,935) ✅ DONE │ │ ├── SI Item → income_account (SKU-mapped) ✅ DONE │ │ ├── SI Tax (TPS/TVQ) ✅ DONE │ │ ├── GL Entry (4 per invoice) ✅ DONE │ │ ├── PLE (outstanding tracking) ✅ DONE │ │ └── Comment (invoice notes, 580K) ✅ DONE │ ├── Payment Entry (343,684) ✅ DONE │ │ ├── PE Reference (allocations) ✅ DONE │ │ └── GL Entry (2 per payment) ✅ DONE │ ├── Payment Arrangement ←── accord_paiement ◄ TO IMPORT │ │ (7,283: amount, dates, cutoff, status) │ └── Payment Method (custom doctype) ◄ TO IMPORT │ ├── Stripe (752 customers, 143 auto-PPA) │ │ └── stripe_id → Stripe API for live card info │ ├── Paysafe/Bambora (658 tokens from account_profile) │ │ └── profile_id + card_id + token │ └── Bank PPA (655 with bank account info) │ └── ppa_name, ppa_code, ppa_branch, ppa_account │ ├── SERVICE │ ├── Service Location (delivery) ✅ DONE │ │ ├── Service Equipment (device) ✅ DONE │ │ │ ├── OLT data (fibre table) ✅ DONE │ │ │ ├── Live status (GenieACS TR-069) ✅ LIVE │ │ │ └── Provisioning data (WiFi/VoIP) ✅ DONE │ │ ├── VoIP Line ←── pbx (790 DIDs) ◄ TO IMPORT │ │ │ ├── 911 Address ←── phone_addr (1,014) ◄ TO IMPORT │ │ │ │ └── Synced to external 911 provider (CAUCA codes) │ │ │ │ └── 224 orphan 911 records (no active PBX line) │ │ │ └── ATA Config ←── phone_provisioning ◄ TO IMPORT │ │ │ (786 records: device type, MAC, SIP creds) │ │ ├── IP History ←── ip_history (20,999) ◄ TO IMPORT │ │ └── Address History ←── delivery_history ○ LOW PRIORITY │ ├── Subscription (active services) ✅ DONE │ │ ├── Subscription Plan ✅ DONE │ │ └── Billing frequency + price ✅ DONE │ └── Suspension Status ←── account_suspension ◄ TO IMPORT │ (1,049 records) │ ├── SUPPORT │ ├── Issue / Ticket (98,524) ✅ DONE │ │ ├── Communication (ticket messages) ✅ DONE │ │ ├── Dispatch Job ✅ DONE │ │ └── Work Order ←── bon_travail (14,486) ◄ TO IMPORT │ │ (tech, hours, parts, billing) │ └── Comment / Memo (29,245) ✅ DONE │ ├── ACCOUNT │ ├── Portal User (Website User) ✅ DONE │ │ └── Auth bridge (MD5 → pbkdf2) ✅ DONE │ ├── Stripe ID ✅ DONE │ └── PPA flags + bank info ✅ DONE │ └── ANALYTICS (low priority) ├── Bandwidth Usage ←── service_snapshot (46K) ○ SKIP ├── VoIP CDR ←── voicemeup (96) ○ SKIP └── TV Wizard ←── tele_wiz (1,065) ○ SKIP ``` --- ## Import Priority & Plan ### Phase 13: Remaining Customer Data | Step | Table | Target | Records | Depends on | |------|-------|--------|---------|------------| | 13a | `soumission` | Quotation | 908 | Customer, Item | | 13b | `accord_paiement` | Payment Arrangement (custom) | 7,283 | Customer | | 13c | `bon_travail` | Work Order log on Issue/Customer | 14,486 | Customer, Employee | | 13d | `account_profile` + `account.*ppa*` + `account.stripe_id` | Payment Method (custom doctype) | 658 + 655 + 752 | Customer | | 13e | `pbx` + `phone_provisioning` | VoIP Line (custom doctype) linked to Location + Subscription | 790 + 786 | Customer, Service Location | | 13f | `phone_addr` | 911 Address fields on VoIP Line + sync status flag | 1,014 | VoIP Line | | 13f.1 | — | **DID Report**: all DIDs → customer → location → 911 address → sync status | report | VoIP Line | | 13f.2 | — | **911 Audit Report**: mismatches + 224 orphan 911 addresses | report | VoIP Line | | 13g | `account_suspension` | Customer.is_suspended flag | 1,049 | Customer | | 13h | `ip_history` | Comment on Service Location | 20,999 | Service Location | ### Phase 14: UI Sections in ClientDetailPage | Section | Position | Data | |---------|----------|------| | Soumissions | After subscriptions, before tickets | Quotation list with status | | Accord de paiement | Badge on header + sidebar card | Active arrangement with countdown | | Bons de travail | Under ticket detail or in timeline | Tech visits with hours | | VoIP | Under equipment strip per location | Phone lines with 911 status | | Suspension | Red banner on header | Active suspension with dates | ### Phase 15: Natural Language Actions Each action maps to an API call on ERPNext or targo-hub: | Intent | Action | API | |--------|--------|-----| | "Create a quote for..." | Parse items + customer → POST Quotation | ERPNext API | | "Suspend client X" | Set is_suspended=1, trigger OLT port disable | targo-hub + OLT API | | "Set up payment plan..." | Create Payment Arrangement with dates/amount | ERPNext API | | "What does X owe?" | SUM(outstanding_amount) from Sales Invoice | ERPNext API | | "Send a tech to..." | Create Dispatch Job with location | ERPNext API | | "Check their internet" | GenieACS device status + signal levels | targo-hub | | "Reboot their router" | TR-069 reboot task via GenieACS | targo-hub | | "What IP did X have on..." | Query ip_history or Comment | ERPNext API | | "Add a phone line" | Create VoIP Line + register 911 | PBX API + 911 provider API + targo-hub | | "Update 911 address for..." | Update phone_addr + push to 911 provider | 911 provider API | | "Show DID report" | All DIDs with customer, location, 911 match | ERPNext report | | "List mismatched 911" | DIDs where service addr ≠ 911 addr | ERPNext report | | "Switch client to Stripe" | Create Stripe customer + set PPA flag | Stripe API | | "Is client X on auto-pay?" | Check Payment Method for active PPA | ERPNext API | | "Convert quote to service" | Quotation → Subscriptions + Location setup | ERPNext API | --- ## Natural Language Architecture ``` User input (text or voice) ↓ Intent classifier (LLM) ↓ extracts: action, customer, parameters Action router ↓ maps intent → API endpoint + payload Confirmation step (show what will change) ↓ user approves Execute via targo-hub API ↓ returns result Response in natural language ``` Key principle: **every action the UI can do, the NL interface can do**. The ops-app becomes a visual confirmation layer for NL-driven changes.