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>
This commit is contained in:
parent
e9324b45bc
commit
fa37426f34
394
docs/DATA-STRUCTURE-FOUNDATION.md
Normal file
394
docs/DATA-STRUCTURE-FOUNDATION.md
Normal file
|
|
@ -0,0 +1,394 @@
|
||||||
|
# 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
|
||||||
Loading…
Reference in New Issue
Block a user