Groundwork for the rep visit check-in feature: a rep taps "I'm on site",
the vehicle's tracker position surfaces which customers are there, and
the visit is logged against them.
Pure/unit-testable layers only — no PocketBase writes and no UI yet,
both of which are blocked on schema that has to be added in the PB
admin first. Design doc: docs/superpowers/specs/2026-09-05-rep-visit-check-in-design.md
- lib/visit-matching.ts rankNearbyCustomers: radius match around a
point, rep's own customers first
- lib/visit-stops.ts detectStops: breadcrumb trail -> stops.
Anchor-based clustering rather than a running
centroid, which would let a slow crawl chain
into one bogus stationary stop
- lib/tracker-parsing.ts Netstar FleetAI response decoding
- lib/server/tracker-service.ts
FleetAiAdapter + FakeTrackerAdapter behind a
TrackerAdapter seam. The fake is auto-selected
when no credentials are configured, so the rest
of the feature builds and tests without vendor
access.
- lib/geo.ts isRealGeoPoint, lifting the Null-Island guard
that was inlined identically in customers/[id]
and companies/[id]
Three FleetAI conventions drive real code here and are each pinned by a
test: errors arrive as HTTP 200 with a failure envelope; "--" means
"unavailable" and is not null (and for IGN, not false); timestamps are
platform-local UTC+2 with no offset marker, so parsing them naively puts
every fresh fix two hours in the past.
FleetAI has no position-history endpoint, so the design polls
getTokenBaseLiveData (whole fleet in one call) and keeps its own trail.
scripts/fleetai-probe.mjs is a throwaway read-only probe to confirm the
three account-shaped assumptions before more is built on top.
102 tests pass. The 9 pre-existing failing test files elsewhere in the
suite were verified identical on a stashed tree and are unrelated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Covers the reorderable block-list editor for sale/delivery/cash-up
slips, per-letterhead persistence, and the delivery-note pricing
default, worked out via brainstorming with the visual companion.
The 'Total {sumQty}{unitSuffix}' header line now explicitly documents
how mixed / missing units are handled: suffix is omitted when all
items have empty/null units (operator defaults to 'EA'); when units
are mixed, no suffix is shown; when all items share a non-EA unit
(e.g. BOX), the suffix is shown.
Self-review pass; no other changes.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Click-to-expand on each delivery-note row reveals an inline items
panel (description + qty) below the row. Operator can match the items
against the printed DN paper in their hand before loading. Pure
client-side render — items already loaded by /api/v1/deliveries are
filtered by delivery_note == dn.id. Empty state with fallback link to
/delivery-notes/[id] for DNs whose items aren't in the page slice.
Single-file edit on src/app/(app)/deliveries/page.tsx. No server,
schema, API, or MCP changes.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The plan + summary landed locally during the implementation but were
not committed alongside the code. Both documents describe the
rep-scoped Orders / Quotes / Invoicing / Delivery Notes list filter
work that already shipped (commits 8faa443, ed07617, 29eb003, 9cf8e39
etc).
- 2026-08-31-rep-scoped-list-filter.md — the implementation plan
- 2026-09-01-rep-scoped-list-filter-SUMMARY.md — post-implementation
summary with the commit log + verification notes + the known
limitation (Orders/Quotes tab counts are unfiltered for reps)
Co-Authored-By: Claude Code <noreply@anthropic.com>
Three bite-sized tasks with full code:
1. TDD the order-age util (test → impl → run)
2. Wire OrderAgeBadge into /orders (replace bare date)
3. Enrich /deliveries DN row subline (customer/order/paperclip)
All UI/rendering only. No server, no schema, no MCP changes.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Two small, independent rendering improvements for /deliveries and /orders:
1. /deliveries — each delivery-note row gets a compact subline with
customer name, company name, parent order number, and a signed-copy
indicator (paperclip + filename tooltip). All data already on the
row payload — no server-side changes.
2. /orders — order card right-aligned metadata slot gets a colour-
coded age badge (today / Xd old) replacing the bare date string.
New src/lib/order-age.ts as a pure function with bucket thresholds
(green <3d, amber 3-7d, orange 8-14d, red 15+d) and a unit test.
No PB schema changes. No /api/v1 route changes. No MCP tool changes.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Two spec-vs-implementation drifts caught by the final review:
- Move 'per-row validation → 200 with reason in results[]' out of the
'Reject 400' list. The route enforces body shape (json / empty /
too-many / bad id / missing actor) as 400; the service enforces
forbidden_field / non-finite / not_found / pb_error per row so one
bad sibling never aborts the rest.
- Move the 'realtime-conflict banner' from Page-behaviour to Out of
scope (the implementation deferred it; the spec still listed it).
Refine the handling of order_number values in the MCP API. Custom free-text order_numbers are now allowed to repeat across orders without triggering collision checks, while only values formatted as ORD-YYYY-MM-NNN will be subject to collision verification. Update tests to reflect this new behavior and ensure proper functionality.
Backend-first TDD plan (pos-auth, pos-service, /api/v1/pos/* routes)
followed by relocating and rewiring the existing static POS mockup
into public/pos/, removing the hardcoded PB superuser credentials.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Scopes hardening the existing POS mockup (superuser creds removed,
catalog moved to stock_items, checkout wired to stock_ledger) as a
route inside WFDASH-WEBAPP rather than a separate service.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three documents behind the two features on this branch:
- specs/2026-08-30-non-invoiceable-order-extras-design.md — the design
the implementation followed, including why the flag must be named for
the exception (a positively-named field defaults to false and would
mark every existing row non-billable the moment it is created).
- plans/2026-08-30-non-invoiceable-order-extras.md — the task-by-task
plan, now fully implemented.
- specs/2026-08-30-quantity-handling-audit.md — the order_qty /
quantity audit that produced the preceding fix, plus the partial
collection findings that are still open.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The original sample scored 0.222 Jaccard — below the 0.3 candidate
floor — so it would have been dropped rather than tiered. Verified
0.333 for the replacement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 TDD tasks across two phases. Phase 1 (backend) ends with orders
matched automatically at intake plus a backlog sweep; Phase 2 surfaces
the paired quote line's note and attachments on both order pages.
Resolves the spec's two open questions: `after` is stable in
next/server on Next 16.2.6, and PocketBase date filters take
`.toISOString()`.
Two deviations from the spec, both noted in the plan: line pairing
hooks into the existing setOrderQuote rather than a new
linkQuoteToOrder, and the per-line context extends the existing
matched-attachments response rather than adding an endpoint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Design for closing three gaps in the existing order↔quote matcher:
- The matcher only runs when an operator clicks it, so the ~85% of
orders that arrive against a quote stay unlinked. Adds a call from
createOrder plus a catch-up sweep for the backlog.
- order_items.quote_item is only written when an order is created
from a quote, so post-hoc links never pair lines. Adds persistence
for confident pairs.
- findMatchedQuoteItem re-guesses the pairing at render time using
exact/substring matching, so buyer notes and per-line attachments
silently vanish on any wording difference. Replaced with the
tokenised matcher already in the repo.
Fuzzy candidates are windowed to 90 days before the order's own date
(widening to 12 months only when nothing clears the threshold);
exact-identifier lookups stay unwindowed.
No PocketBase schema changes required.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Companion to the design spec at docs/superpowers/specs/. Covers all 15
implementation tasks (windowRange helper, aggregations, rep-activity
pagination, route handlers, tests, PWA manifest, mobile layout,
dashboard view with realtime + bottom sheet) plus the final
verification step.
Co-Authored-By: Claude <noreply@anthropic.com>
Implementation plan for the spec committed in d87a007. Four tasks:
1. Scaffold 7 component tests (all fail initially)
2. Replace handleApprove + handleApprovePartial with lifted
SendQuoteModal + sendApprove; new 'Approve & send' button
3. Verify partial-row test passes from task 2 wiring
4. Manual smoke test against running PB
Lifts the detail-screen "Approve & send email" button behaviour onto
the pending-approvals list rows. Replaces the list's existing
two-step approve + drill-in flow with the existing combined
review-approve endpoint behind the same SendQuoteModal the detail
page already uses.
Out of scope: "approve without send" toggle in the modal, inline
markup-edit on a row.
Co-Authored-By: Claude <noreply@anthropic.com>
11 tasks covering schema, type, PATCH endpoint, three service
gate relaxations, PDF route bypass, PDF component rendering, UI
toggle on Quote Details card, MCP tool exposure, and an E2E
smoke test. TDD throughout with file:line citations to the
existing service tests as templates.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds is_no_quote boolean to quotes collection so operators can send
an official decline letter on a quote the company cannot price.
Spec covers schema, service-layer gate relaxations, PDF rendering,
UI toggle placement, error edges, tests, and rollout.
Approach A (surgical flag relaxation) chosen over B/C during
brainstorming to keep v1 minimal and reversible.
Co-Authored-By: Claude <noreply@anthropic.com>
Implementation plan for the spec at
docs/superpowers/specs/2026-08-07-order-item-context-block-design.md.
Tasks: backend service skeleton + impl + tests, /api/v1/.../context
endpoint + tests, OrderItemContextPanel + tests,
OrderItemContextPopoverTrigger + tests, sprint modal wire-in, orders
page wire-in, full test suite + build smoke.
Each task follows a TDD cycle: write failing test, run to confirm,
implement minimal code, run to confirm pass, commit.
Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
Replaces the AI Insights section in order-sprint-process-modal with a
deterministic PocketBase-derived context block (last quote, last order,
last invoiced, stock). Same backend exposed as a popover trigger on
the orders detail page, replacing the existing AI-history ⓘ there.
Backend: new src/lib/server/order-item-context-service.ts +
GET /api/v1/order-items/[id]/context. Reuses pure helpers from
item-history-service (normaliseSkuForHistory, findBestDescriptionMatch,
aggregateSuppliers, computeCheapest).
UI: new <OrderItemContextPanel/> (inline expandable for the sprint
modal) and <OrderItemContextPopoverTrigger/> (ⓘ popover for orders
page). Apply = cost + supplier only; selling stays locked to the order.
Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
5 tasks: stock-items service helper, two PDF document components +
render helpers, two GET endpoints, UI changes (wrap + buttons).
TDD per task with test-first steps. Final task is full-repo
verification + manual smoke checklist.
Co-authored-by: Orca <help@stably.ai>
Two small additions to /stock-orders:
- Wrap long text in Number + Supplier columns
- Two PDF download buttons (Stocktake PDF, Orders PDF)
Both PDFs always include all rows, ignore the active status
filter and pagination. No persistence — throwaway reports.
Design reviewed: covers architecture, data model, API contract,
error handling, testing, and manual smoke. No new PB schema,
no new types.
Co-authored-by: Orca <help@stably.ai>
Six-task TDD plan to widen listStockPurchases with ordered_value +
received_value per row, expose it via a new v1 list route, migrate
the /stock-orders page from a client component reading PB directly
to a Server Component + sibling client table component, and move the
existing 7 page tests + add an 8th.
Co-authored-by: Orca <help@stably.ai>
Add an Approved design doc for the /stock-orders list table: two new
currency columns (Ordered, Received) showing sum(line.quantity *
line.cost_price) and sum(line.quantity_received * line.cost_price) per
stock purchase. Migrates the page from client-side direct PB read to
the service layer per AGENTS.md.
Co-authored-by: Orca <help@stably.ai>