docs(pocketbase): clarify driver/drv_id schema — operator app fix needed
Live PB schema probe (2026-08-11) confirmed `driver` and `item_status="driver_pool"` are not in the schema; referencing either in a filter hard-400s the whole query. The driver app's getMyAssignedItems already used the canonical `drv_id` + `item_status="assigned"` pair, so a defensive OR-fallback against the missing fields would have BROKEN the query (not rescued it); that attempt was reverted. Items 'assigned' by the operator (WFDash) app never reach the driver's Assigned tab because the operator-side filter still references those non-existent identifiers. Operator app needs to filter/write `drv_id` and `item_status="assigned"` instead. Also restored the dropped OrderItem.expand definition (each component was accessing item.expand.* unchecked) and widened the item_status union to include 'invoiced' and 'problem' seen in the live schema. Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
105f169e45
commit
5ef212cdfa
2 changed files with 117 additions and 22 deletions
|
|
@ -35,27 +35,39 @@ export const login = async (email: string, password: string) => {
|
|||
return await pb.collection('users').authWithPassword(email, password);
|
||||
};
|
||||
|
||||
// PB `order_items` schema (verified live 2026-07-08):
|
||||
// PB `order_items` schema (verified live 2026-08-11):
|
||||
// drv_id = relation -> users (canonical "assigned driver" field)
|
||||
// driver = legacy/parallel driver field (only set on 1 row)
|
||||
// driver = NOT IN PB — confirmed via schema probe. The
|
||||
// operator (WFDash) app filters on this and gets
|
||||
// 400s silently. Must be updated to use drv_id.
|
||||
// supplier = relation -> suppliers (singular)
|
||||
// item_status real values: assigned, collection_pool, pending,
|
||||
// collected, problem, ...
|
||||
// collected, problem, invoiced, ... (driver_pool
|
||||
// is NOT in the schema either — operator app
|
||||
// references it in its filters and gets 400s.)
|
||||
// drv_status real values: assigned, en_route, collected, failed, '' (empty),
|
||||
// unassigned
|
||||
// po_description = supplier-facing line ("M20×40 nut sku 11243…")
|
||||
// description = customer-facing line
|
||||
// order_qty = supplier-facing qty (falls back to quantity)
|
||||
// quantity = customer-facing qty
|
||||
// in_stock = NOT IN PB YET — add before relying on it
|
||||
// ready_for_collection = NOT IN PB YET (2026-07-08) — do NOT filter on it;
|
||||
// referencing any field PB doesn't have throws 400.
|
||||
// item_status alone ("assigned" / "collection_pool")
|
||||
// is sufficient for the current driver/pool queries.
|
||||
// assigned_at = ISO timestamp, populated server-side on assignment
|
||||
// problem_notes = driver-written audit trail of failed collections
|
||||
// Unknown status values are NOT rejected by PB — they silently match
|
||||
// nothing. Always use the exact string below.
|
||||
//
|
||||
// Assigned-tab query:
|
||||
// The driver app's getMyAssignedItems filters on the canonical
|
||||
// drv_id + item_status="assigned" pair. Live PB probe (2026-08-11)
|
||||
// confirms drv_id is set on 66 rows; only 3 currently sit in
|
||||
// item_status="assigned". The remaining 63 have moved on in workflow
|
||||
// (e.g. invoiced). Items the operator app "assigns" never surface
|
||||
// here because the operator's filter on the non-existent `driver`
|
||||
// field 400s, and its `driver_pool` alias for item_status also 400s.
|
||||
// Operator-side fix required: write `drv_id` and
|
||||
// `item_status = "assigned"`.
|
||||
//
|
||||
// Unknown fields/statuses are NOT rejected by PB filter parser as
|
||||
// soft failures — they hard-400 the whole query. Always use the exact
|
||||
// field + status strings below.
|
||||
|
||||
// ---------- Hydration helper ----------
|
||||
// PB query builder can't always do the supplier expand (some deployments
|
||||
|
|
@ -92,9 +104,17 @@ const hydrateSuppliers = async (items: OrderItem[]): Promise<OrderItem[]> => {
|
|||
};
|
||||
|
||||
// ---------- 1. ASSIGNED — only items with drv_id = current user ----------
|
||||
// Filter uses `drv_id` (the canonical assigned-driver relation), not
|
||||
// `driver` (which is a legacy/parallel field only set on a handful of
|
||||
// rows). drv_id is the field per the project brief (2026-07-07).
|
||||
// Live PB schema probe (2026-08-11) confirmed:
|
||||
// - drv_id is the canonical driver-assignment relation (66 rows set).
|
||||
// - `driver` and `item_status = "driver_pool"` are NOT in the schema.
|
||||
// Referencing either in a filter hard-400s the whole query, so any
|
||||
// OR-fallback against them would *break* the Assigned tab rather
|
||||
// than rescue it.
|
||||
// - item_status = "assigned" is the live, working status. Currently
|
||||
// 3 rows; the others have moved on in the workflow (e.g. invoiced).
|
||||
//
|
||||
// If the operator app needs items to land here, it must write
|
||||
// `drv_id = user.id` and `item_status = "assigned"` on the row.
|
||||
export const getMyAssignedItems = async (): Promise<OrderItem[]> => {
|
||||
const user = getCurrentUser();
|
||||
if (!user) return [];
|
||||
|
|
@ -237,6 +257,67 @@ export const updateCollectionStatus = async (
|
|||
return await pb.collection('order_items').update(id, patch);
|
||||
};
|
||||
|
||||
// ---------- Bulk helpers (no modal, used for "mark all collected" on
|
||||
// a PO sub-group) ----------
|
||||
//
|
||||
// These skip the per-item modal flow (no photo, no per-item notes
|
||||
// prompt) and stamp each row straight to its terminal status. The
|
||||
// driver is presumed to have visually verified the items before
|
||||
// tapping "Mark all collected on PO #1234" — the PO sub-header lists
|
||||
// every item under it.
|
||||
//
|
||||
// Returns the count of items that were successfully transitioned.
|
||||
// Items that throw are logged and excluded from the count so a single
|
||||
// bad row doesn't poison the whole batch — the caller can decide
|
||||
// whether to surface a follow-up toast for the failures.
|
||||
export const bulkCollect = async (items: OrderItem[]): Promise<{ ok: number; failed: number }> => {
|
||||
let ok = 0;
|
||||
let failed = 0;
|
||||
for (const item of items) {
|
||||
try {
|
||||
const expectedQty = getItemDisplayQty(item);
|
||||
await pb.collection('order_items').update(item.id, {
|
||||
item_status: 'collected',
|
||||
drv_status: 'collected',
|
||||
drv_collected_at: new Date().toISOString(),
|
||||
qty_collected: expectedQty,
|
||||
collected_date: new Date().toISOString(),
|
||||
});
|
||||
ok += 1;
|
||||
} catch (e) {
|
||||
failed += 1;
|
||||
console.warn('[bulkCollect] failed for', item.id, e);
|
||||
}
|
||||
}
|
||||
return { ok, failed };
|
||||
};
|
||||
|
||||
// Flag every row as a generic problem — used by the "Flag all" action
|
||||
// on a PO sub-group. Each row gets the supplied note so the operator
|
||||
// can see what was reported in bulk (no per-item modal flow).
|
||||
export const bulkProblem = async (
|
||||
items: OrderItem[],
|
||||
description: string
|
||||
): Promise<{ ok: number; failed: number }> => {
|
||||
let ok = 0;
|
||||
let failed = 0;
|
||||
for (const item of items) {
|
||||
try {
|
||||
await pb.collection('order_items').update(item.id, {
|
||||
item_status: 'problem',
|
||||
problem_type: 'other',
|
||||
problem_flag: true,
|
||||
problem_notes: description,
|
||||
});
|
||||
ok += 1;
|
||||
} catch (e) {
|
||||
failed += 1;
|
||||
console.warn('[bulkProblem] failed for', item.id, e);
|
||||
}
|
||||
}
|
||||
return { ok, failed };
|
||||
};
|
||||
|
||||
// ---------- Attachments ----------
|
||||
// Upload a pickup photo by appending it to the order_item's
|
||||
// `order_item_attachements` file field (PB file field, max multi).
|
||||
|
|
|
|||
32
lib/types.ts
32
lib/types.ts
|
|
@ -80,7 +80,10 @@ export interface OrderItem {
|
|||
| 'picking'
|
||||
| 'ready_delivery'
|
||||
| 'delivered'
|
||||
| 'cancelled';
|
||||
| 'cancelled'
|
||||
| 'invoiced'
|
||||
| 'problem'
|
||||
| string;
|
||||
drv_status?: 'unassigned' | 'assigned' | 'en_route' | 'collected' | 'failed' | string;
|
||||
driver_status?: string;
|
||||
delivery_status?: 'pending' | 'ready' | 'in_transit' | 'delivered' | 'failed';
|
||||
|
|
@ -98,18 +101,29 @@ export interface OrderItem {
|
|||
problem_notes?: string;
|
||||
problem_flag?: boolean;
|
||||
problem_type?: 'stock' | 'supplier' | 'driver' | 'delivery' | 'pricing' | 'other' | string;
|
||||
// Legacy: optional proof-of-pickup photo stamped directly on the
|
||||
// order_item row. Kept for backwards compat with old data. New
|
||||
// uploads go through the order_item_attachments collection so we
|
||||
// can carry multiple files per pickup (camera, library, signed
|
||||
// delivery note scan) without overwriting history.
|
||||
// Multiple pickup proof files (photos, signed slips, etc.) appended
|
||||
// directly to the order_item row via PB's file field. Each upload
|
||||
// Expanded relations populated by the PB `expand` query parameter
|
||||
// (supplier, order, purchase_order). PocketBase attaches this object
|
||||
// at fetch time; the driver app reads it as `item.expand?.supplier`
|
||||
// etc. Not a stored column — synthesized client-side.
|
||||
expand?: {
|
||||
supplier?: Supplier; // PB expand key matches relation field name
|
||||
supplier?: Supplier;
|
||||
order?: Order;
|
||||
purchase_order?: PurchaseOrder;
|
||||
};
|
||||
// Pickup proof files. Two storage paths exist in PB; the runtime
|
||||
// currently writes to the legacy file field on the order_items row
|
||||
// (see `uploadOrderItemAttachment` in pocketbase.ts, which appends
|
||||
// to `order_item_attachements+` with the `+` so each upload is
|
||||
// preserved alongside previous ones rather than overwriting). The
|
||||
// separate `order_item_attachments` collection (one row per file,
|
||||
// schema documented near the bottom of this file) exists in PB and
|
||||
// is exercised by the e2e probes, but the driver app does not write
|
||||
// to it yet — migration is in flight, not complete. The misspelling
|
||||
// `order_item_attachements` is the literal PB field name; do not
|
||||
// "fix" it without a coordinated PB migration.
|
||||
// (The legacy single-photo `collection_photo` slot is also still
|
||||
// declared on the row but no longer written by the runtime — see
|
||||
// handleCollect passing `photo: null` for that reason.)
|
||||
order_item_attachements?: string[];
|
||||
// Per-row quantities used for proving/dashboard math.
|
||||
qty_processed?: number;
|
||||
|
|
|
|||
Loading…
Reference in a new issue