gigafibre-fsm/docs/DATA-STRUCTURE-FOUNDATION.md
louispaulb bfffed2b41 feat: ONT diagnostics — grouped mesh topology, signal RSSI, management link
- EquipmentDetail: collapsible node groups (clients grouped by mesh node)
- Signal strength as RSSI % (0-255 per 802.11-2020) with 10-tone color scale
- Management IP clickable link to device web GUI (/superadmin/)
- Fibre status compact top bar (status + Rx/Tx power when available)
- targo-hub: WAN IP detection across all VLAN interfaces
- targo-hub: full WiFi client count (direct + EasyMesh mesh repeaters)
- targo-hub: /devices/:id/hosts endpoint with client-to-node mapping
- ClientsPage: start empty, load only on search (no auto-load all)
- nginx: dynamic ollama resolver (won't crash if ollama is down)
- Cleanup: remove unused BillingKPIs.vue and TagInput.vue
- New docs and migration scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 21:26:14 -04:00

16 KiB

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 LeadCustomer 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)

WEBSITE PATH:
  Input:  card details (Stripe Elements)
  Output: SetupIntent confirmed — card SAVED, NOT charged
          Stripe Customer + PaymentMethod created
          Customer sees: "Aucun frais avant la complétion de l'installation"
          Charge triggered AFTER tech confirms installation complete

AGENT PATH:
  No payment collected upfront
  Invoice generated → agent sends payment link via email/SMS
  POST /api/send-payment-link
    { customer_id, method: "email|sms|both" }
    → Creates Stripe Payment Link or Checkout Session
    → Sends via Twilio SMS / Mailjet email

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

# 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

-- 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