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>
52 lines
2.1 KiB
TypeScript
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);
|
|
});
|