WFDASH-WEBAPP/scripts/migrations/2026-10-19-stock-photos-multi.ts
Dave d3132ab7f6 feat(collection-confirm): collected stock orders go through Collection Confirm
The driver app records the full quantity as received when it collects a
stock pickup, and Collection Confirm only listed stock lines that were
not fully received — so collected stock orders skipped it and sat at
"sent", outside the accountant's booking queue, until someone clicked
Mark received (29 stuck on 2026-10-19).

- STOCK_ON_PAGE_FILTER / stockLineOnPage: a collected stock line stays on
  Collection Confirm until its stock order is received; cancelled orders
  never show. Shared by the list and the AI check (so those lines get
  photos read and ticks too).
- Confirm stock: markStockOrdersReceived flips each stock order to
  "received" once every line is collected/booked (→ booking queue);
  orders with lines still out stay open, reported as stillOpen. The
  route returns { updatedIds, received, stillOpen }; the toast says so.
- Stock photos: `npm run pb:migrate:stock-photos` makes
  stock_purchases.additional_attachments multi-file;
  addStockPurchaseAttachment, Move photo and Delete photo add/remove with
  `+`/`-` instead of replacing the single slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:03:33 +02:00

52 lines
2.1 KiB
TypeScript

/**
* One-shot migration: let stock orders hold several photos.
*
* stock_purchases.additional_attachments maxSelect 0 (one file) → 10
*
* Why: drivers now photograph the supplier invoice for stock pickups too
* (one per PO, "page 2", "page 3"…), and Collection Confirm's "Move photo"
* can add a photo to a stock order. With a single-file field each new
* photo replaced the last.
*
* Run with:
*
* npm run pb:migrate:stock-photos
*
* Idempotent — skips when the field already allows more than one file.
* Existing single photos stay (PB turns the value into a one-item list).
* Manual step on the live PB; run it BEFORE deploying the driver app
* change. Rollback: set Max select back to 1 in the PB admin.
*/
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 = "[stock-photos]";
const MAX = 10;
async function main() {
if (!ADMIN_EMAIL || !ADMIN_PASSWORD) throw new Error("PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD are required");
const pb = new PocketBase(PB_URL);
pb.autoCancellation(false);
await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD);
const collection = await pb.collections.getOne("stock_purchases");
const fields = collection.fields as Array<Record<string, unknown> & { name: string; maxSelect?: number }>;
const field = fields.find((f) => f.name === "additional_attachments");
if (!field) throw new Error("stock_purchases.additional_attachments not found");
if ((field.maxSelect ?? 0) > 1) {
console.log(`${TAG} additional_attachments already allows ${field.maxSelect} files — nothing to do.`);
return;
}
await pb.collections.update(collection.id, {
fields: fields.map((f) => (f.name === "additional_attachments" ? { ...f, maxSelect: MAX } : f)),
});
console.log(`${TAG} stock_purchases.additional_attachments now allows ${MAX} files.`);
}
main().catch((err) => {
console.error(`${TAG} failed:`, err?.response ?? err);
process.exit(1);
});