@flowselections/mailbox-orders 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"mailbox-ai.server.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-ai.server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mailbox-ai.server.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-ai.server.ts"],"names":[],"mappings":"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"}
@@ -195,7 +195,8 @@ function strList(v) {
195
195
  ? v.map((x) => (typeof x === "string" ? x.slice(0, 120) : "")).filter(Boolean).slice(0, 50)
196
196
  : [];
197
197
  }
198
- function normalize(raw) {
198
+ function normalize(raw, productCatalog) {
199
+ const allowedProductIds = new Set(productCatalog.map((product) => product.id));
199
200
  const fields = {};
200
201
  const rawFields = raw?.fields && typeof raw.fields === "object" ? raw.fields : {};
201
202
  for (const [k, v] of Object.entries(rawFields)) {
@@ -213,7 +214,9 @@ function normalize(raw) {
213
214
  const lines = rawLines
214
215
  .map((l) => ({
215
216
  raw_product_text: typeof l?.raw_product_text === "string" ? l.raw_product_text.slice(0, 500) : "",
216
- product_id: typeof l?.product_id === "string" && l.product_id.length === 36 ? l.product_id : null,
217
+ product_id: typeof l?.product_id === "string" && allowedProductIds.has(l.product_id)
218
+ ? l.product_id
219
+ : null,
217
220
  plant_type: typeof l?.plant_type === "string" && l.plant_type.trim() ? l.plant_type.trim().slice(0, 200) : null,
218
221
  quantity: num(l?.quantity),
219
222
  unit: typeof l?.unit === "string" ? l.unit.slice(0, 40) : null,
@@ -283,7 +286,7 @@ export async function parseOrderEmail(admin, args) {
283
286
  const started = Date.now();
284
287
  try {
285
288
  const completion = await callProvider(cfg, system, user);
286
- const parsed = normalize(extractJson(completion.text));
289
+ const parsed = normalize(extractJson(completion.text), args.productCatalog);
287
290
  const durationMs = Date.now() - started;
288
291
  await logAiCall(admin, {
289
292
  moduleKey: cfg.moduleKey,
@@ -160,6 +160,7 @@ export declare const approveProposal: import("@tanstack/start-client-core").Opti
160
160
  ok: true;
161
161
  orderId: string;
162
162
  orderNumber: string;
163
+ alreadyApproved: boolean;
163
164
  shortages: any[];
164
165
  }>>;
165
166
  export declare const rejectProposal: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
@@ -1 +1 @@
1
- {"version":3,"file":"mailbox.functions.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox.functions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mailbox.functions.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox.functions.ts"],"names":[],"mappings":"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"}
@@ -328,59 +328,37 @@ export const approveProposal = createServerFn({ method: "POST" })
328
328
  throw new Error(blockText);
329
329
  }
330
330
  }
331
- const orderNumber = `MB-${Date.now()}`;
332
- const { data: order, error: orderErr } = await supabaseAdmin
333
- .from("orders").insert({
334
- order_number: orderNumber,
335
- customer_name: data.customerName,
336
- customer_id: data.customerId,
337
- order_date: new Date().toISOString().slice(0, 10),
338
- delivery_date: data.deliveryDate,
339
- status: "nieuw",
340
- total: "0",
341
- notes: data.notes,
342
- custom_fields: { ...customFields, source: "mailbox" },
343
- }).select("id").single();
344
- if (orderErr)
345
- throw new Error(orderErr.message);
346
- const items = data.lines.map((l) => ({
347
- order_id: order.id,
348
- product_id: l.product_id,
349
- product_name: l.raw_product_text || "Onbekend product",
350
- quantity: Math.max(1, Math.round(l.quantity ?? 1)),
351
- unit: l.unit ?? "stuks",
352
- }));
353
- const { error: itemsErr } = await supabaseAdmin.from("order_items").insert(items);
354
- if (itemsErr)
355
- throw new Error(itemsErr.message);
356
- if (data.fieldValues && data.fieldValues.length > 0) {
357
- for (const fv of data.fieldValues) {
358
- await supabaseAdmin.from("mailbox_proposal_field_values").upsert({
359
- proposal_id: data.proposalId,
360
- field_key: fv.field_key,
361
- value: fv.value === undefined ? null : fv.value,
362
- ai_filled: false,
363
- needs_review: false,
364
- }, { onConflict: "proposal_id,field_key" });
365
- }
331
+ if (!data.customerId)
332
+ throw new Error("Selecteer een bestaande klant.");
333
+ if (data.lines.some((line) => !line.product_id || !line.quantity || line.quantity <= 0)) {
334
+ throw new Error("Iedere bestelregel moet aan een bestaand product gekoppeld zijn.");
366
335
  }
367
- await supabaseAdmin.from("mailbox_order_proposals").update({
368
- status: "approved",
369
- reviewed_by: context.userId,
370
- reviewed_at: new Date().toISOString(),
371
- created_order_id: order.id,
372
- }).eq("id", data.proposalId);
373
- const { data: prop } = await supabaseAdmin
374
- .from("mailbox_order_proposals").select("message_id").eq("id", data.proposalId).single();
375
- if (prop?.message_id) {
376
- await supabaseAdmin.from("mailbox_messages").update({ status: "converted" }).eq("id", prop.message_id);
336
+ const { data: approval, error: approvalError } = await supabaseAdmin.rpc("approve_mailbox_order_proposal", {
337
+ p_proposal_id: data.proposalId,
338
+ p_reviewed_by: context.userId,
339
+ p_customer_id: data.customerId,
340
+ p_delivery_date: data.deliveryDate,
341
+ p_notes: data.notes ?? "",
342
+ p_custom_fields: customFields,
343
+ p_lines: data.lines.map((line) => ({
344
+ product_id: line.product_id,
345
+ quantity: line.quantity,
346
+ unit: line.unit,
347
+ })),
348
+ p_field_values: data.fieldValues ?? [],
349
+ });
350
+ if (approvalError)
351
+ throw new Error(approvalError.message);
352
+ const result = approval;
353
+ if (!result?.orderId || !result.orderNumber) {
354
+ throw new Error("Bestelling kon niet veilig worden aangemaakt.");
377
355
  }
378
356
  // ─── Gebeurtenis melden aan de Logistics Engine ─────────────────────────
379
357
  const deliveryLocationRef = typeof customFields["delivery_location"] === "string" ? customFields["delivery_location"] : null;
380
358
  await context.supabase.rpc("logistics_emit_event", {
381
359
  p_source_module: "mailbox",
382
360
  p_event_type: "order.received",
383
- p_order_ref: orderNumber,
361
+ p_order_ref: result.orderNumber,
384
362
  p_payload: {
385
363
  checkinAt: data.deliveryDate,
386
364
  pickupAt: data.deliveryDate,
@@ -392,9 +370,15 @@ export const approveProposal = createServerFn({ method: "POST" })
392
370
  ruleVersion: null,
393
371
  },
394
372
  p_shadow: true,
395
- p_idempotency_key: `mailbox:${orderNumber}:order.received:v1`,
373
+ p_idempotency_key: `mailbox:${data.proposalId}:order.received:v1`,
396
374
  });
397
- return { ok: true, orderId: order.id, orderNumber, shortages };
375
+ return {
376
+ ok: true,
377
+ orderId: result.orderId,
378
+ orderNumber: result.orderNumber,
379
+ alreadyApproved: result.alreadyApproved === true,
380
+ shortages,
381
+ };
398
382
  });
399
383
  export const rejectProposal = createServerFn({ method: "POST" })
400
384
  .middleware([requireSupabaseAuth])
@@ -522,106 +506,14 @@ export const createCustomerFromGroup = createServerFn({ method: "POST" })
522
506
  throw new Error(error.message);
523
507
  return { id: inserted.id, created: true };
524
508
  });
525
- // Producten voor de picker — ALLE artikelen uit de voorraad:
526
- // 1. alles in `products`
527
- // 2. elk handelsitem uit `floriday_trade_items_cache` (Voorraad Module) dat nog
528
- // geen products-rij heeft, wordt hier automatisch aangemaakt (id = floriday_id)
529
- // zodat bestelregels, belading en orders er gewoon naar kunnen verwijzen
530
- // Voorraad komt uit `trade_item_stock` en `trade_item_stock_view`.
509
+ // Producten voor de picker: uitsluitend bestaande rijen uit `products`.
510
+ // Deze module maakt nooit zelf artikelen aan. Voorraad wordt alleen als
511
+ // aanvullende informatie opgehaald uit de Voorraad Module.
531
512
  export const listProductsForPicker = createServerFn({ method: "GET" })
532
513
  .middleware([requireSupabaseAuth])
533
514
  .handler(async ({ context }) => {
534
515
  const { supabaseAdmin } = await import("../integrations/supabase/client.server");
535
- // ── 1. Handelsitems uit de Voorraad Module materialiseren ───────────────
536
- const { data: cacheRows } = await supabaseAdmin
537
- .from("floriday_trade_items_cache")
538
- .select("floriday_id, is_deleted, data")
539
- .range(0, 4999);
540
- const tradeItems = (Array.isArray(cacheRows) ? cacheRows : []).filter((r) => !r?.is_deleted && typeof r?.floriday_id === "string");
541
- const { data: existingRows } = await supabaseAdmin
542
- .from("products")
543
- .select("id, custom_fields, floriday_trade_item_id")
544
- .range(0, 9999);
545
- const existing = Array.isArray(existingRows) ? existingRows : [];
546
- const known = new Set();
547
- for (const p of existing) {
548
- known.add(String(p.id));
549
- const fid = p?.custom_fields?.floriday_id ?? p?.floriday_trade_item_id;
550
- if (typeof fid === "string" && fid)
551
- known.add(fid);
552
- }
553
- const norm = (v) => Math.max(1, Math.round(Number(v ?? 1)) || 1);
554
- const nameOf = (d) => {
555
- const n = d?.tradeItemName;
556
- if (typeof n === "string" && n.trim())
557
- return n.trim();
558
- if (n && typeof n === "object") {
559
- const v = n.nl ?? n.en ?? Object.values(n).find((x) => typeof x === "string");
560
- if (typeof v === "string" && v.trim())
561
- return v.trim();
562
- }
563
- return typeof d?.tradeItemReference === "string" && d.tradeItemReference
564
- ? d.tradeItemReference
565
- : "Onbekend artikel";
566
- };
567
- const missing = tradeItems.filter((r) => !known.has(r.floriday_id));
568
- const buildRow = (r) => {
569
- const d = r.data ?? {};
570
- const pk = Array.isArray(d.packingConfigurations) ? d.packingConfigurations[0] ?? {} : {};
571
- return {
572
- id: r.floriday_id,
573
- product: nameOf(d),
574
- // batch + location zijn NOT NULL zonder default in de Voorraad Module
575
- batch: "",
576
- location: "",
577
- unit: "stuks",
578
- quantity: 0,
579
- plants_per_crate: norm(pk?.piecesPerPackage ?? pk?.unitsPerPackage),
580
- crates_per_layer: norm(pk?.packagesPerLayer),
581
- layers_per_cart: norm(pk?.layersPerLoadCarrier),
582
- floriday_trade_item_id: r.floriday_id,
583
- article_number: typeof d?.supplierArticleCode === "string" ? d.supplierArticleCode : null,
584
- custom_fields: { floriday_id: r.floriday_id, source: "floriday_trade_items_cache" },
585
- };
586
- };
587
- // Concept-artikelen uit de Voorraad Module (nog niet in Floriday) ook tonen
588
- const { data: draftRowsAll } = await supabaseAdmin
589
- .from("trade_item_drafts")
590
- .select("id, data, floriday_id")
591
- .range(0, 4999);
592
- const draftMissing = (Array.isArray(draftRowsAll) ? draftRowsAll : []).filter((d) => typeof d?.id === "string" && !known.has(d.id) &&
593
- !(typeof d?.floriday_id === "string" && known.has(d.floriday_id)));
594
- const buildDraftRow = (r) => {
595
- const d = r.data ?? {};
596
- const pk = Array.isArray(d.packingConfigurations) ? d.packingConfigurations[0] ?? {} : {};
597
- return {
598
- id: r.id,
599
- product: nameOf(d),
600
- batch: "",
601
- location: "",
602
- unit: "stuks",
603
- quantity: 0,
604
- plants_per_crate: norm(pk?.unitsPerPackage ?? pk?.piecesPerPackage),
605
- crates_per_layer: norm(pk?.packagesPerLayer),
606
- layers_per_cart: norm(pk?.layersPerLoadCarrier),
607
- article_number: typeof d?.supplierArticleCode === "string" ? d.supplierArticleCode : null,
608
- custom_fields: { draft_id: r.id, source: "trade_item_drafts" },
609
- };
610
- };
611
- const toInsert = [...missing.map(buildRow), ...draftMissing.map(buildDraftRow)];
612
- // Artikelen die (nog) niet in `products` staan, tonen we hoe dan ook —
613
- // ook als materialiseren faalt.
614
- let virtualRows = [];
615
- if (toInsert.length > 0) {
616
- const { error: upsertError } = await supabaseAdmin
617
- .from("products")
618
- .upsert(toInsert, { onConflict: "id" });
619
- if (upsertError) {
620
- console.error("[listProductsForPicker] materialiseren mislukt:", upsertError.message);
621
- virtualRows = toInsert;
622
- }
623
- }
624
- // ── 2. Voorraad verzamelen ──────────────────────────────────────────────
516
+ // ── 1. Voorraad verzamelen ──────────────────────────────────────────────
625
517
  const { data: stockRows } = await supabaseAdmin
626
518
  .from("trade_item_stock")
627
519
  .select("trade_item_key, physical_stock, reserved_stock");
@@ -657,8 +549,6 @@ export const listProductsForPicker = createServerFn({ method: "GET" })
657
549
  .in("id", Array.from(availByDraftId.keys()));
658
550
  for (const d of Array.isArray(drafts) ? drafts : []) {
659
551
  const qty = availByDraftId.get(d.id) ?? 0;
660
- // Concepten worden ook als product gematerialiseerd op hun eigen id
661
- addAvail(availByProductId, d.id, qty);
662
552
  const linked = d?.data?.linkedProductId;
663
553
  if (typeof linked === "string" && linked)
664
554
  addAvail(availByProductId, linked, qty);
@@ -684,19 +574,13 @@ export const listProductsForPicker = createServerFn({ method: "GET" })
684
574
  catch {
685
575
  // view optioneel
686
576
  }
687
- // ── 3. Alle producten teruggeven ────────────────────────────────────────
577
+ // ── 2. Alle bestaande producten teruggeven ─────────────────────────────
688
578
  const { data } = await supabaseAdmin
689
579
  .from("products")
690
580
  .select("id, product, unit, plants_per_crate, crates_per_layer, layers_per_cart, custom_fields, floriday_trade_item_id")
691
581
  .order("product")
692
582
  .range(0, 9999);
693
- const dbRows = Array.isArray(data) ? data : [];
694
- const seen = new Set(dbRows.map((p) => String(p.id)));
695
- const allRows = [
696
- ...dbRows,
697
- ...virtualRows.filter((v) => !seen.has(String(v.id))),
698
- ];
699
- const items = allRows
583
+ const items = (Array.isArray(data) ? data : [])
700
584
  .map((p) => {
701
585
  const fid = p?.custom_fields?.floriday_id ?? p?.floriday_trade_item_id;
702
586
  const byProd = availByProductId.get(p.id) ?? 0;
@@ -0,0 +1,3 @@
1
+ /** Haalt een aantal-prefix ("3x ", "3 x ", "10 stuks ") weg uit ruwe mailtekst. */
2
+ export declare function stripLeadingQuantity(text: string | null | undefined): string;
3
+ //# sourceMappingURL=product-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-name.d.ts","sourceRoot":"","sources":["../../src/lib/product-name.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAO5E"}
@@ -0,0 +1,10 @@
1
+ /** Haalt een aantal-prefix ("3x ", "3 x ", "10 stuks ") weg uit ruwe mailtekst. */
2
+ export function stripLeadingQuantity(text) {
3
+ const t = (text ?? "").trim();
4
+ if (!t)
5
+ return "";
6
+ const cleaned = t
7
+ .replace(/^\d+[.,]?\d*\s*(x|st|stk|stuks|stuk|planten|plant|fust|fusten|pcs)?\s*[-:·]?\s*/i, "")
8
+ .trim();
9
+ return cleaned || t;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/mailbox-orders",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",