All docs moved with git mv so --follow preserves history. Flattens the single-folder layout into goal-oriented folders and adds a README.md index at every level. - docs/README.md — new landing page with "I want to…" intent table - docs/architecture/ — overview, data-model, app-design - docs/features/ — billing-payments, cpe-management, vision-ocr, flow-editor - docs/reference/ — erpnext-item-diff, legacy-wizard/ - docs/archive/ — HANDOFF-2026-04-18, MIGRATION, status-snapshots/ - docs/assets/ — pptx sources, build scripts (fixed hardcoded path) - roadmap.md gains a "Modules in production" section with clickable URLs for every ops/tech/portal route and admin surface - Phase 4 (Customer Portal) flipped to "Largely Shipped" based on audit of services/targo-hub/lib/payments.js (16 endpoints, webhook, PPA cron, Klarna BNPL all live) - Archive files get an "ARCHIVED" banner so stale links inside them don't mislead readers Code comments + nginx configs rewritten to use new doc paths. Root README.md documentation table replaced with intent-oriented index. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5.8 KiB
Status — 2026-04-18 (session 2) — ARCHIVED
⚠️ ARCHIVED snapshot. For current state, see ../../roadmap.md.
Follow-up to 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— newvisionQueue,scanResults,enqueueVisionScan,syncVisionQueue,consumeScanResult. Driven off queue length (notnavigator.onLine, which lies in basements).apps/field/src/composables/useScanner.js—Promise.racewith 8s timeout, retryable errors enqueue, late arrivals dispatched throughonNewCode.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=trueby 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:
- Creates Quotation (as before).
- Creates Service Contract via hub
/contract/createwithduration_months = max(contract_months),monthly_rate = sum(recurring),benefits[] = onetime items where regular_price > rate,contract_type = 'Résidentiel'(orCommercialwhen DocuSeal is selected). - 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-00001for C-LPB4 (Louis-Paul Bourdon) created via manual hub call during the Service Contract prod-doctype setup. Seememory/project_contract_acceptance.mdfor 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
- Open ops → any customer → Soumissions → "+ Nouvelle soumission".
- Pick "Duo 300 + Tél" preset. Set Frais d'installation prix régulier = 75$ (rate stays 0 → benefit of 75$).
- Keep acceptation = JWT (Résidentiel).
- Publish. Confirm: Quotation created + Service Contract created + SMS sent.
- 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ésidentielvsCommercialindependently of the acceptance method. Today: JWT = Résidentiel, DocuSeal = Commercial. If you want a large commercial client on JWT (or vice-versa), add acontract_typetoggle 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/generatefor 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.