gigafibre-fsm/docs/DATA-STRUCTURE-FOUNDATION.md
louispaulb fa37426f34 docs: data structure foundation for lead-to-service pipeline
ERPNext doctype relationships and field additions:
- Customer → Service Location → Subscriptions + Equipment
- Project with installation task templates
- Dispatch Job linked to project and location
- New custom fields: wifi_ssid/password, sip_username/password,
  gpon_serial, cwmp_serial, radius credentials on Service Equipment

Order wizard flow (website + agent):
- Address check → plan selection → customer info → dates → payment
- Atomic creation: Customer + Location + Subs + Equipment + Project
  + Tasks + Dispatch Job + fibre port reservation

Existing field tech app inventory:
- TasksPage (jobs + tickets), ScanPage (photo/live/manual),
  DevicePage (detail + customer link), DiagnosticPage (speed test)
- useScanner composable (3-strip photo scan for multi-barcode)
- Offline queue with replay

Migration gaps identified: WiFi/VoIP provisioning data,
RADIUS credentials, product catalog, fibre→RQA address matching

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 09:37:38 -04:00

395 lines
16 KiB
Markdown

# Data Structure Foundation — Lead to Live Service
## ERPNext Doctype Relationships
```
┌──────────────┐
│ Lead │ ← Website visitor / phone call
│ (new type) │ Not yet a customer
└──────┬───────┘
│ Convert to Customer
┌───────────────────────────────────────────────────────────┐
│ Customer │
│ name, email, phone, customer_group (Résidentiel/Comm.) │
│ stripe_id, legacy_customer_id │
└──────────┬─────────────────────────┬──────────────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────────┐
│ Service Location │ │ Sales Invoice │
│ (delivery address) │ │ Payment Entry │
│ │ │ (billing) │
│ address, city, zip │ └─────────────────────────┘
│ lat, lng │
│ connection_type │
│ olt_name, olt_ip │
│ frame/slot/port │
│ ont_id │
│ status: Pending → │
│ Active │
│ fibre_id (legacy) │
└──────┬──────────────┘
├──────────────────────────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ Service │ │ Service Equipment │
│ Subscription │ │ │
│ │ │ serial_number │
│ item (product) │ │ mac_address │
│ status: Pending │ │ equipment_type │
│ → Active │ │ brand, model │
│ start_date │ │ status: En inventaire│
│ billing_interval │ │ → Actif │
│ rate (monthly $) │ │ olt_* fields │
└──────────────────┘ │ wifi_ssid (new) │
│ wifi_password (new) │
│ sip_username (new) │
│ sip_password (new) │
└──────────────────────┘
┌──────────────────────────────────────────────────┐
│ Project │
│ "Installation - {customer_name}" │
│ customer, service_location │
│ status: Open → Completed │
│ │
│ ┌─── Task ────────────────────────────────┐ │
│ │ "Préparer équipement" │ support │ │
│ │ "Activer port OLT" │ support │ │
│ │ "Configurer WiFi/VoIP" │ support │ │
│ │ "Installation sur site" │ tech │ │
│ │ "Vérifier signal" │ tech │ │
│ │ "Confirmer service" │ tech │ │
│ └─────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Dispatch Job │
│ customer, service_location │
│ project (link to Installation project) │
│ scheduled_date, preferred_dates │
│ assigned_technician │
│ required_tags: [Fibre, Installation] │
│ equipment_list (child table) │
│ status: Scheduled → In Progress → Completed │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Issue (Ticket) │
│ customer, service_location │
│ subject, description │
│ issue_type: Installation, Support, Réparation │
│ status: Open → Resolved → Closed │
│ linked_project (new field) │
└──────────────────────────────────────────────────┘
```
---
## New Doctype: Installation Order (or use Lead + Project)
Rather than creating a new doctype, we use ERPNext's existing **Lead****Customer** conversion + **Project** with tasks. The wizard creates everything in one shot.
### Option A: Use ERPNext Lead Doctype
```
Lead (ERPNext built-in)
lead_name: "Jean Tremblay"
email_id, phone
source: "Website" | "Phone" | "Walk-in"
status: "Lead" → "Opportunity" → "Converted" → "Do Not Contact"
Custom fields:
address_line, city, postal_code
fibre_id (link to fibre table entry)
olt_port (frame/slot/port)
requested_services: ["Internet", "TV", "Phone"]
internet_plan: "FTTH150I"
tv_package: "TVBSTANDARD"
phone: Check
preferred_date_1, preferred_date_2, preferred_date_3
stripe_payment_intent
notes
```
### Option B: Direct Customer Creation (simpler)
Skip Lead, create Customer directly on checkout. Use a Project as the "order" container.
**Recommended: Option B** — for self-service checkout, the customer already paid. No need for a Lead stage. For phone orders, the agent creates the customer directly too.
---
## The Wizard Flow (Website or Agent)
### Step 1: Address Check
```
Input: address string
Output: fibre entry (id, olt_port, max_speed, zone_tarifaire)
OR "not available" → lead capture
```
### Step 2: Plan Selection
```
Input: fibre zone_tarifaire
Output: available plans with pricing
- Internet: filtered by max_speed and zone
- TV: always available if internet selected
- Phone: always available (even without fiber)
- Discounts: auto-calculated (2x, 3x, 4x combo)
```
### Step 3: Customer Info
```
Input: name, email, phone
preferred installation dates (3 choices)
OR "contactez-moi"
Output: customer data ready for creation
```
### Step 4: Payment (Stripe)
```
Input: card details (Stripe Elements)
Output: payment_intent confirmed
First month pro-rated + one-time charges
```
### Step 5: Order Creation (atomic — all or nothing)
```
Creates in ERPNext (via targo-hub or n8n):
1. Customer
{ customer_name, email, phone, customer_group }
2. Service Location
{ customer, address, olt_port, fibre_id, status: "Pending Install" }
3. Service Subscriptions (one per service)
{ customer, location, item: FTTH150I, status: "Pending" }
{ customer, location, item: TVBSTANDARD, status: "Pending" }
{ customer, location, item: TELEPMENS, status: "Pending" }
4. Service Equipment (placeholders — serials TBD by tech)
{ customer, location, type: "ONT", status: "En inventaire" }
{ customer, location, type: "Routeur", status: "En inventaire" }
{ customer, location, type: "Décodeur TV", status: "En inventaire" } (if TV)
5. Project "Installation - Jean Tremblay"
{ customer, service_location, status: "Open" }
Tasks:
- Préparer équipement (assigné: support)
- Activer port OLT {frame/slot/port} (assigné: support)
- Configurer WiFi (assigné: support, auto-possible)
- Configurer VoIP (if phone, assigné: support)
- Installation sur site (assigné: tech via dispatch)
- Vérifier signal ONT (assigné: tech)
- Confirmer service fonctionnel (assigné: tech)
6. Dispatch Job
{ customer, location, project, scheduled_date: preferred_date_1,
tags: [Fibre, Installation], status: "Scheduled" }
7. Reserve Fibre Port
UPDATE fibre SET sn = 'RESERVED-{customer_id}' WHERE id = {fibre_id}
8. Notifications
- SMS to customer (Twilio): "Commande confirmée!"
- Email to customer (Mailjet): order summary
- SSE to Ops: new-installation event
```
---
## Provisioning Data on Service Equipment
### New Custom Fields Needed
```python
# On Service Equipment doctype, add:
# WiFi provisioning (for Deco routers)
custom_wifi_ssid = Data, label="WiFi SSID"
custom_wifi_password = Password, label="WiFi Password"
custom_wifi_ssid_5g = Data, label="WiFi SSID 5GHz" (if different)
custom_wifi_enabled = Check, label="WiFi Enabled", default=1
# VoIP provisioning (for ONTs with phone)
custom_sip_username = Data, label="SIP Username"
custom_sip_password = Password, label="SIP Password"
custom_sip_line = Int, label="SIP Line Number", default=1
# GPON provisioning
custom_gpon_serial = Data, label="GPON Serial" (physical sticker: RCMG/TPLG)
custom_cwmp_serial = Data, label="CWMP Serial" (GenieACS internal)
custom_fibre_line_profile = Data, label="Line Profile ID"
custom_fibre_service_profile = Data, label="Service Profile ID"
# RADIUS (for PPPoE if applicable)
custom_radius_user = Data, label="RADIUS Username"
custom_radius_password = Password, label="RADIUS Password"
```
### WiFi Generation on Order
```
When order is created:
1. Generate WiFi SSID: "Gigafibre-{lastname}" or customer-chosen
2. Generate WiFi password: random 12-char alphanumeric
3. Store on Service Equipment (Routeur type)
4. When tech installs Deco → it bootstraps → ACS reads WiFi from ERPNext
via targo-hub: GET /devices/{mac}/provision → returns SSID/password
```
### VoIP Provisioning on Order
```
When phone service ordered:
1. Allocate SIP account from Fonoster/Routr
POST /telephony/credentials → creates SIP user
2. Store SIP username/password on Service Equipment (ONT type)
3. When tech installs ONT → it bootstraps → ACS reads VoIP from ERPNext
via targo-hub: GET /devices/{serial}/provision → returns SIP creds
```
---
## Fibre Availability: Linking to RQA Addresses
### Current State
- `fiber_availability` table in Supabase: uuidadresse → zone_tarifaire + max_speed
- This table was imported from a different source than the legacy `fibre` table
- They need to be linked
### Target
```sql
-- Add fibre_id column to fiber_availability
ALTER TABLE fiber_availability ADD COLUMN fibre_id INTEGER;
ALTER TABLE fiber_availability ADD COLUMN olt_ip VARCHAR(64);
ALTER TABLE fiber_availability ADD COLUMN olt_port VARCHAR(16); -- "0/3/2"
ALTER TABLE fiber_availability ADD COLUMN ont_slot INTEGER; -- available ontid
ALTER TABLE fiber_availability ADD COLUMN port_available BOOLEAN DEFAULT true;
-- Match fibre entries to RQA addresses
-- Strategy: postal code + street name fuzzy match + civic number
UPDATE fiber_availability fa
SET fibre_id = f.id,
olt_ip = f.info_connect,
olt_port = CONCAT(f.frame, '/', f.slot, '/', f.port),
port_available = (f.sn IS NULL OR f.sn = '' OR f.sn LIKE 'RESERVED%')
FROM fibre f
JOIN addresses a ON a.identifiant_unique_adresse = fa.uuidadresse
WHERE similarity(lower(f.rue), lower(a.odonyme_recompose_normal)) > 0.5
AND lower(f.ville) = lower(a.nom_municipalite)
AND f.zip = a.code_postal;
```
### Alternative: Serve Fibre Data Directly
Instead of linking to Supabase, have targo-hub serve a `/availability` endpoint that queries the legacy fibre table directly:
```
GET /availability?q=123+rue+principale+saint-anicet
1. Fuzzy search RQA addresses (Supabase/pg_trgm)
2. For matched address, query fibre table:
SELECT * FROM fibre
WHERE rue LIKE '%principale%' AND ville LIKE '%anicet%'
AND (sn IS NULL OR sn = '') -- available port
3. Return: { available, max_speed, olt_port, zone_tarifaire, fibre_id }
```
---
## Agent Phone Order Flow
Same data structure, different entry point:
```
Agent in Ops app:
1. Search customer (existing) or click "Nouveau client"
2. Enter address → same availability check
3. Select services → same plan picker
4. Choose dates → same date picker
5. Click "Créer commande"
→ Same API call as website checkout
→ Same n8n/targo-hub workflow
→ Same Project + Tasks + Dispatch Job creation
Difference: no Stripe payment upfront
→ Invoice generated, customer pays later or agent takes CC over phone
```
### Ops App: New "Nouvelle installation" Wizard
```
Route: /ops/new-installation
Components:
Step1_AddressCheck.vue — address search + availability
Step2_PlanSelection.vue — internet/tv/phone picker + pricing
Step3_CustomerInfo.vue — name/email/phone (or existing customer)
Step4_DateSelection.vue — preferred dates calendar
Step5_Summary.vue — review + confirm
On confirm → POST /api/checkout (same endpoint as website)
```
---
## Migration: Legacy Data → ERPNext
### Already Migrated
- ✅ Customers (account → Customer)
- ✅ Service Locations (delivery → Service Location)
- ✅ Service Subscriptions (service → Service Subscription)
- ✅ Service Equipment (device → Service Equipment)
- ✅ Tickets (ticket → Issue)
- ✅ Invoices (invoice → Sales Invoice)
- ✅ OLT data on equipment (fibre → custom fields)
### Still Needed
1. **WiFi provisioning data** → Service Equipment custom fields
- genieacs.wifi (1,713 entries, 858 unique Deco MACs)
- Match: wifi.serial (Deco MAC) → device.mac → Service Equipment.mac_address
2. **VoIP provisioning data** → Service Equipment custom fields
- genieacs.voip (797 entries, 469 unique RCMG serials)
- Match: voip.serial (RCMG) → device.sn → Service Equipment.serial_number
3. **RADIUS credentials** → Service Equipment or Service Subscription
- service.radius_user + service.radius_pwd
- Match: service.device_id → device.sn → Service Equipment
4. **Product catalog** → ERPNext Item (if not already done)
- 100+ legacy products with SKU, pricing, speed tiers
- fibre_lineprofile + fibre_serviceprofile for OLT provisioning
5. **Fibre → RQA address link** for availability search
- 16,056 fibre entries need matching to 5.2M RQA addresses
- Strategy: postal code + street similarity + civic number
---
## Build Priority
### Week 1: Data Foundation
1. Add provisioning custom fields to Service Equipment
2. Migrate WiFi/VoIP/RADIUS data to those fields
3. Match fibre entries to RQA addresses
4. Expose availability API (fibre port check)
### Week 2: Order Wizard
1. Build /ops/new-installation wizard in Ops app
2. Build targo-hub /checkout endpoint
3. Build n8n "New Order" workflow
4. Auto-create: Customer → Location → Subscriptions → Equipment → Project → Tasks → Dispatch Job
### Week 3: Website Checkout
1. Upgrade AvailabilityDialog → full checkout flow
2. Stripe integration
3. Same /checkout endpoint as Ops wizard
4. SMS/email confirmations
### Week 4: Auto-Provisioning
1. targo-hub /devices/{serial}/provision endpoint
2. ACS webhook on device bootstrap → reads ERPNext
3. WiFi/VoIP auto-push from Service Equipment fields
4. "Device online" notifications