@flowselections/floriday-verkoop-module 1.0.73 → 1.0.76
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 +36 -114
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts +6 -0
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts.map +1 -0
- package/dist-lib/components/verkoop/PlanCoverageBadge.js +23 -0
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +2 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +2 -1
- package/dist-lib/integrations/supabase/types.d.ts +607 -3
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.d.ts +2 -22
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.js +10 -163
- package/dist-lib/lib/create-order.functions.d.ts +0 -57
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +1 -205
- package/dist-lib/lib/logistics-engine.server.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.server.js +8 -3
- package/dist-lib/lib/plan-coverage.d.ts +56 -0
- package/dist-lib/lib/plan-coverage.d.ts.map +1 -0
- package/dist-lib/lib/plan-coverage.js +120 -0
- package/dist-lib/lib/plan-coverage.server.d.ts +3 -0
- package/dist-lib/lib/plan-coverage.server.d.ts.map +1 -0
- package/dist-lib/lib/plan-coverage.server.js +97 -0
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +43 -82
- package/dist-lib/lib/salesorders-mapping.d.ts +5 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +21 -18
- package/package.json +1 -1
|
@@ -70,35 +70,15 @@ export declare function drainCartReplanQueue(_limit?: number): Promise<{
|
|
|
70
70
|
error?: string;
|
|
71
71
|
}>;
|
|
72
72
|
}>;
|
|
73
|
-
export declare const
|
|
73
|
+
export declare const getOrderPlanCoverage: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
74
74
|
supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
|
|
75
75
|
userId: string;
|
|
76
76
|
claims: import("@supabase/auth-js").User;
|
|
77
77
|
}, undefined, undefined, undefined>], (input: unknown) => {
|
|
78
78
|
orderId: string;
|
|
79
79
|
}, Promise<{
|
|
80
|
-
carts: any[];
|
|
81
|
-
bufferId: string | null;
|
|
82
80
|
orderId: string;
|
|
83
|
-
|
|
84
|
-
orders: {
|
|
85
|
-
id: string;
|
|
86
|
-
order_number: string | null;
|
|
87
|
-
}[];
|
|
88
|
-
itemOrders: {
|
|
89
|
-
id: string;
|
|
90
|
-
order_id: string;
|
|
91
|
-
}[];
|
|
92
|
-
blocked: {
|
|
93
|
-
reason: string | null;
|
|
94
|
-
explanation: string | null;
|
|
95
|
-
at: string | null;
|
|
96
|
-
} | null;
|
|
97
|
-
planState: {
|
|
98
|
-
status: string;
|
|
99
|
-
reason: string | null;
|
|
100
|
-
at: string | null;
|
|
101
|
-
} | null;
|
|
81
|
+
coverage: import("./plan-coverage").PlanCoverage;
|
|
102
82
|
}>>;
|
|
103
83
|
export declare const replanBufferForOrder: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
104
84
|
supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart-replan.functions.d.ts","sourceRoot":"","sources":["../../src/lib/cart-replan.functions.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,eAAe,GAAG,sBAAsB,CAAC;IACjD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM;QAYrD,OAAO;cACD,MAAM,GAAG,IAAI;cACb,MAAM,EAAE;kBACJ,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GAlBnB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GAAG,OAAO,CAAC;IACV,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB,CAAC,CAoaD;AA4BD,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM;QAzBxD,OAAO;aACF,MAAM;kBACD,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GAoBnB;AAED,eAAO,MAAM,gBAAgB;;;;8CAEH,OAAO;;;QA/B3B,OAAO;aACF,MAAM;kBACD,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GA6BhB,CAAC;AAML,wBAAsB,oBAAoB,CAAC,MAAM,SAAK,GAAG,OAAO,CAAC;IAC/D,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE,CAAC,CAED;
|
|
1
|
+
{"version":3,"file":"cart-replan.functions.d.ts","sourceRoot":"","sources":["../../src/lib/cart-replan.functions.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,eAAe,GAAG,sBAAsB,CAAC;IACjD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM;QAYrD,OAAO;cACD,MAAM,GAAG,IAAI;cACb,MAAM,EAAE;kBACJ,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GAlBnB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GAAG,OAAO,CAAC;IACV,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB,CAAC,CAoaD;AA4BD,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM;QAzBxD,OAAO;aACF,MAAM;kBACD,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GAoBnB;AAED,eAAO,MAAM,gBAAgB;;;;8CAEH,OAAO;;;QA/B3B,OAAO;aACF,MAAM;kBACD,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GA6BhB,CAAC;AAML,wBAAsB,oBAAoB,CAAC,MAAM,SAAK,GAAG,OAAO,CAAC;IAC/D,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE,CAAC,CAED;AAMD,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;GAU7B,CAAC;AAEL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;QAvf3B,OAAO;cACD,MAAM,GAAG,IAAI;cACb,MAAM,EAAE;kBACJ,MAAM;yBACC,MAAM;yBACN,OAAO;cAClB,MAAM,EAAE;kBACJ,WAAW,EAAE;cACjB,QAAQ;GAqfhB,CAAC"}
|
|
@@ -399,173 +399,20 @@ export async function drainCartReplanQueue(_limit = 50) {
|
|
|
399
399
|
return { ok: true, processed: 0, failed: 0, results: [] };
|
|
400
400
|
}
|
|
401
401
|
// ─── Lezen — voor de UI ─────────────────────────────────────────────────────
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
// logistics_plan_queue). De UI berekent NIETS zelf: hij toont wat er staat.
|
|
407
|
-
export const listOrderCarts = createServerFn({ method: "GET" })
|
|
402
|
+
// De karindeling van een order komt uitsluitend uit de Logistiek Engine
|
|
403
|
+
// (logistics_order_plan_coverage). Deze module leest order_carts/
|
|
404
|
+
// order_cart_items niet zelf. Fail closed: bij een fout "Indeling onbekend".
|
|
405
|
+
export const getOrderPlanCoverage = createServerFn({ method: "GET" })
|
|
408
406
|
.middleware([requireSupabaseAuth])
|
|
409
407
|
.inputValidator((input) => z.object({ orderId: z.string().min(1) }).parse(input))
|
|
410
408
|
.handler(async ({ data, context }) => {
|
|
411
|
-
// Elke query faalt HARD. "Geen karren" en "kon niet laden" zijn twee
|
|
412
|
-
// verschillende dingen; stilzwijgend een lege array teruggeven verbergt
|
|
413
|
-
// fouten (RLS, kolomnaam, embed) als "nog geen karindeling".
|
|
414
|
-
const fail = (what, error) => {
|
|
415
|
-
throw new Error(`Karindeling laden mislukt (${what}): ${error?.message ?? "onbekende fout"}`);
|
|
416
|
-
};
|
|
417
|
-
// De DTO-id is voor Floriday-orders het salesOrderId, niet orders.id.
|
|
418
|
-
// resolveOrderRow doet de twee-staps-lookup + botsingswaarschuwing.
|
|
419
409
|
const orderRow = await resolveOrderRow(context.supabase, data.orderId);
|
|
420
|
-
const dbOrderId = orderRow.id;
|
|
421
|
-
const
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
.maybeSingle();
|
|
427
|
-
if (lockRes.error)
|
|
428
|
-
fail("plan-lock lezen", lockRes.error);
|
|
429
|
-
const planLockedAt = lockRes.data?.plan_locked_at ?? null;
|
|
430
|
-
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)";
|
|
431
|
-
const readCarts = async () => {
|
|
432
|
-
const base = context.supabase.from("order_carts").select(select);
|
|
433
|
-
const res = await (bufferId
|
|
434
|
-
? base.eq("buffer_id", bufferId)
|
|
435
|
-
: base.eq("order_id", dbOrderId)).order("cart_number");
|
|
436
|
-
if (res.error)
|
|
437
|
-
fail("karren lezen", res.error);
|
|
438
|
-
return (res.data ?? []);
|
|
439
|
-
};
|
|
440
|
-
let carts = await readCarts();
|
|
441
|
-
// Terugval via de karregels: gedeelde karren hebben order_id = NULL.
|
|
442
|
-
if (carts.length === 0) {
|
|
443
|
-
const { loadCartsByOrder } = await import("./order-cart-count.server");
|
|
444
|
-
const ids = (await loadCartsByOrder(context.supabase, [dbOrderId])).get(dbOrderId)?.cartIds ?? [];
|
|
445
|
-
if (ids.length) {
|
|
446
|
-
const res = await context.supabase.from("order_carts").select(select).in("id", ids).order("cart_number");
|
|
447
|
-
if (res.error)
|
|
448
|
-
fail("karren lezen", res.error);
|
|
449
|
-
carts = (res.data ?? []);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
// Lezen blijft lezen: is er nog geen indeling, dan wordt die door de
|
|
453
|
-
// Logistiek Engine gemaakt in haar eigen minuut-ronde.
|
|
454
|
-
const autoPlanError = null;
|
|
455
|
-
// Orders in de scope + ordernummer per orderregel (voor de badges).
|
|
456
|
-
let orders = [];
|
|
457
|
-
if (bufferId) {
|
|
458
|
-
const sibs = await context.supabase
|
|
459
|
-
.from("orders")
|
|
460
|
-
.select("id, order_number")
|
|
461
|
-
.eq("buffer_id", bufferId)
|
|
462
|
-
.is("deleted_at", null);
|
|
463
|
-
if (sibs.error)
|
|
464
|
-
fail("orders in buffer lezen", sibs.error);
|
|
465
|
-
orders = (sibs.data ?? []).map((o) => ({
|
|
466
|
-
id: o.id,
|
|
467
|
-
order_number: o.order_number ?? null,
|
|
468
|
-
}));
|
|
469
|
-
}
|
|
470
|
-
if (orders.length === 0) {
|
|
471
|
-
orders = [{ id: dbOrderId, order_number: orderRow.order_number ?? null }];
|
|
472
|
-
}
|
|
473
|
-
const orderIds = orders.map((o) => o.id);
|
|
474
|
-
let itemOrders = [];
|
|
475
|
-
if (orderIds.length > 0) {
|
|
476
|
-
const its = await context.supabase
|
|
477
|
-
.from("order_items")
|
|
478
|
-
.select("id, order_id")
|
|
479
|
-
.in("order_id", orderIds)
|
|
480
|
-
.is("quarantined_at", null);
|
|
481
|
-
if (its.error)
|
|
482
|
-
fail("orderregels lezen", its.error);
|
|
483
|
-
itemOrders = (its.data ?? []).map((i) => ({
|
|
484
|
-
id: i.id,
|
|
485
|
-
order_id: i.order_id,
|
|
486
|
-
}));
|
|
487
|
-
}
|
|
488
|
-
// Blokkadereden alleen ophalen als er — ook na het direct plannen — niets staat.
|
|
489
|
-
let blocked = null;
|
|
490
|
-
if (carts.length === 0 && autoPlanError) {
|
|
491
|
-
blocked = {
|
|
492
|
-
reason: "planningsfout",
|
|
493
|
-
explanation: `De karindeling kon niet worden berekend: ${autoPlanError}`,
|
|
494
|
-
at: new Date().toISOString(),
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (!blocked && carts.length === 0 && orderIds.length > 0) {
|
|
498
|
-
const dec = await context.supabase
|
|
499
|
-
.from("logistics_decisions")
|
|
500
|
-
.select("subject_id, slot, explanation, result, created_at")
|
|
501
|
-
.in("subject_id", orderIds)
|
|
502
|
-
.order("created_at", { ascending: false })
|
|
503
|
-
.limit(1);
|
|
504
|
-
if (dec.error)
|
|
505
|
-
fail("blokkadereden lezen", dec.error);
|
|
506
|
-
const d = (dec.data ?? [])[0];
|
|
507
|
-
if (d) {
|
|
508
|
-
blocked = {
|
|
509
|
-
reason: d.result?.reason ?? d.slot ?? null,
|
|
510
|
-
explanation: d.explanation ?? null,
|
|
511
|
-
at: d.created_at ?? null,
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
if (!blocked) {
|
|
515
|
-
const q = await context.supabase
|
|
516
|
-
.from("logistics_plan_queue")
|
|
517
|
-
.select("order_id, status, reason, error, updated_at")
|
|
518
|
-
.in("order_id", orderIds)
|
|
519
|
-
.order("updated_at", { ascending: false })
|
|
520
|
-
.limit(1);
|
|
521
|
-
if (q.error)
|
|
522
|
-
fail("planqueue lezen", q.error);
|
|
523
|
-
const r = (q.data ?? [])[0];
|
|
524
|
-
// `reason` is de AANLEIDING van de herplanning ("items", "UPDATE"),
|
|
525
|
-
// geen blokkade. Alleen een echte fout is nog een blokkade: een
|
|
526
|
-
// wachtende wachtrijregel hebben we hierboven zelf al ingehaald.
|
|
527
|
-
const status = (r?.status ?? "").toLowerCase();
|
|
528
|
-
const err = r?.error ?? null;
|
|
529
|
-
if (r && (err || status === "failed" || status === "error")) {
|
|
530
|
-
blocked = {
|
|
531
|
-
reason: err ? "planningsfout" : status,
|
|
532
|
-
explanation: err ?? "De karrenplanning is mislukt. Probeer opnieuw te plannen.",
|
|
533
|
-
at: r.updated_at ?? null,
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
// Werkelijke stand bij de planwachtrij: zo ziet de gebruiker of de order
|
|
539
|
-
// is aangemeld en nog wacht, of dat er niets klaarstaat.
|
|
540
|
-
let planState = null;
|
|
541
|
-
if (carts.length === 0 && orderIds.length > 0) {
|
|
542
|
-
const ps = await context.supabase
|
|
543
|
-
.from("logistics_plan_queue")
|
|
544
|
-
.select("status, reason, updated_at")
|
|
545
|
-
.in("order_id", orderIds)
|
|
546
|
-
.order("updated_at", { ascending: false })
|
|
547
|
-
.limit(1);
|
|
548
|
-
if (ps.error)
|
|
549
|
-
fail("planstand lezen", ps.error);
|
|
550
|
-
const row = (ps.data ?? [])[0];
|
|
551
|
-
if (row) {
|
|
552
|
-
planState = {
|
|
553
|
-
status: String(row.status ?? ""),
|
|
554
|
-
reason: row.reason ?? null,
|
|
555
|
-
at: row.updated_at ?? null,
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
return {
|
|
560
|
-
carts,
|
|
561
|
-
bufferId,
|
|
562
|
-
orderId: dbOrderId,
|
|
563
|
-
planLockedAt,
|
|
564
|
-
orders,
|
|
565
|
-
itemOrders,
|
|
566
|
-
blocked,
|
|
567
|
-
planState,
|
|
568
|
-
};
|
|
410
|
+
const dbOrderId = String(orderRow.id);
|
|
411
|
+
const { loadPlanCoverage } = await import("./plan-coverage.server");
|
|
412
|
+
const { unknownCoverage } = await import("./plan-coverage");
|
|
413
|
+
const cov = (await loadPlanCoverage(context.supabase, [dbOrderId])).get(dbOrderId) ??
|
|
414
|
+
unknownCoverage("Geen antwoord van de Engine");
|
|
415
|
+
return { orderId: dbOrderId, coverage: cov };
|
|
569
416
|
});
|
|
570
417
|
export const replanBufferForOrder = createServerFn({ method: "POST" })
|
|
571
418
|
.middleware([requireSupabaseAuth])
|
|
@@ -186,63 +186,6 @@ export declare const deleteMailboxProposal: import("@tanstack/start-client-core"
|
|
|
186
186
|
}, Promise<{
|
|
187
187
|
ok: true;
|
|
188
188
|
}>>;
|
|
189
|
-
export declare const planAndSaveAutoCarts: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
190
|
-
supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
|
|
191
|
-
userId: string;
|
|
192
|
-
claims: import("@supabase/auth-js").User;
|
|
193
|
-
}, undefined, undefined, undefined>], (input: unknown) => {
|
|
194
|
-
orderId: string;
|
|
195
|
-
}, Promise<{
|
|
196
|
-
ok: true;
|
|
197
|
-
autoCarts: {
|
|
198
|
-
cart_number: number;
|
|
199
|
-
product_name: string;
|
|
200
|
-
crates: number;
|
|
201
|
-
plants: number;
|
|
202
|
-
capacity: number;
|
|
203
|
-
}[];
|
|
204
|
-
remainders: {
|
|
205
|
-
crates_remaining: number;
|
|
206
|
-
order_item_id: string;
|
|
207
|
-
product_id: string | null;
|
|
208
|
-
product_name: string;
|
|
209
|
-
plants_per_crate: number;
|
|
210
|
-
crates_per_layer: number;
|
|
211
|
-
layers_per_cart: number;
|
|
212
|
-
}[];
|
|
213
|
-
existingManualCarts: {
|
|
214
|
-
cart_number: number;
|
|
215
|
-
notes: string | null;
|
|
216
|
-
items: any;
|
|
217
|
-
}[];
|
|
218
|
-
nextCartNumber: number;
|
|
219
|
-
blockedLines: {
|
|
220
|
-
order_item_id: string;
|
|
221
|
-
product_name: string;
|
|
222
|
-
missing: string[];
|
|
223
|
-
}[];
|
|
224
|
-
}>>;
|
|
225
|
-
export declare const saveManualCarts: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
226
|
-
supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
|
|
227
|
-
userId: string;
|
|
228
|
-
claims: import("@supabase/auth-js").User;
|
|
229
|
-
}, undefined, undefined, undefined>], (input: unknown) => {
|
|
230
|
-
orderId: string;
|
|
231
|
-
carts: {
|
|
232
|
-
items: {
|
|
233
|
-
order_item_id: string | null;
|
|
234
|
-
product_id: string | null;
|
|
235
|
-
product_name: string;
|
|
236
|
-
crates: number;
|
|
237
|
-
plants_per_crate: number;
|
|
238
|
-
crates_per_layer: number;
|
|
239
|
-
}[];
|
|
240
|
-
is_complete?: boolean | undefined;
|
|
241
|
-
notes?: string | null | undefined;
|
|
242
|
-
}[];
|
|
243
|
-
}, Promise<{
|
|
244
|
-
ok: true;
|
|
245
|
-
}>>;
|
|
246
189
|
export declare const getLocalOrderForEdit: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
247
190
|
supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
|
|
248
191
|
userId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-order.functions.d.ts","sourceRoot":"","sources":["../../src/lib/create-order.functions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create-order.functions.d.ts","sourceRoot":"","sources":["../../src/lib/create-order.functions.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,2BAA2B;;;;;;GA+CpC,CAAC;AAIL,eAAO,MAAM,2BAA2B;;;;0CAElB,OAAO;;;;GAoDzB,CAAC;AAWL,eAAO,MAAM,0BAA0B;;;;;;YA+D7B,MAAM;iBACD,MAAM;sBACD,MAAM,GAAG,IAAI;cACrB,MAAM;oBACA,MAAM,GAAG,IAAI;0BACP,MAAM;0BACN,MAAM;yBACP,MAAM;wBACP,MAAM;wBACN,MAAM;yBACL,MAAM;kBACb,OAAO;gBACT,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS;;GA6RpD,CAAC;AAGL,eAAO,MAAM,kCAAkC;;;;0CAEzB,OAAO;;;;;;;;;;;;;;;;;;;;;;GAkDzB,CAAC;AAKL,eAAO,MAAM,qBAAqB;;;;;;YAoBI,MAAM;;;GAIxC,CAAC;AAGL,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;GAWnC,CAAC;AASL,eAAO,MAAM,8BAA8B;;;;8CAEjB,OAAO;;;;GAU7B,CAAC;AASL,eAAO,MAAM,mBAAmB;;;;;;;;0BAgBuB,MAAM;0BAAoB,MAAM;yBAAmB,MAAM;;;GA8C5G,CAAC;AAGL,eAAO,MAAM,kBAAkB;;;;;;GAU3B,CAAC;AA2CL,eAAO,MAAM,iBAAiB;;;;8CAEJ,OAAO;;;;;;;;;;;;;;;;;;;;;;;aA+OY,MAAM;GAE/C,CAAC;AAKL,eAAO,MAAM,gBAAgB;;;;8CAEH,OAAO;;;;;;;GAY7B,CAAC;AAQL,eAAO,MAAM,gBAAgB;;;;8CAEH,OAAO;;;;;;GAoG7B,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,qBAAqB;;;;8CAER,OAAO;;;;GAyB7B,CAAC;AAyCL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;aAgKG,MAAM;iBACQ,MAAM,GAAG,IAAI;kBACX,MAAM;;YAEnB,MAAM,GAAG,IAAI;;;;YAkB7B,MAAM;mBACS,MAAM,GAAG,IAAI;qBACT,MAAM;;cAErB,MAAM,GAAG,IAAI;;;;;;GAQpC,CAAC;AAIL,eAAO,MAAM,gBAAgB;;;;8CAEH,OAAO;;;;;;;;;;;;;;;;;;;;GAoN7B,CAAC"}
|
|
@@ -5,7 +5,6 @@ import { createServerFn } from "@tanstack/react-start";
|
|
|
5
5
|
import { productNameForStorage } from "./product-name";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
8
|
-
import { CART_PLANNER_VERSION } from "./cart-planner";
|
|
9
8
|
// ─── Klanten ────────────────────────────────────────────────────────────────
|
|
10
9
|
// Bron 1: lokale `customers` (incl. eerder geïmporteerde Twinfield-klanten).
|
|
11
10
|
// Bron 2: `twinfield_customers_cache` — klanten die alleen in Twinfield staan.
|
|
@@ -727,7 +726,7 @@ export const createManualOrder = createServerFn({ method: "POST" })
|
|
|
727
726
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
728
727
|
// Zorg dat elk product_id (= identiteit uit de voorraadmodule) een rij
|
|
729
728
|
// in `products` heeft — de order_items FK vereist dit én de karindeling
|
|
730
|
-
//
|
|
729
|
+
// van de Engine joined op products voor de packing-waarden.
|
|
731
730
|
//
|
|
732
731
|
// BELANGRIJK: de wizard geeft als product_id een identiteit terug die uit
|
|
733
732
|
// 1 van 4 bronnen komt: floriday_trade_items_cache.floriday_id,
|
|
@@ -1035,209 +1034,6 @@ export const deleteMailboxProposal = createServerFn({ method: "POST" })
|
|
|
1035
1034
|
throw new Error(`Voorstel verwijderen faalde: ${dErr.message}`);
|
|
1036
1035
|
return { ok: true };
|
|
1037
1036
|
});
|
|
1038
|
-
export const planAndSaveAutoCarts = createServerFn({ method: "POST" })
|
|
1039
|
-
.middleware([requireSupabaseAuth])
|
|
1040
|
-
.inputValidator((input) => z.object({ orderId: z.string().uuid() }).parse(input))
|
|
1041
|
-
.handler(async ({ data, context }) => {
|
|
1042
|
-
const { data: items, error: itErr } = await context.supabase
|
|
1043
|
-
.from("order_items")
|
|
1044
|
-
.select("id, product_id, product_name, quantity, products(plants_per_crate, crates_per_layer, layers_per_cart)")
|
|
1045
|
-
.eq("order_id", data.orderId)
|
|
1046
|
-
.is("quarantined_at", null);
|
|
1047
|
-
if (itErr)
|
|
1048
|
-
throw new Error(itErr.message);
|
|
1049
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
1050
|
-
await supabaseAdmin.from("order_carts").delete().eq("order_id", data.orderId).eq("is_auto", true);
|
|
1051
|
-
let cartNumber = 1;
|
|
1052
|
-
const { data: existingMax } = await supabaseAdmin
|
|
1053
|
-
.from("order_carts").select("cart_number").eq("order_id", data.orderId)
|
|
1054
|
-
.order("cart_number", { ascending: false }).limit(1);
|
|
1055
|
-
if (existingMax && existingMax[0])
|
|
1056
|
-
cartNumber = existingMax[0].cart_number + 1;
|
|
1057
|
-
const autoCarts = [];
|
|
1058
|
-
const remainders = [];
|
|
1059
|
-
const blockedLines = [];
|
|
1060
|
-
for (const it of items ?? []) {
|
|
1061
|
-
const prod = it.products ?? {};
|
|
1062
|
-
// Onbekend blijft onbekend: geen terugval op 1 (1x1x1 = 1 plant per kar).
|
|
1063
|
-
const pos = (v) => {
|
|
1064
|
-
if (v === null || v === undefined)
|
|
1065
|
-
return null;
|
|
1066
|
-
const n = Math.round(Number(v));
|
|
1067
|
-
return Number.isFinite(n) && n > 0 ? n : null;
|
|
1068
|
-
};
|
|
1069
|
-
const ppc = pos(prod.plants_per_crate);
|
|
1070
|
-
const cpl = pos(prod.crates_per_layer);
|
|
1071
|
-
const lpc = pos(prod.layers_per_cart);
|
|
1072
|
-
if (ppc === null || cpl === null || lpc === null) {
|
|
1073
|
-
blockedLines.push({
|
|
1074
|
-
order_item_id: it.id,
|
|
1075
|
-
product_name: it.product_name,
|
|
1076
|
-
missing: [
|
|
1077
|
-
ppc === null ? "plants_per_crate" : null,
|
|
1078
|
-
cpl === null ? "crates_per_layer" : null,
|
|
1079
|
-
lpc === null ? "layers_per_cart" : null,
|
|
1080
|
-
].filter((m) => m !== null),
|
|
1081
|
-
});
|
|
1082
|
-
continue;
|
|
1083
|
-
}
|
|
1084
|
-
const cratesPerCart = cpl * lpc;
|
|
1085
|
-
const totalPlants = Math.max(0, Number(it.quantity ?? 0));
|
|
1086
|
-
const totalCrates = Math.ceil(totalPlants / ppc);
|
|
1087
|
-
const fullCarts = Math.floor(totalCrates / cratesPerCart);
|
|
1088
|
-
const remainder = totalCrates - fullCarts * cratesPerCart;
|
|
1089
|
-
for (let i = 0; i < fullCarts; i++) {
|
|
1090
|
-
const { data: cart, error: cErr } = await supabaseAdmin
|
|
1091
|
-
.from("order_carts").insert({
|
|
1092
|
-
order_id: data.orderId,
|
|
1093
|
-
cart_number: cartNumber,
|
|
1094
|
-
is_auto: true,
|
|
1095
|
-
is_complete: true,
|
|
1096
|
-
// Expliciet zetten: nooit op de kolom-default leunen.
|
|
1097
|
-
source: "auto",
|
|
1098
|
-
algorithm_version: CART_PLANNER_VERSION,
|
|
1099
|
-
}).select("id").single();
|
|
1100
|
-
if (cErr)
|
|
1101
|
-
throw new Error(cErr.message);
|
|
1102
|
-
await supabaseAdmin.from("order_cart_items").insert({
|
|
1103
|
-
cart_id: cart.id,
|
|
1104
|
-
order_item_id: it.id,
|
|
1105
|
-
product_id: it.product_id,
|
|
1106
|
-
product_name: it.product_name,
|
|
1107
|
-
crates: cratesPerCart,
|
|
1108
|
-
plants_per_crate: ppc,
|
|
1109
|
-
crates_per_layer: cpl,
|
|
1110
|
-
});
|
|
1111
|
-
autoCarts.push({
|
|
1112
|
-
cart_number: cartNumber,
|
|
1113
|
-
product_name: it.product_name,
|
|
1114
|
-
crates: cratesPerCart,
|
|
1115
|
-
plants: cratesPerCart * ppc,
|
|
1116
|
-
capacity: cratesPerCart,
|
|
1117
|
-
});
|
|
1118
|
-
cartNumber++;
|
|
1119
|
-
}
|
|
1120
|
-
if (remainder > 0) {
|
|
1121
|
-
remainders.push({
|
|
1122
|
-
order_item_id: it.id,
|
|
1123
|
-
product_id: it.product_id,
|
|
1124
|
-
product_name: it.product_name,
|
|
1125
|
-
crates_remaining: remainder,
|
|
1126
|
-
plants_per_crate: ppc,
|
|
1127
|
-
crates_per_layer: cpl,
|
|
1128
|
-
layers_per_cart: lpc,
|
|
1129
|
-
});
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
const { data: manualCartsRaw } = await supabaseAdmin
|
|
1133
|
-
.from("order_carts")
|
|
1134
|
-
.select("id, cart_number, notes, order_cart_items(order_item_id, product_id, product_name, crates, plants_per_crate, crates_per_layer)")
|
|
1135
|
-
.eq("order_id", data.orderId).eq("is_auto", false)
|
|
1136
|
-
.order("cart_number");
|
|
1137
|
-
const existingManualCarts = (manualCartsRaw ?? []).map((c) => ({
|
|
1138
|
-
cart_number: c.cart_number,
|
|
1139
|
-
notes: c.notes,
|
|
1140
|
-
items: (c.order_cart_items ?? []).map((i) => ({
|
|
1141
|
-
order_item_id: i.order_item_id,
|
|
1142
|
-
product_id: i.product_id,
|
|
1143
|
-
product_name: i.product_name,
|
|
1144
|
-
crates: Number(i.crates ?? 0),
|
|
1145
|
-
plants_per_crate: Number(i.plants_per_crate ?? 1),
|
|
1146
|
-
crates_per_layer: Number(i.crates_per_layer ?? 1),
|
|
1147
|
-
})),
|
|
1148
|
-
}));
|
|
1149
|
-
const assignedByItem = {};
|
|
1150
|
-
existingManualCarts.forEach((c) => c.items.forEach((i) => {
|
|
1151
|
-
if (!i.order_item_id)
|
|
1152
|
-
return;
|
|
1153
|
-
assignedByItem[i.order_item_id] = (assignedByItem[i.order_item_id] ?? 0) + Number(i.crates ?? 0);
|
|
1154
|
-
}));
|
|
1155
|
-
const adjustedRemainders = remainders
|
|
1156
|
-
.map((r) => ({ ...r, crates_remaining: Math.max(0, r.crates_remaining - (assignedByItem[r.order_item_id] ?? 0)) }))
|
|
1157
|
-
.filter((r) => r.crates_remaining > 0);
|
|
1158
|
-
if (blockedLines.length > 0) {
|
|
1159
|
-
const { error: blockErr } = await supabaseAdmin.rpc("logistics_plan_block", {
|
|
1160
|
-
p_order_ids: [data.orderId],
|
|
1161
|
-
p_reason: "belading_onbekend",
|
|
1162
|
-
p_details: { source: "floriday-verkoop-module/planAndSaveAutoCarts", items: blockedLines },
|
|
1163
|
-
});
|
|
1164
|
-
if (blockErr) {
|
|
1165
|
-
console.warn("[karindeling] blokkade niet vastgelegd:", blockErr.message, blockedLines);
|
|
1166
|
-
}
|
|
1167
|
-
// Eenmalige mailmelding naar het interne adres uit de e-mailmodule.
|
|
1168
|
-
const { notifyOrderBlocked } = await import("./order-block-mail.server");
|
|
1169
|
-
const mail = await notifyOrderBlocked({
|
|
1170
|
-
orderIds: [data.orderId],
|
|
1171
|
-
reasonKey: "belading_onbekend",
|
|
1172
|
-
reason: "Karindeling geblokkeerd: orderregel(s) zonder volledige belading.",
|
|
1173
|
-
details: { items: blockedLines },
|
|
1174
|
-
});
|
|
1175
|
-
for (const w of mail.warnings)
|
|
1176
|
-
console.warn("[karindeling]", w);
|
|
1177
|
-
}
|
|
1178
|
-
return {
|
|
1179
|
-
ok: true,
|
|
1180
|
-
autoCarts,
|
|
1181
|
-
remainders: adjustedRemainders,
|
|
1182
|
-
existingManualCarts,
|
|
1183
|
-
nextCartNumber: cartNumber,
|
|
1184
|
-
blockedLines,
|
|
1185
|
-
};
|
|
1186
|
-
});
|
|
1187
|
-
const ManualCartItemSchema = z.object({
|
|
1188
|
-
order_item_id: z.string().uuid().nullable(),
|
|
1189
|
-
product_id: z.string().uuid().nullable(),
|
|
1190
|
-
product_name: z.string().min(1).max(255),
|
|
1191
|
-
crates: z.number().int().min(1),
|
|
1192
|
-
plants_per_crate: z.number().int().min(1),
|
|
1193
|
-
crates_per_layer: z.number().int().min(1),
|
|
1194
|
-
});
|
|
1195
|
-
const ManualCartSchema = z.object({
|
|
1196
|
-
is_complete: z.boolean().optional(),
|
|
1197
|
-
notes: z.string().max(500).nullable().optional(),
|
|
1198
|
-
items: z.array(ManualCartItemSchema).min(1),
|
|
1199
|
-
});
|
|
1200
|
-
export const saveManualCarts = createServerFn({ method: "POST" })
|
|
1201
|
-
.middleware([requireSupabaseAuth])
|
|
1202
|
-
.inputValidator((input) => z.object({ orderId: z.string().uuid(), carts: z.array(ManualCartSchema) }).parse(input))
|
|
1203
|
-
.handler(async ({ data }) => {
|
|
1204
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
1205
|
-
await supabaseAdmin.from("order_carts").delete()
|
|
1206
|
-
.eq("order_id", data.orderId).eq("is_auto", false);
|
|
1207
|
-
const { data: existingMax } = await supabaseAdmin
|
|
1208
|
-
.from("order_carts").select("cart_number").eq("order_id", data.orderId)
|
|
1209
|
-
.order("cart_number", { ascending: false }).limit(1);
|
|
1210
|
-
let cartNumber = existingMax && existingMax[0] ? existingMax[0].cart_number + 1 : 1;
|
|
1211
|
-
for (const cart of data.carts) {
|
|
1212
|
-
const { data: inserted, error: cErr } = await supabaseAdmin
|
|
1213
|
-
.from("order_carts").insert({
|
|
1214
|
-
order_id: data.orderId,
|
|
1215
|
-
cart_number: cartNumber,
|
|
1216
|
-
is_auto: false,
|
|
1217
|
-
is_complete: !!cart.is_complete,
|
|
1218
|
-
notes: cart.notes ?? null,
|
|
1219
|
-
// Expliciet zetten: nooit op de kolom-default leunen.
|
|
1220
|
-
source: "manual",
|
|
1221
|
-
algorithm_version: CART_PLANNER_VERSION,
|
|
1222
|
-
}).select("id").single();
|
|
1223
|
-
if (cErr)
|
|
1224
|
-
throw new Error(cErr.message);
|
|
1225
|
-
const rows = cart.items.map((i) => ({
|
|
1226
|
-
cart_id: inserted.id,
|
|
1227
|
-
order_item_id: i.order_item_id,
|
|
1228
|
-
product_id: i.product_id,
|
|
1229
|
-
product_name: i.product_name,
|
|
1230
|
-
crates: i.crates,
|
|
1231
|
-
plants_per_crate: i.plants_per_crate,
|
|
1232
|
-
crates_per_layer: i.crates_per_layer,
|
|
1233
|
-
}));
|
|
1234
|
-
const { error: iErr } = await supabaseAdmin.from("order_cart_items").insert(rows);
|
|
1235
|
-
if (iErr)
|
|
1236
|
-
throw new Error(iErr.message);
|
|
1237
|
-
cartNumber++;
|
|
1238
|
-
}
|
|
1239
|
-
return { ok: true };
|
|
1240
|
-
});
|
|
1241
1037
|
// ─── Lokale bestelling bewerken (aantal + leverdatum) ─────────────────────
|
|
1242
1038
|
// Alleen voor lokale/handmatige bestellingen. FO-orders worden geweigerd.
|
|
1243
1039
|
// Stock (reserved) en karindeling worden automatisch bijgewerkt door
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logistics-engine.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"logistics-engine.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAyEpC;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,SAAS,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,MAAM,CAAC;IACzE,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBnE;AAED,iEAAiE;AACjE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,gBAAgB,GAAG,eAAe,GACvC,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
|
@@ -23,9 +23,14 @@ export async function buildEngineOrderPayload(orderId) {
|
|
|
23
23
|
return null;
|
|
24
24
|
const o = order;
|
|
25
25
|
const cf = (o.custom_fields ?? {});
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
const
|
|
26
|
+
// Karren uit de Engine-leesfunctie (gedeelde bufferkarren inbegrepen).
|
|
27
|
+
// Fail closed: lukt de vraag niet, dan geen payload (zoals voorheen bij een leesfout).
|
|
28
|
+
const { loadPlanCoverage } = await import("./plan-coverage.server");
|
|
29
|
+
const cov = (await loadPlanCoverage(supabaseAdmin, [orderId])).get(orderId);
|
|
30
|
+
if (!cov || cov.state === "onbekend") {
|
|
31
|
+
throw new Error(cov?.error ?? "Indeling niet beschikbaar bij de Logistiek Engine");
|
|
32
|
+
}
|
|
33
|
+
const cartCount = cov.carts.length;
|
|
29
34
|
const pickupAt = cf.pickup_moment ?? cf.pickup_datetime ?? cf.pickup_date ?? o.delivery_date ?? null;
|
|
30
35
|
// Ladingdrager NOOIT gokken. Een onbekende of ontbrekende drager blokkeert:
|
|
31
36
|
// een verzonnen "trolley" leidt tot een verkeerde karrenberekening.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type PlanCoverageState = "ingedeeld" | "deels" | "geen_indeling" | "geen_regels" | "onbekend";
|
|
2
|
+
export interface PlanCoverageLine {
|
|
3
|
+
orderItemId: string | null;
|
|
4
|
+
productId: string | null;
|
|
5
|
+
productName: string | null;
|
|
6
|
+
crates: number;
|
|
7
|
+
boxes: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PlanCoverageCart {
|
|
10
|
+
cartId: string;
|
|
11
|
+
cartNumber: number | null;
|
|
12
|
+
bufferId: string | null;
|
|
13
|
+
shared: boolean;
|
|
14
|
+
carrierType: string | null;
|
|
15
|
+
lines: PlanCoverageLine[];
|
|
16
|
+
}
|
|
17
|
+
export interface PlanCoveragePickRow {
|
|
18
|
+
queueItemId: string | null;
|
|
19
|
+
documentNumber: string | null;
|
|
20
|
+
cartId: string | null;
|
|
21
|
+
printedAt: string | null;
|
|
22
|
+
completedAt: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface PlanCoverage {
|
|
25
|
+
state: PlanCoverageState;
|
|
26
|
+
label: string;
|
|
27
|
+
progressPct: number | null;
|
|
28
|
+
reason: string | null;
|
|
29
|
+
carts: PlanCoverageCart[];
|
|
30
|
+
pickRows: PlanCoveragePickRow[];
|
|
31
|
+
pickStatus: "geen_pickrij" | "open" | "bezig" | "gepickt" | null;
|
|
32
|
+
pickedAt: string | null;
|
|
33
|
+
/** Alleen uit de lijstversie (geen karregels): aantal karren volgens de Engine. */
|
|
34
|
+
cartCount?: number | null;
|
|
35
|
+
/** Alleen uit de lijstversie: order staat in een buffer (gedeelde kar). */
|
|
36
|
+
shared?: boolean | null;
|
|
37
|
+
/** Gezet als de Engine-vraag mislukte (fail closed: niets geschreven). */
|
|
38
|
+
error?: string | null;
|
|
39
|
+
}
|
|
40
|
+
export declare function defaultLabel(state: PlanCoverageState): string;
|
|
41
|
+
export declare function unknownCoverage(error: string): PlanCoverage;
|
|
42
|
+
/** Leest het jsonb-antwoord defensief in; ontbrekende velden worden leeg. */
|
|
43
|
+
export declare function parsePlanCoverage(raw: unknown): PlanCoverage;
|
|
44
|
+
/** Leest één rij van logistics_order_plan_coverage_list (platte kolommen, geen karregels). */
|
|
45
|
+
export declare function parsePlanCoverageListRow(r: any): PlanCoverage;
|
|
46
|
+
/** Samenvatting voor één rij in de lijst (buffer: de zwakste status wint). */
|
|
47
|
+
export interface PlanCoverageSummary {
|
|
48
|
+
state: PlanCoverageState;
|
|
49
|
+
label: string;
|
|
50
|
+
progressPct: number | null;
|
|
51
|
+
reason: string | null;
|
|
52
|
+
shared: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function summarize(c: PlanCoverage): PlanCoverageSummary;
|
|
55
|
+
export declare function mergeSummaries(list: (PlanCoverageSummary | null | undefined)[]): PlanCoverageSummary | null;
|
|
56
|
+
//# sourceMappingURL=plan-coverage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-coverage.d.ts","sourceRoot":"","sources":["../../src/lib/plan-coverage.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,GAAG,UAAU,CAAC;AAErG,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,UAAU,EAAE,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IACjE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAYD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAa7D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAY3D;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAsC5D;AAED,8FAA8F;AAC9F,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,GAAG,GAAG,YAAY,CAgB7D;AAED,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAUD,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,GAAG,mBAAmB,CAQ9D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,CAAC,mBAAmB,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,mBAAmB,GAAG,IAAI,CAK3G"}
|