From 1bd659869ddada5fc7bfa5c383f38db018d276d2 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 4 Sep 2026 12:46:17 +0200 Subject: [PATCH] chore(pos): add pos_slip_layouts PB collection for the slip layout editor One row per letterhead, holding the reorderable/toggleable block list for each thermal slip (sale, delivery note, cash-up) plus the delivery note's default pricing toggle. Migration already run against the live PocketBase instance. --- package.json | 1 + .../2026-09-04-create-pos-slip-layouts.ts | 106 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 scripts/migrations/2026-09-04-create-pos-slip-layouts.ts diff --git a/package.json b/package.json index a437feb..c4afc37 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "pb:migrate:quote-items-user-edited": "npx tsx scripts/migrations/add-quote-items-user-edited.ts", "pb:migrate:order-status-in-stock": "echo 'deprecated: orders.status was dropped 2026-06-27. No-op.' && exit 0", "pb:migrate:orders-rep": "npx tsx scripts/migrations/2026-06-26-add-orders-rep.ts", + "pb:migrate:pos-slip-layouts": "npx tsx scripts/migrations/2026-09-04-create-pos-slip-layouts.ts", "pb:migrate:drop-orders-status": "npx tsx scripts/migrations/2026-06-27-drop-orders-status-and-wipe.ts", "pb:migrate:add-order-quote-match": "npx tsx scripts/migrations/2026-06-27-add-order-quote-match-fields.ts", "pb:migrate:add-order-delivery-note-match": "npx tsx scripts/migrations/2026-08-29-add-order-delivery-note-match-fields.ts", diff --git a/scripts/migrations/2026-09-04-create-pos-slip-layouts.ts b/scripts/migrations/2026-09-04-create-pos-slip-layouts.ts new file mode 100644 index 0000000..5e89e52 --- /dev/null +++ b/scripts/migrations/2026-09-04-create-pos-slip-layouts.ts @@ -0,0 +1,106 @@ +/** + * One-shot migration: create the `pos_slip_layouts` collection. + * + * Run with: + * + * npx tsx scripts/migrations/2026-09-04-create-pos-slip-layouts.ts + * + * One row per letterhead, holding the till's reorderable/toggleable + * block list for each of the three thermal slips (sale, delivery + * note, cash-up) plus the delivery note's "show pricing" default. + * A new collection rather than a field on `letterhead_settings` + * because that collection is shared with the main WFDASH PDF-quote + * flow — POS-only slip layout has no business being coupled to it. + * See docs/superpowers/specs/2026-09-04-pos-slip-layout-editor-design.md. + * + * Idempotent — safe to re-run; exits early if the collection exists. + * + * Rollback: delete the `pos_slip_layouts` collection in the PB admin + * UI. No other collection references it, so nothing else is affected. + */ +import PocketBase from "pocketbase"; + +const PB_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090"; +const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL; +const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD; + +async function main() { + if (!ADMIN_EMAIL || !ADMIN_PASSWORD) { + throw new Error( + "Set PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD env vars (e.g. sourced from .env.local)." + ); + } + + const pb = new PocketBase(PB_URL); + await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD); + console.log(`[pos-slip-layouts] Authenticated against ${PB_URL}`); + + const existing = await pb.collections + .getFullList() + .then((all) => all.find((c) => c.name === "pos_slip_layouts")); + + if (existing) { + console.log("[pos-slip-layouts] pos_slip_layouts already exists — skipping."); + return; + } + + const letterhead = await pb.collections.getOne("letterhead_settings"); + + await pb.collections.create({ + name: "pos_slip_layouts", + type: "base", + // Mirrors letterhead_settings' own rules — any authenticated PB + // user (the POS app itself only ever talks to this through the + // admin-authenticated service layer, same as letterhead_settings). + listRule: "@request.auth.id != ''", + viewRule: "@request.auth.id != ''", + createRule: "@request.auth.id != ''", + updateRule: "@request.auth.id != ''", + deleteRule: "@request.auth.id != ''", + fields: [ + { + name: "letterhead", + type: "relation", + required: true, + collectionId: letterhead.id, + cascadeDelete: true, + minSelect: 1, + maxSelect: 1, + }, + { + name: "sale_blocks", + type: "json", + required: false, + maxSize: 0, + }, + { + name: "delivery_blocks", + type: "json", + required: false, + maxSize: 0, + }, + { + name: "cashup_blocks", + type: "json", + required: false, + maxSize: 0, + }, + { + name: "delivery_show_pricing", + type: "bool", + required: false, + }, + ], + // One layout row per letterhead. + indexes: [ + "CREATE UNIQUE INDEX idx_pos_slip_layouts_letterhead ON pos_slip_layouts (letterhead)", + ], + }); + + console.log("[pos-slip-layouts] Created pos_slip_layouts collection. Migration complete."); +} + +main().catch((err) => { + console.error("[pos-slip-layouts] Migration failed:", err); + process.exit(1); +});