- Switch from getList(1, 500) to getFullList so candidate sets
aren't silently truncated at 500 rows (matches orders-service
and quotes-service conventions).
- Tighten the po_draft filter to item_status = po_generated so
items in downstream statuses (collection_pool, delivered, etc.)
don't get pulled over the wire.
- Tighten the po_sent filter to exclude item_status in
{ready_delivery, invoiced, delivered, cancelled} for the same
reason.
- Drop the redundant ready_delivery post-filter on po_sent now
that the PB layer handles it.
- Tests capture the filter string passed to getFullList and assert
its clauses so regressions are caught.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
Two related enhancements to the orders review modal:
* Tier 1 — header totals: the "Order items" header now shows the
item count plus the cost and selling totals (qty*price summed
across the live draft state). Uses formatCurrency from
pb-helpers.ts for the ZAR formatting.
* Tier 2 — add/remove items: the edit-mode items list now has an
"Add item" button (Plus icon) and a per-row Trash icon. New rows
get a sentinel id like "__new_1" and a left-edge accent stripe;
rows flagged for removal flip to a strikethrough + reduced opacity
with disabled inputs (toggle button flips to an Undo2 icon).
On save, the route posts to /api/v1/orders/[id]/review-save with
new itemsToAdd and itemsToRemove arrays. The service layer adds
createOrderItems + softFlagOrderItems handlers (set
item_status="removed" — operator confirmed the value is in the
PB admin schema). The modal re-keys the new drafts with the
returned PB ids so a follow-up save treats them as existing
items.
Partial-failure UI now has three sections (updates / adds /
removes) sharing a labelled FailureSection helper. Response
envelope now includes itemsAdded and itemsRemoved alongside the
existing items block, plus a flat partialFailures array for
the toast summary.
Type cleanup: "removed" added to ItemStatus and the
ITEM_STATUS_VISUAL map.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
Wires a new /orders/sprint surface that lists pending order items
(purchase_order = '' && item_status = 'pending') grouped by category,
mirrors the quote-sprint workflow. Click a row to open
OrderSprintProcessModal for assigning a supplier, cost, and emitting
a purchase order.
Sidebar gets an "Order Sprint" entry under the Orders group.
Notes:
- No automated tests yet (manual smoke test recommended).
- OrderSprintItem type is defined locally in both page.tsx and the
modal — duplicated but consistent; extract to types.ts in a follow-up.
- Page + modal use client-side getPocketBase (Pb is filtered to the
user); the counts route is server-side.
Co-authored-by: Orca <help@stably.ai>
The merge of feature/order-sprint-item-history into main concatenated
src/lib/types.ts by appending the last 50 lines of the feature branch's
version. The two helper interfaces ItemHistorySupplier and
ItemHistoryOtherQuote were defined earlier in the file (lines 1416-1433)
and were dropped, breaking the references in ItemHistory and the import
in item-history-service.ts. This commit restores them.
Co-authored-by: Orca <help@stably.ai>
The orders review modal's source-of-truth panel now reads from the
order_attachements multi-file field on the orders collection
(pbc_3527180448) instead of the quote_attachments collection rows
on the parent quote. Older orders were migrated on 2026-07-29; the
order-level field is authoritative going forward.
- Added orderAttachementsToQuotes() helper in
src/app/(app)/orders/page.tsx that synthesises the
QuoteAttachment[] shape from the order's file-field array, deriving
kind (pdf/image/doc) and content_type from the filename extension.
PB file fields don't carry a content-type from the wire, so we
infer from extension (png/jpg/gif/webp/svg/bmp/pdf/docx/txt).
- Replaced the quote_attachments collection query in
OrdersReviewModalMount with the helper call.
- Added orders + pbc_3527180448 to the ALLOWED_COLLECTIONS whitelist
in the file proxy route so the review modal's PDF/image preview
iframes can stream through /api/v1/files/... .
Co-authored-by: Orca <help@stably.ai>
Items in the orders review modal are read-only by default. A new
'Edit items' button (Pencil icon, top right of the items list)
opens a confirmation dialog before the inputs become editable.
- State: editingItems (default false), confirmingEdit (default false).
- Both reset to false whenever the modal re-opens (useEffect on
[open, order.id]) so re-opening always starts read-only.
- All 4 per-row inputs (qty, cost, markup, selling) carry
disabled={!editingItems || saving}.
- Visual hint: when editing, the items list gets a subtle warn
ring + tint (ring-1 ring-warn/40 bg-warn/5).
- data-testid hooks: review-edit-items-btn on the button,
review-edit-confirm-dialog on the dialog, review-items-list on
the ul with data-editing attribute.
- 4 new tests cover: read-only default, button opens dialog,
confirm enables editing, cancel keeps read-only.
Also adds order_attachements?: string[] to the Order type — the
PB file field on the orders collection used by the modal's source-
of-truth panel (separate commit wires the data).
Co-authored-by: Orca <help@stably.ai>
A post-commit re-edit of the test file introduced a duplicate
updateOrderItemsPrices describe block at the tail. All tests still
pass (the duplicates run independently). Keep the file aligned
with HEAD.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
Fix#4: The customer label rendered below the combobox read the
original `customer` prop, so switching the selection left a stale
name on screen. Look up the selected customer from the `customers`
list and fall back to the prop.
Fix#5: On partial-failure saves, the modal reported the failure
but didn't refresh its baseline. A retry would resend already-
successful rows. Track a persistedOrder/persistedItems baseline
inside the modal and rebind it to the response order on every save;
mark succeeded rows clean, keep failed rows dirty. Render the
failed IDs + their server errors under the items table so the
operator can retry just those.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
Cross-order rejection (Fix#3), PB filter quote escape (Fix#6), and
mapping of PB unique-index write errors to OrderNumberCollisionError
(Fix#7) get full coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
A request to order A could include an item ID from order B and
silently modify it. Read the current row and reject with failed[]
when current.order does not match orderId. Test covers the cross-
order case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
getAdminPocketBase reads PB_ADMIN_EMAIL/PB_ADMIN_PASSWORD which are
server-only env vars. The modal mount is a client component, so the
admin client would fail to authenticate at runtime. The users PB
session already has read access; switch to getPocketBase.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
This route is invoked from the in-app review modal, not from an external
agent integration. The browser session is already authenticated via PB;
calling assertAgentAuth here caused every modal save to 401.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>