The Purchase Order form never reached the server. Creating a PO with two or more "Manual Entry" rows fired N parallel POSTs to /api/collections/stock_items/records via Promise.all. The PocketBase JS SDK auto-cancels any in-flight request whose method + path match a newer one, so the first POST was aborted with: ClientResponseError 0: The request was autocancelled. Promise.all then rejected before the PO create ever ran, so the PO was never created - only an orphan stock item per attempt. Confirmed against the production PB logs: two stock_items POSTs today, zero purchase_orders POSTs, and two orphan stock_items rows with no PO. Fixes: - src/lib/pocketbase.ts: pb.autoCancellation(false) on the browser client. Every server-side client in this repo already did this (scripts/*.mjs, server/po-api.mjs) - the browser client was the one place it was missing. - PurchaseOrderForm: resolve rows sequentially instead of Promise.all, and build the snapshot in the same pass so manual rows carry the real stock item id instead of "". - PurchaseOrderForm: disable the submit button while submitting, so a double click cannot create two POs. - PurchaseOrderForm: compare SKUs trimmed, so a stray trailing space does not create a duplicate stock item. - po-photo-ingest.mjs, jei_skill.ts, setup-users.js: same autoCancellation(false) guard.
62 lines
No EOL
2.2 KiB
JavaScript
62 lines
No EOL
2.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* PO from photo (OCR) — CLI wrapper.
|
|
*
|
|
* node scripts/po-photo-ingest.mjs <base64-image-or-file>
|
|
*
|
|
* Delegates to the shared ingestPhotoAndCreate() helper in
|
|
* `src/lib/api/photoIngest.ts`. Real OCR providers (OpenAI Vision /
|
|
* Google Document AI / AWS Textract) live in that module; the CLI just
|
|
* wires input + auth + output.
|
|
*
|
|
* Required env:
|
|
* POCKETBASE_URL defaults to https://pb.jeistk.apphub.co.za
|
|
* POCKETBASE_ADMIN_EMAIL defaults to david@wfsales.co.za
|
|
* POCKETBASE_ADMIN_PASSWORD defaults to 1234567890
|
|
* OCR_PROVIDER openai | google | textract | stub (default stub)
|
|
* OPENAI_API_KEY required for openai
|
|
*/
|
|
|
|
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import PocketBase from 'pocketbase';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
|
|
const PB_URL = process.env.POCKETBASE_URL || 'https://pb.jeistk.apphub.co.za';
|
|
const PB_EMAIL = process.env.POCKETBASE_ADMIN_EMAIL || 'david@wfsales.co.za';
|
|
const PB_PASSWORD = process.env.POCKETBASE_ADMIN_PASSWORD || '1234567890';
|
|
|
|
async function main() {
|
|
const input = process.argv[2];
|
|
if (!input) {
|
|
console.error('Usage: node scripts/po-photo-ingest.mjs <file-or-base64-string>');
|
|
process.exit(2);
|
|
}
|
|
|
|
let image: Buffer;
|
|
try {
|
|
image = await fs.readFile(input);
|
|
} catch {
|
|
image = Buffer.from(input.replace(/^data:[^;]+;base64,/, ''), 'base64');
|
|
}
|
|
|
|
const pb = new PocketBase(PB_URL);
|
|
// Never let the SDK auto-cancel a request: parallel stock-item lookups in
|
|
// photoIngest share one method+path and would otherwise cancel each other.
|
|
pb.autoCancellation(false);
|
|
await pb.admins.authWithPassword(PB_EMAIL, PB_PASSWORD);
|
|
|
|
const photoUrl = pathToFileURL(path.join(ROOT, 'src/lib/api/photoIngest.ts')).href;
|
|
const photo = await import(photoUrl);
|
|
|
|
const result = await photo.ingestPhotoAndCreate({ pb, image, mimeType: 'image/png' });
|
|
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err?.message || err);
|
|
process.exit(1);
|
|
}); |