Files
2026-07-12 10:17:17 -04:00

3.8 KiB

RO-Quote Sync Mechanism

Overview

When a quote is generated from a Repair Order (RO), approved services on the quote sync back to the RO's services array on Save. This is the bidirectional RO ↔ Quote link.

Key Files

File Role
src/components/quoteGenerator/QuoteSummary.tsx Sync logic in handleSave (lines 146-194) and ensureShareToken (lines ~270-303)
src/pages/QuoteGenerator.tsx Sets repairOriginId from data.repairOrderId on edit load (line ~524) and from fromRO URL param (line ~103)

Data Flow

1. New Quote from RO

  • User clicks "Generate Quote" from RO → navigates to /quote?fromRO=RO_ID&name=...
  • fromRO effect in QuoteGenerator.tsx loads the RO, sets repairOriginId = ro.id
  • User adds/approves services, clicks Save
  • handleSave in QuoteSummary.tsx creates the quote record with repairOrderId: repairOriginId (line 125)
  • Sync block runs: filters services.filter(s => s.approved), loads current RO services, merges (by service name — avoids duplicates), updates RO with JSON.stringify(roServices)
  • Toast: "Synced N approved service(s) to RO"

2. Editing Existing Quote from RO

  • Quote has repairOrderId set in PocketBase
  • Edit-load effect restores setRepairOriginId(data.repairOrderId) (QuoteGenerator.tsx line ~524)
  • User approves more services, clicks Save
  • Sync block runs same as above

3. Share with Customer (ensureShareToken)

  • New quote: Creates the quote record, syncs approved services to RO, then creates share token. Same merge logic as handleSave.
  • Editing existing quote (editId set): The if (!quoteId) block is SKIPPED — no sync on share of existing quotes. The quote is read from PB, share token created/returned. This is a known gap.

Critical Conditions

The sync block only runs when BOTH are true:

  1. repairOriginId is set (non-null)
  2. services.filter(s => s.approved).length > 0

Why Sync Might Not Run

Cause Symptom
Quote's repairOrderId is null in PB repairOriginId stays null; entire sync block skipped silently
No services approved Sync block skipped; no toast shown (by design)
RO load fails (permissions/network) Falls to catch block → error toast: "Sync to RO failed: ..."
Schema validation fails before sync Error toast from Zod; sync never reached

Most common cause: The quote was created before the repairOrderId field existed. Check the quote in PocketBase admin — if repairOrderId is empty, the quote has no RO link. Generate a new quote from the RO to get the field populated.

PocketBase Fields

  • quotes.repairOrderId — relation field linking quote to its originating RO. Set at quote creation (line 125 in handleSave).
  • repairOrders.services — stored as JSON.stringify(array) (plain JSON text field, NOT a PocketBase relation).

Sync Merge Logic

Services are merged by name (not ID):

  • If RO already has a service with the same name → update it (preserving status if already set)
  • If name not found → append new service

Each synced service gets: id: 'qs-{quoteService.id}', name, description, laborHours (from price/rate), laborRate, partsCost: 0, total: price, status: 'pending'

Testing the Sync

  1. Create an RO owned by the current user
  2. Generate a quote from it (/quote?fromRO=RO_ID)
  3. Add and approve services
  4. Click Save
  5. Check the RO in PocketBase — services should contain the approved services
  6. Verify via API: GET /api/collections/repairOrders/records/RO_ID

Previous Investigation

  • The sync code was verified working end-to-end (2026-07-08)
  • All debug [SPQ-DEBUG] console.logs have been removed
  • The sync runs in both handleSave and ensureShareToken (new-quote path only)
  • RO services field is written as JSON.stringify(roServices) — always produces valid JSON arrays