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>
108 lines
5.7 KiB
Markdown
108 lines
5.7 KiB
Markdown
# Status — 2026-04-18 (session 2)
|
|
|
|
> Follow-up to STATUS_2026-04-18.md. This session was the sales-flow sprint.
|
|
|
|
## What changed this session
|
|
|
|
### 1. Field scanner — offline resilience for weak-signal zones
|
|
Photos that can't reach Gemini Vision within 8s (weak LTE / no service) are
|
|
now queued in IndexedDB and retried in the background; late results are merged
|
|
back into the scanner UI when they arrive.
|
|
|
|
- `apps/field/src/stores/offline.js` — new `visionQueue`, `scanResults`,
|
|
`enqueueVisionScan`, `syncVisionQueue`, `consumeScanResult`.
|
|
Driven off queue length (not `navigator.onLine`, which lies in basements).
|
|
- `apps/field/src/composables/useScanner.js` — `Promise.race` with 8s timeout,
|
|
retryable errors enqueue, late arrivals dispatched through `onNewCode`.
|
|
- `apps/field/src/pages/ScanPage.vue` — pending chip "N scan(s) en attente".
|
|
|
|
### 2. Sales flow — residential contract wiring
|
|
The ProjectWizard now knows how to turn a quotation into the Service Contract
|
|
(promotion-framing récap). The contract *is* the shopping-cart recap per
|
|
your brief: "c'est un peu le récapitulatif d'un panier d'achat."
|
|
|
|
**Entry from customer page:** ClientDetailPage → Soumissions section now has a
|
|
"**+ Nouvelle soumission**" button that opens the wizard pre-filled with
|
|
customer phone/email/primary service location. Wizard skips to `quotation` mode
|
|
+ `requireAcceptance=true` by default when launched from a customer.
|
|
|
|
**Residential presets (one-click):**
|
|
- Installation standard (Internet 500 + Routeur offert + Frais offerts, 24 mois)
|
|
- Duo 300 + Tél (79.99$/mois, 24 mois, installation offerte)
|
|
- Trio complet (119.99$/mois, 24 mois, routeur + installation offerts)
|
|
- Internet seul (89.99$/mois, 12 mois)
|
|
|
|
Presets live in `apps/ops/src/data/wizard-constants.js` → `RESIDENTIAL_PRESETS`
|
|
— edit there to tune prices / add bundles.
|
|
|
|
**Promo framing on one-time items:** each one-time line has a "Prix régulier
|
|
(si promo)" field. When `regular_price > rate`, the line becomes a **benefit**
|
|
on the Service Contract (e.g. Installation 75$ → 0$ = 75$ de promotion étalée
|
|
sur 24 mois). Shown with celebration chip + line-through regular price in
|
|
review.
|
|
|
|
**What `publish()` now does on a residential quotation:**
|
|
1. Creates Quotation (as before).
|
|
2. Creates Service Contract via hub `/contract/create` with
|
|
`duration_months = max(contract_months)`, `monthly_rate = sum(recurring)`,
|
|
`benefits[] = onetime items where regular_price > rate`,
|
|
`contract_type = 'Résidentiel'` (or `Commercial` when DocuSeal is selected).
|
|
3. Sends acceptance via `/contract/send` (promotion-framed récap, not the old
|
|
Quotation `/accept/generate`) — résidentiel gets the "J'accepte ce
|
|
récapitulatif" page; commercial gets DocuSeal.
|
|
|
|
If no recurring commitment items exist, the old `/accept/generate` path is still
|
|
used (unchanged). Direct-order / prepaid modes are untouched.
|
|
|
|
**Success screen** shows the Service Contract name when one was created.
|
|
|
|
## Files touched this session
|
|
```
|
|
apps/field/src/stores/offline.js +vision queue
|
|
apps/field/src/composables/useScanner.js +timeout/retry
|
|
apps/field/src/pages/ScanPage.vue +pending chip
|
|
apps/ops/src/composables/useWizardPublish.js +contract creation branch
|
|
apps/ops/src/composables/useWizardCatalog.js +applyPreset
|
|
apps/ops/src/data/wizard-constants.js +RESIDENTIAL_PRESETS
|
|
apps/ops/src/components/shared/ProjectWizard.vue +presets, promo UI, customer prop
|
|
apps/ops/src/pages/ClientDetailPage.vue +Nouvelle soumission button + wizard dialog
|
|
docs/STATUS_2026-04-18.md scanner stack corrected (Gemini, not html5-qrcode)
|
|
```
|
|
|
|
## Deployed
|
|
- Ops app: `dist/pwa/*` → `/opt/ops-app/` at 2026-04-18 ~08:05 EDT.
|
|
New bundle: `index.caa91ade.js`. Hard-reload in browser to pick up.
|
|
- Field app: **not yet deployed**. Build + deploy when you want it live:
|
|
`cd apps/field && quasar build && scp -r dist/spa/* root@96.125.196.67:/opt/field-app/`
|
|
|
|
## End-to-end smoke test done
|
|
- `CTR-00001` for C-LPB4 (Louis-Paul Bourdon) created via manual hub call
|
|
during the Service Contract prod-doctype setup. See
|
|
`memory/project_contract_acceptance.md` for creation gotchas (Dispatch User role).
|
|
- The wizard integration has **not been exercised end-to-end** yet — it
|
|
compiles, UI renders, but please run one manual test on a real customer
|
|
before announcing it.
|
|
|
|
## Suggested test path when you're back
|
|
1. Open ops → any customer → Soumissions → "+ Nouvelle soumission".
|
|
2. Pick "Duo 300 + Tél" preset. Set Frais d'installation prix régulier = 75$ (rate stays 0 → benefit of 75$).
|
|
3. Keep acceptation = JWT (Résidentiel).
|
|
4. Publish. Confirm: Quotation created + Service Contract created + SMS sent.
|
|
5. Open the SMS link on phone, click "J'accepte ce récapitulatif", verify
|
|
contract goes Actif + signature_proof stored.
|
|
|
|
## Known follow-ups (deliberately deferred)
|
|
- Wizard doesn't yet let the agent pick `Résidentiel` vs `Commercial`
|
|
independently of the acceptance method. Today: JWT = Résidentiel,
|
|
DocuSeal = Commercial. If you want a large commercial client on JWT
|
|
(or vice-versa), add a `contract_type` toggle in the expansion panel.
|
|
- Presets use hard-coded prices. Eventually these should come from the
|
|
live catalog (ERPNext Item price list) — same place `loadCatalog()`
|
|
reads from.
|
|
- `/accept/send` (old) and `/contract/send` (new) are two parallel paths.
|
|
When the residential flow is proven, consider deprecating
|
|
`/accept/generate` for quotations with recurring commitments.
|
|
- Two-column visual split (recurring | one-time) was mentioned — current
|
|
design keeps them in one list with swap button + promo chip, which
|
|
seemed to stay readable. Flag if you want the split.
|