Major additions accumulated over 9 days — single commit per request. Flow editor (new): - Generic visual editor for step trees, usable by project wizard + agent flows - PROJECT_KINDS / AGENT_KINDS catalogs decouple UI from domain - Drag-and-drop reorder via vuedraggable with scope isolation per peer group - Chain-aware depends_on rewrite on reorder (sequential only — DAGs preserved) - Variable picker with per-applies_to catalog (Customer / Quotation / Service Contract / Issue / Subscription), insert + copy-clipboard modes - trigger_condition helper with domain-specific JSONLogic examples - Global FlowEditorDialog mounted once in MainLayout, Odoo inline pattern - Server: targo-hub flow-runtime.js, flow-api.js, flow-templates.js - ERPNext: Flow Template/Run doctypes, scheduler, 5 seeded system templates - depends_on chips resolve to step labels instead of opaque "s4" ids QR/OCR scanner (field app): - Camera capture → Gemini Vision via targo-hub with 8s timeout - IndexedDB offline queue retries photos when signal returns - Watcher merges late-arriving scan results into the live UI Dispatch: - Planning mode (draft → publish) with offer pool for unassigned jobs - Shared presets, recurrence selector, suggested-slots dialog - PublishScheduleModal, unassign confirmation Ops app: - ClientDetailPage composables extraction (useClientData, useDeviceStatus, useWifiDiagnostic, useModemDiagnostic) - Project wizard: shared detail sections, wizard catalog/publish composables - Address pricing composable + pricing-mock data - Settings redesign hosting flow templates Targo-hub: - Contract acceptance (JWT residential + DocuSeal commercial tracks) - Referral system - Modem-bridge diagnostic normalizer - Device extractors consolidated Migration scripts: - Invoice/quote print format setup, Jinja rendering - Additional import + fix scripts (reversals, dates, customers, payments) Docs: - Consolidated: old scattered MDs → HANDOFF, ARCHITECTURE, DATA_AND_FLOWS, FLOW_EDITOR_ARCHITECTURE, BILLING_AND_PAYMENTS, CPE_MANAGEMENT, APP_DESIGN_GUIDELINES - Archived legacy wizard PHP for reference - STATUS snapshots for 2026-04-18/19 Cleanup: - Removed ~40 generated PDFs/HTMLs (invoice_preview*, rendered_jinja*) - .gitignore now covers invoice preview output + nested .DS_Store Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
16 KiB
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 · ARCHITECTURE · ROADMAP · DATA_AND_FLOWS · BILLING_AND_PAYMENTS · CPE_MANAGEMENT · APP_DESIGN_GUIDELINES
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.
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_stepsJSON 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 (
<input capture="environment">) → Gemini 2.5 Flash Vision viamsg.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)
- Wizard creates Quotation w/
wizard_stepsJSON (dispatch + subscriptions to execute on acceptance). - Wizard calls
POST /accept/generatewithuse_docuseal: true. - Hub creates DocuSeal submission (template 1), writes
sign_urltoQuotation.custom_docuseal_signing_url. - PDF print format Soumission TARGO renders with QR + "SIGNER CETTE SOUMISSION" button.
- Customer signs → DocuSeal webhook fires
form.completed→ hub runsacceptQuotation()→ dispatch jobs + subscriptions materialized fromwizard_steps.
See 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_namewhenquotation_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_frequencyon 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), correctparty_nameusage. - Hub —
DOCUSEAL_DEFAULT_TEMPLATE_IDenv knob, writeback ofsign_urlto Quotation. - Custom app —
gigafibre_utils.api.url_qr_base64(url)helper for QR generation in print formats.
🚧 In flight (right now)
- Customer portal v2 (Vue SPA) — invoice list, Stripe Payment Element, subscription view, contact update. Spec ready. No front-end code yet.
- Dispatch v2 — larger rewrite of dispatch PWA at
dispatch.gigafibre.ca. Timeline + Mapbox + Gantt + auto-dispatch by tag/skill. - ClientDetailPage refactor — 1,512-line monolith → composables (
useCustomer,useSubscriptionGroups,useFormatters,useStatusClasses). - Service deactivation workflow — n8n webhook ties Subscription cancellation to OLT port shutdown.
- 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/formatMoneyimplementations across files — should consolidate. - 6+ ad-hoc
statusClassfunctions (subStatusClass,eqStatusClass,ticketStatusClass...) — extract touseStatusClasses. - 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
hosttype. - netplan overrides systemd-networkd — remove for static networking.
- Docker exec + nested SSH quoting is fragile — always write
.pyto disk,docker cp, then exec. Seefeedback_docker_exec.md. - iptables + Docker — avoid
iptables-multiport, don't persist bridge rules, use the raw table. Seefeedback_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), notdist/spa/(dir), to/opt/ops-app/. Seefeedback_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 + APP_DESIGN_GUIDELINES.md.
- "They need to work on billing/invoicing": add BILLING_AND_PAYMENTS.md.
- "They need to work on CPE / network gear": add 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. 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.