@flowselections/floriday-verkoop-module 1.0.54 → 1.0.56
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.
- package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
- package/dist-lib/components/verkoop/OrderKarindelingCard.js +7 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +45 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +45 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +45 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +45 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/cart-lock.functions.d.ts +45 -0
- package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-planning-settings.functions.d.ts +495 -0
- package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.d.ts +137 -26
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.js +93 -63
- package/dist-lib/lib/create-order.functions.d.ts +765 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +135 -0
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +45 -0
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.d.ts +45 -0
- package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-public.functions.d.ts +315 -0
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +225 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.functions.d.ts +180 -0
- package/dist-lib/lib/invoice-timing.functions.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.functions.js +12 -6
- package/dist-lib/lib/load-carriers.functions.d.ts +45 -0
- package/dist-lib/lib/load-carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.functions.d.ts +225 -0
- package/dist-lib/lib/logistics-engine.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.functions.js +10 -5
- package/dist-lib/lib/logistics-stock.functions.d.ts +90 -0
- package/dist-lib/lib/logistics-stock.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-addresses.functions.d.ts +45 -0
- package/dist-lib/lib/order-addresses.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-addresses.functions.js +15 -9
- package/dist-lib/lib/order-buffers.functions.d.ts +135 -0
- package/dist-lib/lib/order-buffers.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-buffers.functions.js +10 -10
- package/dist-lib/lib/order-carrier.functions.d.ts +90 -0
- package/dist-lib/lib/order-carrier.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-carrier.functions.js +20 -11
- package/dist-lib/lib/order-pickup.functions.d.ts +90 -0
- package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-pickup.functions.js +20 -11
- package/dist-lib/lib/order-row.server.d.ts +24 -0
- package/dist-lib/lib/order-row.server.d.ts.map +1 -0
- package/dist-lib/lib/order-row.server.js +58 -0
- package/dist-lib/lib/order-shipments.functions.d.ts +180 -0
- package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-shipments.functions.js +11 -6
- package/dist-lib/lib/product-name.d.ts +3 -0
- package/dist-lib/lib/product-name.d.ts.map +1 -0
- package/dist-lib/lib/product-name.js +43 -0
- package/dist-lib/lib/salesorders-list.functions.d.ts +45 -0
- package/dist-lib/lib/salesorders-list.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts +225 -0
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/stock-consumption-alerts.functions.d.ts +90 -0
- package/dist-lib/lib/stock-consumption-alerts.functions.d.ts.map +1 -1
- package/dist-lib/lib/tradeitems.functions.d.ts +45 -0
- package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { createServerFn } from "@tanstack/react-start";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
9
|
+
import { resolveOrderRow } from "./order-row.server";
|
|
9
10
|
import { CART_PLANNER_VERSION, planCarts, } from "./cart-planner";
|
|
10
11
|
import { resolveBufferForOrderInternal } from "./order-buffers.functions";
|
|
11
12
|
export async function replanBufferInternal(bufferId) {
|
|
@@ -159,18 +160,14 @@ export async function replanScopeInternal(scope) {
|
|
|
159
160
|
for (const it of items ?? []) {
|
|
160
161
|
itemToOrder.set(it.id, it.order_id);
|
|
161
162
|
}
|
|
162
|
-
// Bestaande karren in de scope tellen (voor waarschuwing) en wissen.
|
|
163
163
|
// Nummering én delete-scope liggen altijd op bufferniveau als de order een
|
|
164
|
-
// buffer heeft; order-scope is uitsluitend terugval.
|
|
164
|
+
// buffer heeft; order-scope is uitsluitend terugval. Wissen én wegschrijven
|
|
165
|
+
// gebeuren verderop in ÉÉN transactie via public.replace_order_carts, zodat
|
|
166
|
+
// een mislukte replan de bestaande indeling laat staan.
|
|
165
167
|
if (!bufferId) {
|
|
166
168
|
console.warn("[cart-replan] geen buffer voor order %s — nummering en delete vallen terug op order-scope", soloOrderId);
|
|
167
169
|
}
|
|
168
|
-
|
|
169
|
-
const { data: existingCarts } = await cartScope(supabaseAdmin.from("order_carts").select("id, source"));
|
|
170
|
-
const replacedManualCount = (existingCarts ?? []).filter((c) => c.source === "manual").length;
|
|
171
|
-
const { error: delErr } = await cartScope(supabaseAdmin.from("order_carts").delete());
|
|
172
|
-
if (delErr)
|
|
173
|
-
throw new Error(delErr.message);
|
|
170
|
+
let replacedManualCount = 0;
|
|
174
171
|
// De karindeling wordt hier altijd zelf gerekend. custom_fields.floriday_packing
|
|
175
172
|
// levert alleen de belading-VELDEN (pieces_per_package / packages_per_layer /
|
|
176
173
|
// layers_per_load_carrier) — de mirror schrijft nooit een voorgekauwde
|
|
@@ -277,8 +274,10 @@ export async function replanScopeInternal(scope) {
|
|
|
277
274
|
}
|
|
278
275
|
}
|
|
279
276
|
}
|
|
280
|
-
|
|
281
|
-
|
|
277
|
+
// Payload voor public.replace_order_carts. LET OP: de sleutels hieronder
|
|
278
|
+
// moeten gelijk lopen met de sleutellijsten in die SQL-functie; ontbreekt er
|
|
279
|
+
// één, dan gooit de database een fout in plaats van stil NULL te schrijven.
|
|
280
|
+
const cartPayload = plannedCarts.map(({ cart, profile }) => {
|
|
282
281
|
// Bepaal cart.order_id: null bij mix van orders, anders die ene order.
|
|
283
282
|
const orderIdsOnCart = new Set();
|
|
284
283
|
for (const i of cart.items) {
|
|
@@ -287,9 +286,7 @@ export async function replanScopeInternal(scope) {
|
|
|
287
286
|
orderIdsOnCart.add(oid);
|
|
288
287
|
}
|
|
289
288
|
const singleOrderId = orderIdsOnCart.size === 1 ? [...orderIdsOnCart][0] : null;
|
|
290
|
-
|
|
291
|
-
.from("order_carts")
|
|
292
|
-
.insert({
|
|
289
|
+
return {
|
|
293
290
|
order_id: singleOrderId,
|
|
294
291
|
buffer_id: bufferId,
|
|
295
292
|
cart_number: cart.cart_number,
|
|
@@ -303,14 +300,7 @@ export async function replanScopeInternal(scope) {
|
|
|
303
300
|
carrier_type_id: profile.id,
|
|
304
301
|
max_layers: profile.layers_per_carrier ?? null,
|
|
305
302
|
algorithm_version: CART_PLANNER_VERSION,
|
|
306
|
-
|
|
307
|
-
.select("id")
|
|
308
|
-
.single();
|
|
309
|
-
if (cErr)
|
|
310
|
-
throw new Error(cErr.message);
|
|
311
|
-
if (cart.items.length > 0) {
|
|
312
|
-
const rows = cart.items.map((i) => ({
|
|
313
|
-
cart_id: inserted.id,
|
|
303
|
+
items: cart.items.map((i) => ({
|
|
314
304
|
order_item_id: i.order_item_id,
|
|
315
305
|
product_id: i.product_id,
|
|
316
306
|
product_name: i.product_name,
|
|
@@ -320,14 +310,28 @@ export async function replanScopeInternal(scope) {
|
|
|
320
310
|
layers_per_cart: i.layers_per_cart,
|
|
321
311
|
layer_fraction: i.layer_fraction,
|
|
322
312
|
layer_share_pct: i.layer_share_pct,
|
|
323
|
-
}))
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
313
|
+
})),
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
// Delete + inserts in één transactie: faalt er iets, dan blijft de oude
|
|
317
|
+
// karindeling staan in plaats van een lege buffer achter te laten.
|
|
318
|
+
const { data: replaceRes, error: replaceErr } = await supabaseAdmin.rpc("replace_order_carts", {
|
|
319
|
+
p_buffer_id: bufferId,
|
|
320
|
+
p_order_id: bufferId ? null : soloOrderId,
|
|
321
|
+
p_carts: cartPayload,
|
|
322
|
+
});
|
|
323
|
+
if (replaceErr)
|
|
324
|
+
throw new Error(replaceErr.message);
|
|
325
|
+
const cartsCreated = Number(replaceRes?.carts_written ?? 0);
|
|
326
|
+
const itemsWritten = Number(replaceRes?.items_written ?? 0);
|
|
327
|
+
replacedManualCount = Number(replaceRes?.replaced_manual_count ?? 0);
|
|
328
|
+
// Controleer dat de database daadwerkelijk heeft weggeschreven wat de
|
|
329
|
+
// planner opleverde. Nul karren mag, maar alleen als de planner er ook nul
|
|
330
|
+
// opleverde — niet als gevolg van een fout.
|
|
331
|
+
const expectedItems = cartPayload.reduce((n, c) => n + c.items.length, 0);
|
|
332
|
+
if (cartsCreated !== cartPayload.length || itemsWritten !== expectedItems) {
|
|
333
|
+
throw new Error(`Karindeling niet volledig weggeschreven: ${cartsCreated}/${cartPayload.length} karren, ` +
|
|
334
|
+
`${itemsWritten}/${expectedItems} artikelen.`);
|
|
331
335
|
}
|
|
332
336
|
return {
|
|
333
337
|
ok: true,
|
|
@@ -360,8 +364,12 @@ export async function replanOrderCartsFromCron(orderId) {
|
|
|
360
364
|
}
|
|
361
365
|
export const replanOrderCarts = createServerFn({ method: "POST" })
|
|
362
366
|
.middleware([requireSupabaseAuth])
|
|
363
|
-
.inputValidator((input) => z.object({ orderId: z.string().
|
|
364
|
-
.handler(async ({ data }) =>
|
|
367
|
+
.inputValidator((input) => z.object({ orderId: z.string().min(1) }).parse(input))
|
|
368
|
+
.handler(async ({ data, context }) => {
|
|
369
|
+
// DTO-id kan een Floriday salesOrderId zijn.
|
|
370
|
+
const order = await resolveOrderRow(context.supabase, data.orderId);
|
|
371
|
+
return replanOrderInternal(order.id);
|
|
372
|
+
});
|
|
365
373
|
// ─── Queue drain (aangeroepen door pg_cron elke minuut) ─────────────────────
|
|
366
374
|
export async function drainCartReplanQueue(limit = 50) {
|
|
367
375
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
@@ -406,65 +414,79 @@ export async function drainCartReplanQueue(limit = 50) {
|
|
|
406
414
|
// logistics_plan_queue). De UI berekent NIETS zelf: hij toont wat er staat.
|
|
407
415
|
export const listOrderCarts = createServerFn({ method: "GET" })
|
|
408
416
|
.middleware([requireSupabaseAuth])
|
|
409
|
-
.inputValidator((input) => z.object({ orderId: z.string().
|
|
417
|
+
.inputValidator((input) => z.object({ orderId: z.string().min(1) }).parse(input))
|
|
410
418
|
.handler(async ({ data, context }) => {
|
|
411
|
-
|
|
419
|
+
// Elke query faalt HARD. "Geen karren" en "kon niet laden" zijn twee
|
|
420
|
+
// verschillende dingen; stilzwijgend een lege array teruggeven verbergt
|
|
421
|
+
// fouten (RLS, kolomnaam, embed) als "nog geen karindeling".
|
|
422
|
+
const fail = (what, error) => {
|
|
423
|
+
throw new Error(`Karindeling laden mislukt (${what}): ${error?.message ?? "onbekende fout"}`);
|
|
424
|
+
};
|
|
425
|
+
// De DTO-id is voor Floriday-orders het salesOrderId, niet orders.id.
|
|
426
|
+
// resolveOrderRow doet de twee-staps-lookup + botsingswaarschuwing.
|
|
427
|
+
const orderRow = await resolveOrderRow(context.supabase, data.orderId);
|
|
428
|
+
const dbOrderId = orderRow.id;
|
|
429
|
+
const bufferId = orderRow.buffer_id;
|
|
430
|
+
const lockRes = await context.supabase
|
|
412
431
|
.from("orders")
|
|
413
|
-
.select("
|
|
414
|
-
.eq("id",
|
|
432
|
+
.select("plan_locked_at")
|
|
433
|
+
.eq("id", dbOrderId)
|
|
415
434
|
.maybeSingle();
|
|
416
|
-
|
|
417
|
-
|
|
435
|
+
if (lockRes.error)
|
|
436
|
+
fail("plan-lock lezen", lockRes.error);
|
|
437
|
+
const planLockedAt = lockRes.data?.plan_locked_at ?? null;
|
|
418
438
|
const select = "id, order_id, buffer_id, cart_number, source, is_complete, occupancy_pct, total_layers_used, algorithm_version, carrier_type, max_layers, notes, order_cart_items(id, order_item_id, product_id, product_name, crates, plants_per_crate, crates_per_layer, layers_per_cart, layer_fraction, layer_share_pct)";
|
|
419
439
|
const base = context.supabase.from("order_carts").select(select);
|
|
420
|
-
const
|
|
440
|
+
const cartsRes = await (bufferId
|
|
421
441
|
? base.eq("buffer_id", bufferId)
|
|
422
|
-
: base.eq("order_id",
|
|
442
|
+
: base.eq("order_id", dbOrderId)).order("cart_number");
|
|
443
|
+
if (cartsRes.error)
|
|
444
|
+
fail("karren lezen", cartsRes.error);
|
|
445
|
+
const carts = (cartsRes.data ?? []);
|
|
423
446
|
// Orders in de scope + ordernummer per orderregel (voor de badges).
|
|
424
447
|
let orders = [];
|
|
425
448
|
if (bufferId) {
|
|
426
|
-
const
|
|
449
|
+
const sibs = await context.supabase
|
|
427
450
|
.from("orders")
|
|
428
451
|
.select("id, order_number")
|
|
429
452
|
.eq("buffer_id", bufferId)
|
|
430
453
|
.is("deleted_at", null);
|
|
431
|
-
|
|
454
|
+
if (sibs.error)
|
|
455
|
+
fail("orders in buffer lezen", sibs.error);
|
|
456
|
+
orders = (sibs.data ?? []).map((o) => ({
|
|
432
457
|
id: o.id,
|
|
433
458
|
order_number: o.order_number ?? null,
|
|
434
459
|
}));
|
|
435
460
|
}
|
|
436
461
|
if (orders.length === 0) {
|
|
437
|
-
|
|
438
|
-
.from("orders")
|
|
439
|
-
.select("id, order_number")
|
|
440
|
-
.eq("id", data.orderId)
|
|
441
|
-
.maybeSingle();
|
|
442
|
-
if (own) {
|
|
443
|
-
orders = [{ id: own.id, order_number: own.order_number ?? null }];
|
|
444
|
-
}
|
|
462
|
+
orders = [{ id: dbOrderId, order_number: orderRow.order_number ?? null }];
|
|
445
463
|
}
|
|
446
464
|
const orderIds = orders.map((o) => o.id);
|
|
447
465
|
let itemOrders = [];
|
|
448
466
|
if (orderIds.length > 0) {
|
|
449
|
-
const
|
|
467
|
+
const its = await context.supabase
|
|
450
468
|
.from("order_items")
|
|
451
469
|
.select("id, order_id")
|
|
452
470
|
.in("order_id", orderIds);
|
|
453
|
-
|
|
471
|
+
if (its.error)
|
|
472
|
+
fail("orderregels lezen", its.error);
|
|
473
|
+
itemOrders = (its.data ?? []).map((i) => ({
|
|
454
474
|
id: i.id,
|
|
455
475
|
order_id: i.order_id,
|
|
456
476
|
}));
|
|
457
477
|
}
|
|
458
478
|
// Blokkadereden alleen ophalen als er niets gepland staat.
|
|
459
479
|
let blocked = null;
|
|
460
|
-
if (
|
|
461
|
-
const
|
|
480
|
+
if (carts.length === 0 && orderIds.length > 0) {
|
|
481
|
+
const dec = await context.supabase
|
|
462
482
|
.from("logistics_decisions")
|
|
463
483
|
.select("subject_id, slot, explanation, result, created_at")
|
|
464
484
|
.in("subject_id", orderIds)
|
|
465
485
|
.order("created_at", { ascending: false })
|
|
466
486
|
.limit(1);
|
|
467
|
-
|
|
487
|
+
if (dec.error)
|
|
488
|
+
fail("blokkadereden lezen", dec.error);
|
|
489
|
+
const d = (dec.data ?? [])[0];
|
|
468
490
|
if (d) {
|
|
469
491
|
blocked = {
|
|
470
492
|
reason: d.result?.reason ?? d.slot ?? null,
|
|
@@ -473,28 +495,36 @@ export const listOrderCarts = createServerFn({ method: "GET" })
|
|
|
473
495
|
};
|
|
474
496
|
}
|
|
475
497
|
if (!blocked) {
|
|
476
|
-
const
|
|
498
|
+
const q = await context.supabase
|
|
477
499
|
.from("logistics_plan_queue")
|
|
478
500
|
.select("order_id, status, reason, error, updated_at")
|
|
479
501
|
.in("order_id", orderIds)
|
|
480
502
|
.order("updated_at", { ascending: false })
|
|
481
503
|
.limit(1);
|
|
482
|
-
|
|
504
|
+
if (q.error)
|
|
505
|
+
fail("planqueue lezen", q.error);
|
|
506
|
+
const r = (q.data ?? [])[0];
|
|
483
507
|
if (r && (r.error || r.reason)) {
|
|
508
|
+
// `reason` in logistics_plan_queue is vaak een sleutel/veldnaam
|
|
509
|
+
// ("items"), geen leesbare reden. De echte fout staat in `error`.
|
|
510
|
+
const err = r.error ?? null;
|
|
511
|
+
const rawReason = r.reason ?? null;
|
|
484
512
|
blocked = {
|
|
485
|
-
reason:
|
|
486
|
-
explanation:
|
|
513
|
+
reason: rawReason,
|
|
514
|
+
explanation: err ?? (rawReason ? `Planning geblokkeerd (${rawReason})` : null),
|
|
487
515
|
at: r.updated_at ?? null,
|
|
488
516
|
};
|
|
489
517
|
}
|
|
490
518
|
}
|
|
491
519
|
}
|
|
492
|
-
return { carts
|
|
520
|
+
return { carts, bufferId, orderId: dbOrderId, planLockedAt, orders, itemOrders, blocked };
|
|
493
521
|
});
|
|
494
522
|
export const replanBufferForOrder = createServerFn({ method: "POST" })
|
|
495
523
|
.middleware([requireSupabaseAuth])
|
|
496
|
-
.inputValidator((input) => z.object({ orderId: z.string().
|
|
497
|
-
.handler(async ({ data }) => {
|
|
498
|
-
|
|
499
|
-
|
|
524
|
+
.inputValidator((input) => z.object({ orderId: z.string().min(1) }).parse(input))
|
|
525
|
+
.handler(async ({ data, context }) => {
|
|
526
|
+
// DTO-id kan een Floriday salesOrderId zijn.
|
|
527
|
+
const order = await resolveOrderRow(context.supabase, data.orderId);
|
|
528
|
+
const bufferId = await resolveBufferForOrderInternal(order.id);
|
|
529
|
+
return replanScopeInternal({ bufferId, orderId: order.id });
|
|
500
530
|
});
|