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>
264 lines
16 KiB
Markdown
264 lines
16 KiB
Markdown
# 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 (`<input capture="environment">`) → 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.*
|