diff --git a/package.json b/package.json
index 546fb64..25c5fb4 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"pb:migrate:rep-trip-legs": "npx tsx scripts/migrations/2026-09-28-create-rep-trip-legs.ts",
"pb:migrate:quote-assistant": "npx tsx scripts/migrations/2026-10-16-add-quote-assistant-fields.ts",
"pb:migrate:collection-ai": "npx tsx scripts/migrations/2026-10-17-add-collection-ai.ts",
+ "pb:migrate:pool-pending": "npx tsx scripts/migrations/2026-10-30-add-stock-pool-pending.ts",
"pb:migrate:collection-auto-push": "npx tsx scripts/migrations/2026-10-28-add-collection-auto-push.ts",
"pb:migrate:order-assistant": "npx tsx scripts/migrations/2026-10-24-add-order-assistant-fields.ts",
"pb:migrate:po-number-unique": "npx tsx scripts/migrations/2026-10-19-po-number-unique-index.ts",
diff --git a/scripts/migrations/2026-10-30-add-stock-pool-pending.ts b/scripts/migrations/2026-10-30-add-stock-pool-pending.ts
new file mode 100644
index 0000000..27c8157
--- /dev/null
+++ b/scripts/migrations/2026-10-30-add-stock-pool-pending.ts
@@ -0,0 +1,47 @@
+/**
+ * One-shot migration: let a stock line wait in the collection pool as
+ * "pending", like a customer stop that is not ready yet.
+ *
+ * stock_purchase_items.pool_pending bool (false = ready, the old behaviour)
+ *
+ * Inverted on purpose: a new bool is false on every existing row, so every
+ * existing stock line stays ready and nothing changes until someone presses
+ * Back to pending.
+ *
+ * Run with: npm run pb:migrate:pool-pending
+ *
+ * Idempotent. Manual step on the live PB, before deploying.
+ */
+import "./_env-loader";
+import PocketBase from "pocketbase";
+
+const PB_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || process.env.PB_URL || "http://127.0.0.1:8090";
+const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL;
+const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD;
+const TAG = "[pool-pending]";
+
+async function main() {
+ if (!ADMIN_EMAIL || !ADMIN_PASSWORD) {
+ throw new Error("Set PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD (e.g. from .env.local).");
+ }
+ const pb = new PocketBase(PB_URL);
+ await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);
+ console.log(`${TAG} Authenticated against ${PB_URL}`);
+ const collection = await pb.collections.getOne("stock_purchase_items");
+ if (collection.fields.some((f: { name: string }) => f.name === "pool_pending")) {
+ console.log(`${TAG} stock_purchase_items.pool_pending already exists — skipping.`);
+ return;
+ }
+ await pb.collections.update(collection.id, {
+ fields: [
+ ...collection.fields,
+ { name: "pool_pending", type: "bool", required: false, presentable: false },
+ ],
+ });
+ console.log(`${TAG} stock_purchase_items.pool_pending added.`);
+}
+
+main().catch((err) => {
+ console.error(`${TAG} Migration failed:`, err);
+ process.exit(1);
+});
diff --git a/src/app/(app)/collection-pool/components.tsx b/src/app/(app)/collection-pool/components.tsx
index edc6bf1..228d39c 100644
--- a/src/app/(app)/collection-pool/components.tsx
+++ b/src/app/(app)/collection-pool/components.tsx
@@ -166,7 +166,7 @@ export function PoolRow({
// to clear. Hiding it when there is nothing to undo keeps the row's
// actions honest instead of offering a button that silently does
// nothing.
- const showBackToPending = isStock ? isAssigned : Boolean(row.ready_for_collection) || isAssigned;
+ const showBackToPending = Boolean(row.ready_for_collection) || isAssigned;
// `collection_date` on a pending stop is a FORECAST from the supplier;
// on a ready stop it's the confirmed collection day. Either way the
// row goes blue so a dated stop stands out in the list, but the badge
@@ -379,20 +379,17 @@ export function PoolRow({
· {collectionDate.relative}
)}
- {/* Ready/pending badge is customer-only — stock lines
- don't have a `ready_for_collection` concept. */}
- {!isStock && (
-
- {readyLabel}
-
- )}
+ {/* Ready/pending badge: stock lines carry it too (`pool_pending`). */}
+
+ {readyLabel}
+
{!isStock && noteLines.length > 0 && (
Notes
@@ -422,9 +419,9 @@ export function PoolRow({
size="sm"
variant={isAssigned ? "outline" : "default"}
onClick={() => onOpenAssign(row)}
- disabled={busy || (!isStock && !row.ready_for_collection)}
+ disabled={busy || !row.ready_for_collection}
title={
- !isStock && !row.ready_for_collection
+ !row.ready_for_collection
? "Mark this stop as ready before assigning"
: undefined
}
@@ -435,7 +432,7 @@ export function PoolRow({
is still pending. The reverse direction ("back to pending")
is its own action below, because it also clears a driver
assignment and always demands a reason. */}
- {!isStock && !row.ready_for_collection && (
+ {!row.ready_for_collection && (