@flowselections/mailbox-orders 1.0.15 → 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":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AA0B9D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;QAiBf,MAAM;cACM,MAAM;mBACI,MAAM;kBACE,MAAM,GAAG,IAAI;YACzB,MAAM,GAAG,IAAI;gBACL,MAAM,GAAG,IAAI;sBACD,MAAM,GAAG,IAAI;;;GAGnE,CAAC;AAWL,eAAO,MAAM,cAAc,2EACD,OAAO;;;;;;;;GA+B7B,CAAC;AAGL,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;GAWzB,CAAC;AAEL,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB;;;;8CAEL,OAAO;;;;;GAK7B,CAAC;AAEL,iFAAiF;AACjF,eAAO,MAAM,8BAA8B;;;;;;;;;GAMvC,CAAC;AAML,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;GAWrB,CAAC;AAEL,eAAO,MAAM,YAAY;;;;8CAEC,OAAO;;;;GAa7B,CAAC;AAKL,eAAO,MAAM,aAAa;;;;8CAEA,OAAO;;;;GAmC7B,CAAC;AAGL,qEAAqE;AACrE,eAAO,MAAM,iBAAiB;;;;8CAEJ,OAAO;;;;GAyC7B,CAAC;AASL,eAAO,MAAM,uBAAuB;;;;;WAO+B,MAAM,GAAG,IAAI;;;;GAC5E,CAAC;AAgBL,eAAO,MAAM,eAAe;;;;8CAEF,OAAO;;;;;;;;;;;;;;;;;;;;;;GA6M7B,CAAC;AAEL,eAAO,MAAM,cAAc;;;;8CAED,OAAO;;;;;GAY7B,CAAC;AAGL,eAAO,MAAM,cAAc;;;;8CAED,OAAO;;;;GAW7B,CAAC;AAQL,eAAO,MAAM,sBAAsB;;;;;;GAiD/B,CAAC;AAIL,eAAO,MAAM,uBAAuB;;;;8CAEV,OAAO;;;;;;;;;;;;QAyBiB,MAAM;;;QAoBxB,MAAM;;GAClC,CAAC;AAQL,eAAO,MAAM,qBAAqB;;;;;;;GAyN9B,CAAC;AAiBL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;qBAqBS,MAAM;sBAAgB,MAAM;gBAAU,MAAM;gBAAU,MAAM;kBAAY,MAAM;;;;uBAhCvG,MAAM;oBACT,MAAM,GAAG,IAAI;sBACX,MAAM;0BAEF,MAAM;0BACN,MAAM;yBACP,MAAM;;;qBA0FW,MAAM;eAClB,MAAM,GAAG,IAAI;;;;GAoBjC,CAAC;AAiBL,eAAO,MAAM,eAAe;;;;8CAEF,OAAO;;;;;;;;;;;;;;;;GAuC7B,CAAC;AAEL,eAAO,MAAM,cAAc;;;;0CAEL;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE;aAAR,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;GAQnC,CAAC;AAEL,eAAO,MAAM,gCAAgC;;;;0CAEvB;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE;gBAAR,MAAM;;;;;;;;;;;;;;;;;;;GAUtC,CAAC;AAKL,eAAO,MAAM,mBAAmB;;;;;;YAqBD,MAAM;;;;;GAQjC,CAAC;AAEL,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;GAS9B,CAAC;AAGL,eAAO,MAAM,gBAAgB;;;;;;GAUzB,CAAC;AAiBL,eAAO,MAAM,8BAA8B;;;;8CAEjB,OAAO;;;;0BAOT,MAAM;0BAAoB,MAAM;yBAAmB,MAAM;oBAC/D,OAAO;oBACP,MAAM,GAAG,IAAI;oBAAc,MAAM,GAAG,IAAI;sBACtC,MAAM,GAAG,IAAI;sBAAgB,MAAM,GAAG,IAAI;;yBAEO,MAAM;yBAAmB,MAAM;;;0BAL5E,MAAM;0BAAoB,MAAM;yBAAmB,MAAM;oBAC/D,OAAO;oBACP,MAAM,GAAG,IAAI;oBAAc,MAAM,GAAG,IAAI;sBACtC,MAAM,GAAG,IAAI;sBAAgB,MAAM,GAAG,IAAI;;;;;GAsM1D,CAAC"}
1
+ {"version":3,"file":"mailbox.functions.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox.functions.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,6 @@ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
4
4
  import { createClient } from "@supabase/supabase-js";
5
5
  import { z } from "zod";
6
6
  import { computeEarliestDelivery } from "../lib/delivery-date";
7
- import { stripLeadingQuantity } from "../lib/product-name";
8
7
  import { emailBodyText, inlineCidImages, normalizeAddressList, normalizeAttachments, sanitizeEmailHtml, stripInvisible, } from "./mailbox-body";
9
8
  import { EMPTY_LOAD_CARRIERS, isLoadCarrierAllowed, loadCarrierLabel, normalizeAllowedLoadCarriers, } from "./load-carriers";
10
9
  // ─── Actieve mailbox (uit gedeelde e-mail-auth module) ──────────────────────
@@ -329,76 +328,37 @@ export const approveProposal = createServerFn({ method: "POST" })
329
328
  throw new Error(blockText);
330
329
  }
331
330
  }
332
- const orderNumber = `MB-${Date.now()}`;
333
- const { data: order, error: orderErr } = await supabaseAdmin
334
- .from("orders").insert({
335
- order_number: orderNumber,
336
- customer_name: data.customerName,
337
- customer_id: data.customerId,
338
- order_date: new Date().toISOString().slice(0, 10),
339
- delivery_date: data.deliveryDate,
340
- status: "nieuw",
341
- total: "0",
342
- notes: data.notes,
343
- custom_fields: { ...customFields, source: "mailbox" },
344
- }).select("id").single();
345
- if (orderErr)
346
- throw new Error(orderErr.message);
347
- // Productnaam altijd uit de catalogus; alleen zonder koppeling terugvallen
348
- // op de (opgeschoonde) tekst uit de mail.
349
- const linkedIds = Array.from(new Set(data.lines.map((l) => l.product_id).filter((v) => !!v)));
350
- const nameById = new Map();
351
- if (linkedIds.length > 0) {
352
- const { data: prodRows } = await supabaseAdmin
353
- .from("products")
354
- .select("id, product")
355
- .in("id", linkedIds);
356
- for (const p of Array.isArray(prodRows) ? prodRows : []) {
357
- const n = typeof p?.product === "string" ? p.product.trim() : "";
358
- if (n)
359
- nameById.set(String(p.id), n);
360
- }
361
- }
362
- const items = data.lines.map((l) => ({
363
- order_id: order.id,
364
- product_id: l.product_id,
365
- product_name: (l.product_id ? nameById.get(l.product_id) : null) ||
366
- stripLeadingQuantity(l.raw_product_text) ||
367
- "Onbekend product",
368
- quantity: Math.max(1, Math.round(l.quantity ?? 1)),
369
- unit: l.unit ?? "stuks",
370
- }));
371
- const { error: itemsErr } = await supabaseAdmin.from("order_items").insert(items);
372
- if (itemsErr)
373
- throw new Error(itemsErr.message);
374
- if (data.fieldValues && data.fieldValues.length > 0) {
375
- for (const fv of data.fieldValues) {
376
- await supabaseAdmin.from("mailbox_proposal_field_values").upsert({
377
- proposal_id: data.proposalId,
378
- field_key: fv.field_key,
379
- value: fv.value === undefined ? null : fv.value,
380
- ai_filled: false,
381
- needs_review: false,
382
- }, { onConflict: "proposal_id,field_key" });
383
- }
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.");
384
335
  }
385
- await supabaseAdmin.from("mailbox_order_proposals").update({
386
- status: "approved",
387
- reviewed_by: context.userId,
388
- reviewed_at: new Date().toISOString(),
389
- created_order_id: order.id,
390
- }).eq("id", data.proposalId);
391
- const { data: prop } = await supabaseAdmin
392
- .from("mailbox_order_proposals").select("message_id").eq("id", data.proposalId).single();
393
- if (prop?.message_id) {
394
- 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.");
395
355
  }
396
356
  // ─── Gebeurtenis melden aan de Logistics Engine ─────────────────────────
397
357
  const deliveryLocationRef = typeof customFields["delivery_location"] === "string" ? customFields["delivery_location"] : null;
398
358
  await context.supabase.rpc("logistics_emit_event", {
399
359
  p_source_module: "mailbox",
400
360
  p_event_type: "order.received",
401
- p_order_ref: orderNumber,
361
+ p_order_ref: result.orderNumber,
402
362
  p_payload: {
403
363
  checkinAt: data.deliveryDate,
404
364
  pickupAt: data.deliveryDate,
@@ -410,9 +370,15 @@ export const approveProposal = createServerFn({ method: "POST" })
410
370
  ruleVersion: null,
411
371
  },
412
372
  p_shadow: true,
413
- p_idempotency_key: `mailbox:${orderNumber}:order.received:v1`,
373
+ p_idempotency_key: `mailbox:${data.proposalId}:order.received:v1`,
414
374
  });
415
- 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
+ };
416
382
  });
417
383
  export const rejectProposal = createServerFn({ method: "POST" })
418
384
  .middleware([requireSupabaseAuth])
@@ -540,106 +506,14 @@ export const createCustomerFromGroup = createServerFn({ method: "POST" })
540
506
  throw new Error(error.message);
541
507
  return { id: inserted.id, created: true };
542
508
  });
543
- // Producten voor de picker — ALLE artikelen uit de voorraad:
544
- // 1. alles in `products`
545
- // 2. elk handelsitem uit `floriday_trade_items_cache` (Voorraad Module) dat nog
546
- // geen products-rij heeft, wordt hier automatisch aangemaakt (id = floriday_id)
547
- // zodat bestelregels, belading en orders er gewoon naar kunnen verwijzen
548
- // 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.
549
512
  export const listProductsForPicker = createServerFn({ method: "GET" })
550
513
  .middleware([requireSupabaseAuth])
551
514
  .handler(async ({ context }) => {
552
515
  const { supabaseAdmin } = await import("../integrations/supabase/client.server");
553
- // ── 1. Handelsitems uit de Voorraad Module materialiseren ───────────────
554
- const { data: cacheRows } = await supabaseAdmin
555
- .from("floriday_trade_items_cache")
556
- .select("floriday_id, is_deleted, data")
557
- .range(0, 4999);
558
- const tradeItems = (Array.isArray(cacheRows) ? cacheRows : []).filter((r) => !r?.is_deleted && typeof r?.floriday_id === "string");
559
- const { data: existingRows } = await supabaseAdmin
560
- .from("products")
561
- .select("id, custom_fields, floriday_trade_item_id")
562
- .range(0, 9999);
563
- const existing = Array.isArray(existingRows) ? existingRows : [];
564
- const known = new Set();
565
- for (const p of existing) {
566
- known.add(String(p.id));
567
- const fid = p?.custom_fields?.floriday_id ?? p?.floriday_trade_item_id;
568
- if (typeof fid === "string" && fid)
569
- known.add(fid);
570
- }
571
- const norm = (v) => Math.max(1, Math.round(Number(v ?? 1)) || 1);
572
- const nameOf = (d) => {
573
- const n = d?.tradeItemName;
574
- if (typeof n === "string" && n.trim())
575
- return n.trim();
576
- if (n && typeof n === "object") {
577
- const v = n.nl ?? n.en ?? Object.values(n).find((x) => typeof x === "string");
578
- if (typeof v === "string" && v.trim())
579
- return v.trim();
580
- }
581
- return typeof d?.tradeItemReference === "string" && d.tradeItemReference
582
- ? d.tradeItemReference
583
- : "Onbekend artikel";
584
- };
585
- const missing = tradeItems.filter((r) => !known.has(r.floriday_id));
586
- const buildRow = (r) => {
587
- const d = r.data ?? {};
588
- const pk = Array.isArray(d.packingConfigurations) ? d.packingConfigurations[0] ?? {} : {};
589
- return {
590
- id: r.floriday_id,
591
- product: nameOf(d),
592
- // batch + location zijn NOT NULL zonder default in de Voorraad Module
593
- batch: "",
594
- location: "",
595
- unit: "stuks",
596
- quantity: 0,
597
- plants_per_crate: norm(pk?.piecesPerPackage ?? pk?.unitsPerPackage),
598
- crates_per_layer: norm(pk?.packagesPerLayer),
599
- layers_per_cart: norm(pk?.layersPerLoadCarrier),
600
- floriday_trade_item_id: r.floriday_id,
601
- article_number: typeof d?.supplierArticleCode === "string" ? d.supplierArticleCode : null,
602
- custom_fields: { floriday_id: r.floriday_id, source: "floriday_trade_items_cache" },
603
- };
604
- };
605
- // Concept-artikelen uit de Voorraad Module (nog niet in Floriday) ook tonen
606
- const { data: draftRowsAll } = await supabaseAdmin
607
- .from("trade_item_drafts")
608
- .select("id, data, floriday_id")
609
- .range(0, 4999);
610
- const draftMissing = (Array.isArray(draftRowsAll) ? draftRowsAll : []).filter((d) => typeof d?.id === "string" && !known.has(d.id) &&
611
- !(typeof d?.floriday_id === "string" && known.has(d.floriday_id)));
612
- const buildDraftRow = (r) => {
613
- const d = r.data ?? {};
614
- const pk = Array.isArray(d.packingConfigurations) ? d.packingConfigurations[0] ?? {} : {};
615
- return {
616
- id: r.id,
617
- product: nameOf(d),
618
- batch: "",
619
- location: "",
620
- unit: "stuks",
621
- quantity: 0,
622
- plants_per_crate: norm(pk?.unitsPerPackage ?? pk?.piecesPerPackage),
623
- crates_per_layer: norm(pk?.packagesPerLayer),
624
- layers_per_cart: norm(pk?.layersPerLoadCarrier),
625
- article_number: typeof d?.supplierArticleCode === "string" ? d.supplierArticleCode : null,
626
- custom_fields: { draft_id: r.id, source: "trade_item_drafts" },
627
- };
628
- };
629
- const toInsert = [...missing.map(buildRow), ...draftMissing.map(buildDraftRow)];
630
- // Artikelen die (nog) niet in `products` staan, tonen we hoe dan ook —
631
- // ook als materialiseren faalt.
632
- let virtualRows = [];
633
- if (toInsert.length > 0) {
634
- const { error: upsertError } = await supabaseAdmin
635
- .from("products")
636
- .upsert(toInsert, { onConflict: "id" });
637
- if (upsertError) {
638
- console.error("[listProductsForPicker] materialiseren mislukt:", upsertError.message);
639
- virtualRows = toInsert;
640
- }
641
- }
642
- // ── 2. Voorraad verzamelen ──────────────────────────────────────────────
516
+ // ── 1. Voorraad verzamelen ──────────────────────────────────────────────
643
517
  const { data: stockRows } = await supabaseAdmin
644
518
  .from("trade_item_stock")
645
519
  .select("trade_item_key, physical_stock, reserved_stock");
@@ -675,8 +549,6 @@ export const listProductsForPicker = createServerFn({ method: "GET" })
675
549
  .in("id", Array.from(availByDraftId.keys()));
676
550
  for (const d of Array.isArray(drafts) ? drafts : []) {
677
551
  const qty = availByDraftId.get(d.id) ?? 0;
678
- // Concepten worden ook als product gematerialiseerd op hun eigen id
679
- addAvail(availByProductId, d.id, qty);
680
552
  const linked = d?.data?.linkedProductId;
681
553
  if (typeof linked === "string" && linked)
682
554
  addAvail(availByProductId, linked, qty);
@@ -702,19 +574,13 @@ export const listProductsForPicker = createServerFn({ method: "GET" })
702
574
  catch {
703
575
  // view optioneel
704
576
  }
705
- // ── 3. Alle producten teruggeven ────────────────────────────────────────
577
+ // ── 2. Alle bestaande producten teruggeven ─────────────────────────────
706
578
  const { data } = await supabaseAdmin
707
579
  .from("products")
708
580
  .select("id, product, unit, plants_per_crate, crates_per_layer, layers_per_cart, custom_fields, floriday_trade_item_id")
709
581
  .order("product")
710
582
  .range(0, 9999);
711
- const dbRows = Array.isArray(data) ? data : [];
712
- const seen = new Set(dbRows.map((p) => String(p.id)));
713
- const allRows = [
714
- ...dbRows,
715
- ...virtualRows.filter((v) => !seen.has(String(v.id))),
716
- ];
717
- const items = allRows
583
+ const items = (Array.isArray(data) ? data : [])
718
584
  .map((p) => {
719
585
  const fid = p?.custom_fields?.floriday_id ?? p?.floriday_trade_item_id;
720
586
  const byProd = availByProductId.get(p.id) ?? 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/mailbox-orders",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",