Backend services: - targo-hub: extract deepGetValue to helpers.js, DRY disconnect reasons lookup map, compact CAPABILITIES, consolidate vision.js prompts/schemas, extract dispatch scoring weights, trim section dividers across 9 files - modem-bridge: extract getSession() helper (6 occurrences), resetIdleTimer(), consolidate DM query factory, fix duplicate username fill bug, trim headers (server.js -36%, tplink-session.js -47%, docker-compose.yml -57%) Frontend: - useWifiDiagnostic: extract THRESHOLDS const, split processDiagnostic into 6 focused helpers (processOnlineStatus, processWanIPs, processRadios, processMeshNodes, processClients, checkRadioIssues) - EquipmentDetail: merge duplicate ROLE_LABELS, remove verbose comments Documentation (17 → 13 files, -1,400 lines): - New consolidated README.md (architecture, services, dependencies, auth) - Merge ECOSYSTEM-OVERVIEW into ARCHITECTURE.md - Merge MIGRATION-PLAN + ARCHITECTURE-COMPARE + FIELD-GAP + CHANGELOG → MIGRATION.md - Merge COMPETITIVE-ANALYSIS into PLATFORM-STRATEGY.md - Update ROADMAP.md with current phase status - Delete CONTEXT.md (absorbed into README) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
20 KiB
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:
- Displayed in the ops-app ClientDetailPage
- Driven by natural language ("suspend this customer", "create a quote for fibre 100M", "set up a payment plan for $150/month")
- 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.