# Gigafibre FSM — Status Snapshot (2026-04-18) > **Audience:** new contributor, stakeholder, or future-me opening this cold. > **Goal:** be able to hold a coherent conversation about the system in 10 minutes. > **Companion docs:** [README](../README.md) · [ARCHITECTURE](ARCHITECTURE.md) · [ROADMAP](ROADMAP.md) · [DATA_AND_FLOWS](DATA_AND_FLOWS.md) · [BILLING_AND_PAYMENTS](BILLING_AND_PAYMENTS.md) · [CPE_MANAGEMENT](CPE_MANAGEMENT.md) · [APP_DESIGN_GUIDELINES](APP_DESIGN_GUIDELINES.md) --- ## TL;DR Gigafibre (consumer brand of TARGO Internet) is a Quebec fiber ISP. We are migrating a legacy PHP/MariaDB billing system (used daily since ~2012) to **ERPNext v16 on PostgreSQL**, wrapping it in Vue 3/Quasar PWAs for ops, dispatch, field, and customer self-service. The accounting, subscriptions, invoices, and payments are **already migrated and balanced**. We are now in the **"live ops" transition phase**: new quotes flow through ERPNext + DocuSeal e-signature, dispatch runs on the new PWA, and the legacy system is kept in read-only mode pending customer-portal cutover. --- ## Where we are (at a glance) | Area | State | Notes | |---|---|---| | **Accounting migration** | ✅ Shipped | 629,935 invoices + 343,684 payments, GL balanced ($130M debit/credit) | | **Master data migration** | ✅ Shipped | 15,303 customers · 833 items · 21,876 subscriptions · 7,241 equipment | | **Staff SSO (auth.targo.ca)** | ✅ Live | Authentik forwardAuth, 6 groups | | **Ops PWA (erp.gigafibre.ca/ops/)** | ✅ Live | ClientDetail, Tickets, Dispatch, Dashboard | | **Dispatch PWA v1** | ✅ Live | Timeline + Mapbox + drag-drop | | **Field tech PWA** | ✅ Live | Mobile via `/t/{token}` | | **GenieACS TR-069** | ✅ Live | ~7,560 CPEs polled every 5 min | | **targo-hub gateway** | ✅ Live | 11 modules (ERP, Twilio, SIP, AI, SSE, etc.) | | **Quotation + DocuSeal flow** | 🆕 Just shipped (2026-04-18) | See "New this cycle" below | | **Customer SSO (id.gigafibre.ca)** | ✅ Live | Authentik federated from staff | | **Customer portal v2** | 🚧 In progress | Vue SPA spec ready; checkout + Stripe pending | | **ERPNext scheduler** | ⏸ Paused | `pause_scheduler=1` — requires explicit approval to reactivate | | **Invoice PDF "Facture TARGO"** | ✅ Live | Inline-SVG logo, QR to Stripe/portal | | **Quote PDF "Soumission TARGO"** | ✅ Live | Same branding, QR to DocuSeal signing URL | | **Legacy PHP/MariaDB** | 🗃 Read-only | Accessed from 10.100.80.100 for residual lookups | --- ## Architecture at a glance ``` Internet (Cloudflare DNS for gigafibre.ca) │ ┌────────────────────┴───────────────────┐ │ Proxmox VM — 96.125.196.67 │ │ Ubuntu 24.04 · Docker · Traefik v2.11│ └────────────────────┬───────────────────┘ │ ┌──────────┬───────────┬────┴─────┬──────────┬─────────────┐ │ │ │ │ │ │ ERPNext Ops PWA targo-hub n8n DocuSeal Authentik erp. /ops/ msg. n8n. sign. auth.targo.ca giga.ca (nginx) giga.ca giga.ca giga.ca id.giga.ca │ │ │ ├─── Twilio (SMS, +14382313838) │ ├─── 3CX PBX (targopbx.3cx.ca) │ ├─── Mailjet SMTP │ ├─── Fonoster/Routr (SIP @ voice.giga.ca) │ ├─── GenieACS NBI (10.5.2.115:7557) │ ├─── Oktopus TR-369 (oss.giga.ca) │ ├─── Gemini AI (agent/voice/OCR) │ ├─── Stripe (payments) │ └─── Traccar (tech GPS, tracker.targointernet.com) │ └─── Legacy MariaDB (read-only, 10.100.80.100) ``` For the authoritative breakdown see [ARCHITECTURE.md](ARCHITECTURE.md). --- ## Features inventory (by surface) ### 👥 Staff — Ops PWA (`erp.gigafibre.ca/ops/`) - **ClientDetailPage** — master client view, 8 parallel API calls (contacts, devices, subscriptions, invoices, payments, tickets, equipment, SMS history). Inline-editable fields Odoo-style. - **TicketsPage** — issue triage, assignment, status, SLA indicators. - **DispatchPage (v1)** — today's jobs, technician rows, drag-drop re-assign, Mapbox job map. - **DashboardPage** — KPIs, outstanding AR, job throughput. - **Wizard (ProjectWizard.vue)** — creates Quotation with `wizard_steps` JSON describing dispatch + subscription creation deferred until acceptance. - **Theme** — light mode everywhere except Dispatch (dark by design). ### 🔧 Staff — Dispatch PWA v2 (`dispatch.gigafibre.ca`) *[in migration]* - Larger Mapbox-driven timeline, undo support, technician lanes, planned features include Gantt view and auto-dispatch by skill tags. ### 📱 Field — Field tech PWA (mobile) - Route at `/t/{token}` (no login — token-based). - Offline-first IndexedDB; photo upload, checklist, materials used, customer signature. - Barcode/serial scanner: native camera capture (``) → Gemini 2.5 Flash Vision via `msg.gigafibre.ca/vision/barcodes` (extracts barcode / S/N / MAC / model from a single photo). ### 🧑‍💼 Customer — Client portal (`client.gigafibre.ca`) *[v2 in progress]* **Live today:** - Authentik client SSO at `id.gigafibre.ca`. - Legacy password bridge: MD5 → PBKDF2 on first login (11,800 portal users provisioned). - ERPNext Portal Server Script resolves `X-Authentik-Email` → Customer. **In flight (Phase 4):** - Vue SPA: see invoices, pay via Stripe Payment Element, view subscriptions, update contact info. - Online appointment booking. - Real-time tech arrival tracking via SMS. - QR code on modem → subscriber dashboard. ### 🌐 Public — Marketing site (`www.gigafibre.ca`) - React + Vite + Tailwind + shadcn/ui. - RQA address eligibility check, AQ address normalization. - Uses Supabase for pre-signup intake; Twilio for SMS OTP on checkout intent. ### ✍️ Quotation → Signature flow (just shipped) 1. Wizard creates Quotation w/ `wizard_steps` JSON (dispatch + subscriptions to execute on acceptance). 2. Wizard calls `POST /accept/generate` with `use_docuseal: true`. 3. Hub creates DocuSeal submission (template 1), writes `sign_url` to `Quotation.custom_docuseal_signing_url`. 4. PDF print format **Soumission TARGO** renders with QR + "SIGNER CETTE SOUMISSION" button. 5. Customer signs → DocuSeal webhook fires `form.completed` → hub runs `acceptQuotation()` → dispatch jobs + subscriptions materialized from `wizard_steps`. See [project_docuseal_quote.md](~/.claude/projects/.../memory/project_docuseal_quote.md) for deep detail. --- ## ERPNext customisations ### Custom doctypes (FSM module) | Doctype | ID | Purpose | |---|---|---| | Service Location | `LOC-#####` | Customer premises (address, GPS, OLT port, network config) | | Service Equipment | `EQP-#####` | ONT / router / TV box — serial, MAC, IP, parent | | Service Subscription | `SUB-#####` | Active plans — speed, price, billing, RADIUS creds | | Service Contract | `CTR-#####` | Long-term agreement with benefits | | Dispatch Job | `DJ-#####` | Work order — materials, checklist, photos, signature | | Dispatch Technician | `DT-#####` | Tech profile — skills, Traccar GPS, color | | Dispatch Tag | — | Skill tag with level (1 base → 5 expert) | | Checklist Template | — | Re-usable job checklists | ### Custom fields (highlights) | Doctype | Field | Purpose | |---|---|---| | Item | `custom_billing_frequency` | One-time / Monthly / Annual (source of truth) | | Quotation Item · Sales Invoice Item · Sales Order Item | `custom_billing_frequency` | Inherited from Item via `fetch_from`, overridable per-line | | Quotation | `custom_docuseal_signing_url`, `custom_docuseal_envelope_id` | Populated by hub after DocuSeal submit | | Quotation | `custom_quote_type` | Résidentiel / Commercial | | Quotation | `wizard_steps`, `wizard_context` | JSON blobs driving post-acceptance workflow | | Customer | `legacy_account_id`, `legacy_customer_id`, `ppa_enabled`, `stripe_id` | Legacy mapping + Stripe link | | Item | `legacy_product_id`, `download_speed`, `upload_speed`, `olt_profile` | Network profile | | Subscription | `radius_user`, `radius_pwd`, `legacy_service_id` | Network auth | | Issue | `legacy_ticket_id`, `assigned_staff`, `issue_type`, `is_important`, `service_location` | Ticket context | | User | `legacy_password_md5` | Bridge for legacy portal passwords | ### Print formats | Format | Doctype | State | |---|---|---| | **Facture TARGO** | Sales Invoice | ✅ Live — used for monthly billing, QR to Stripe/portal | | **Soumission TARGO** | Quotation | ✅ Live (2026-04-18) — QR to DocuSeal signing URL, split totals (Services récurrents + Frais uniques) | ### Known ERPNext v16 quirks - **PostgreSQL GROUP BY / HAVING / double-quote bugs** — patches applied, bulk submit workflow documented. - **Scheduler paused** (`pause_scheduler=1`) — do NOT reactivate without approval; Subscription → Invoice automation depends on it. - **Frappe REST filter on Quotation.customer** rejected — use `party_name` when `quotation_to == "Customer"`. --- ## Integrations (live + credentialed) | Integration | URL / endpoint | Auth | Used by | |---|---|---|---| | Authentik (staff) | `auth.targo.ca` | OAuth client + forwardAuth | ERPNext, ops, dispatch, n8n, hub | | Authentik (client) | `id.gigafibre.ca` | forward_single proxy | client.gigafibre.ca | | Twilio | `api.twilio.com` | Basic (SID/token) | targo-hub twilio.js, SMS, OTP | | Cloudflare | `api.cloudflare.com` | X-Auth-Key + X-Auth-Email (Global) | DNS, ops app checks | | GenieACS | `http://10.5.2.115:7557` | LAN no-auth | targo-hub devices.js (5-min poll) | | Oktopus (TR-369) | `oss.gigafibre.ca` | user/pass | CPE next-gen mgmt | | Stripe | `api.stripe.com` | secret key | Customer checkout, webhook | | Fonoster / Routr | `voice.gigafibre.ca:5160/5163` | SIP creds | Voice calls via WSS | | 3CX PBX | `targopbx.3cx.ca` | Basic | Voice polling, call logs | | Mailjet SMTP | `in-v3.mailjet.com:587` | user/pass | DocuSeal + transactional email | | Traccar | `tracker.targointernet.com:8082` | user/pass | Tech GPS | | DocuSeal | `sign.gigafibre.ca` | API key | Quotation e-signature (just live) | | n8n | `n8n.gigafibre.ca` | webhook (no auth) | SMS, OLT SSH commands, subscription automations | | Gemini AI | `generativelanguage.googleapis.com` | Bearer | agent replies, OCR, voice | All tokens/keys live in `/opt/targo-hub/.env` on the production host and are tracked in memory records (never in repo). --- ## 🆕 New this cycle (2026-04-08 → 2026-04-18) - **DocuSeal e-signature for Quotations** — hub routes, Custom Fields, "Soumission TARGO" print format, signing URL writeback, webhook → `acceptQuotation`. - **Billing frequency system** — `custom_billing_frequency` on Item + Quotation Item + Sales Invoice Item + Sales Order Item; print format splits Récurrent vs One-time with their own subtotals. - **Print format fixes** — inline-SVG logo (was oversized PNG), `frappe.db.commit()` fix (was silently rolling back), correct `party_name` usage. - **Hub** — `DOCUSEAL_DEFAULT_TEMPLATE_ID` env knob, writeback of `sign_url` to Quotation. - **Custom app** — `gigafibre_utils.api.url_qr_base64(url)` helper for QR generation in print formats. --- ## 🚧 In flight (right now) 1. **Customer portal v2 (Vue SPA)** — invoice list, Stripe Payment Element, subscription view, contact update. Spec ready. No front-end code yet. 2. **Dispatch v2** — larger rewrite of dispatch PWA at `dispatch.gigafibre.ca`. Timeline + Mapbox + Gantt + auto-dispatch by tag/skill. 3. **ClientDetailPage refactor** — 1,512-line monolith → composables (`useCustomer`, `useSubscriptionGroups`, `useFormatters`, `useStatusClasses`). 4. **Service deactivation workflow** — n8n webhook ties Subscription cancellation to OLT port shutdown. 5. **Multi-email account cleanup** — ~30 customers have multiple emails; manual triage pending. --- ## 🎯 Next priorities (ordered) > Update this list at the end of each working session. | # | Item | Why now | Owner | Blocked by | |---|---|---|---|---| | 1 | Register DocuSeal webhook in UI → `msg.gigafibre.ca/accept/docuseal-webhook` on `form.completed` | Without this, signed quotes don't materialise dispatch jobs | manual (UI) | — | | 2 | Validate Subscription → Sales Invoice scheduler flow on a pilot subscription | Before reactivating scheduler for full run | eng | — | | 3 | Reactivate `pause_scheduler=0` for April billing | Recurring invoices must generate | eng + ops approval | #2 | | 4 | Customer portal v2 MVP (invoice list + Stripe Payment Element) | Drive self-serve payment, reduce AR calls | eng | — | | 5 | Tag 46 techs with skills | Enable auto-dispatch | ops | — | | 6 | Issue → Dispatch Job auto-creation wiring | Close ticket/dispatch loop | eng | — | | 7 | Fix 15 invoices flagged "Credit Note Issued" with $3,695.77 overpaid | Accounting hygiene | accounting | — | | 8 | 10DLC Twilio production upgrade | Avoid SMS rate throttling at scale | ops | Twilio approval | --- ## 🐛 Known issues & technical debt - **Scheduler paused** — subscriptions won't produce invoices until reactivated. - **Tech debt in ClientDetailPage.vue (1,512 LOC)** — slated for composable extraction. - **Multiple `formatDate` / `formatMoney` implementations** across files — should consolidate. - **6+ ad-hoc `statusClass` functions** (`subStatusClass`, `eqStatusClass`, `ticketStatusClass`...) — extract to `useStatusClasses`. - **Dispatch v1 vs v2** live simultaneously — cutover plan not yet written. - **15 "Credit Note Issued" invoices** with overpayment of $3,695.77. - **~30 customers** share emails across accounts — migration chose one per; manual reconciliation TBD. - **GenieACS CPE count ≠ ERPNext Service Equipment count** — TPLG serial vs real serial mismatch; MAC-based lookup required. --- ## 🚨 Gotchas (don't get burned) - **Traefik HTTP → HTTPS global redirect breaks Let's Encrypt HTTP-01.** Add redirect per-route or use TLS-ALPN. See `feedback_traefik_ssl.md`. - **Docker 29 + Traefik v3 don't play well** — stay on v2.11. - **MongoDB 5+ needs AVX** — Proxmox CPU must be set to `host` type. - **netplan overrides systemd-networkd** — remove for static networking. - **Docker exec + nested SSH quoting is fragile** — always write `.py` to disk, `docker cp`, then exec. See `feedback_docker_exec.md`. - **iptables + Docker** — avoid `iptables-multiport`, don't persist bridge rules, use the raw table. See `feedback_ddos_iptables.md`. - **ERPNext Customer doctype has multi-address** — but legacy assumed 1:1. Device hierarchy is tied to **address**, not customer, intentionally — preserves manage IP / credentials / parent relationships across customer changes. - **Ops app deploy path** — upload `dist/spa/*` (contents), not `dist/spa/` (dir), to `/opt/ops-app/`. See `feedback_deploy_path.md`. --- ## How to share this context - **"I need someone on-board in 30 min":** have them read the README + this file. - **"They need to work on dispatch":** add [DATA_AND_FLOWS.md](DATA_AND_FLOWS.md) + [APP_DESIGN_GUIDELINES.md](APP_DESIGN_GUIDELINES.md). - **"They need to work on billing/invoicing":** add [BILLING_AND_PAYMENTS.md](BILLING_AND_PAYMENTS.md). - **"They need to work on CPE / network gear":** add [CPE_MANAGEMENT.md](CPE_MANAGEMENT.md). - **"They need server / infra access":** credentials live on the host in `/opt/targo-hub/.env` — share out-of-band. Memory records in `~/.claude/.../memory/` note the shape of each credential. --- *Last updated: 2026-04-18 · Maintained alongside [ROADMAP.md](ROADMAP.md). When this file drifts more than a week out of date, create a new dated snapshot (`STATUS_YYYY-MM-DD.md`) and link it from the top of README.*