@flowselections/floriday-verkoop-module 1.0.51 → 1.0.52
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/SalesOrderDetailPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailPage.js +10 -1
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.js +65 -47
- package/dist-lib/lib/order-numbers.server.d.ts +10 -0
- package/dist-lib/lib/order-numbers.server.d.ts.map +1 -0
- package/dist-lib/lib/order-numbers.server.js +30 -0
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +3 -13
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +9 -9
- package/package.json +1 -1
|
@@ -23,7 +23,7 @@ export async function replanScopeInternal(scope) {
|
|
|
23
23
|
throw new Error("replanScopeInternal: geen scope");
|
|
24
24
|
let ordersQuery = supabaseAdmin
|
|
25
25
|
.from("orders")
|
|
26
|
-
.select("id, custom_fields, plan_locked_at")
|
|
26
|
+
.select("id, order_number, custom_fields, plan_locked_at")
|
|
27
27
|
.is("deleted_at", null);
|
|
28
28
|
ordersQuery = bufferId
|
|
29
29
|
? ordersQuery.eq("buffer_id", bufferId)
|
|
@@ -31,6 +31,11 @@ export async function replanScopeInternal(scope) {
|
|
|
31
31
|
const { data: orders } = await ordersQuery;
|
|
32
32
|
const orderRows = (orders ?? []);
|
|
33
33
|
const orderIds = orderRows.map((o) => o.id);
|
|
34
|
+
const orderNumberById = new Map();
|
|
35
|
+
for (const o of orderRows) {
|
|
36
|
+
if (o.order_number)
|
|
37
|
+
orderNumberById.set(o.id, String(o.order_number));
|
|
38
|
+
}
|
|
34
39
|
if (orderRows.some((o) => o.plan_locked_at)) {
|
|
35
40
|
return {
|
|
36
41
|
ok: true,
|
|
@@ -53,26 +58,51 @@ export async function replanScopeInternal(scope) {
|
|
|
53
58
|
throw new Error(itErr.message);
|
|
54
59
|
// Onbekend blijft onbekend: geen ?? 1. De planner blokkeert regels
|
|
55
60
|
// zonder (volledige) belading in plaats van 1×1×1 te plannen.
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
const pos = (v) => {
|
|
62
|
+
if (v === null || v === undefined)
|
|
63
|
+
return null;
|
|
64
|
+
const n = Number(v);
|
|
65
|
+
return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
|
|
66
|
+
};
|
|
67
|
+
/** Een bron telt alleen als álle drie de velden gevuld zijn (> 0). */
|
|
68
|
+
const completeSource = (plants_per_crate, crates_per_layer, layers_per_cart) => {
|
|
69
|
+
const a = pos(plants_per_crate);
|
|
70
|
+
const b = pos(crates_per_layer);
|
|
71
|
+
const c = pos(layers_per_cart);
|
|
72
|
+
return a && b && c ? { plants_per_crate: a, crates_per_layer: b, layers_per_cart: c } : null;
|
|
65
73
|
};
|
|
74
|
+
// Beladingbron per order: custom_fields.floriday_packing bevat de belading
|
|
75
|
+
// zoals die in Floriday op de order staat. Dat zijn de packing-VELDEN
|
|
76
|
+
// (pieces_per_package / packages_per_layer / layers_per_load_carrier) —
|
|
77
|
+
// géén voorgekauwde karrenlijst; de karindeling rekenen we zelf.
|
|
78
|
+
const floridayByOrder = new Map();
|
|
79
|
+
for (const o of orderRows) {
|
|
80
|
+
const fp = (o.custom_fields ?? {})?.floriday_packing ?? null;
|
|
81
|
+
if (!fp)
|
|
82
|
+
continue;
|
|
83
|
+
const bel = completeSource(fp.pieces_per_package, fp.packages_per_layer, fp.layers_per_load_carrier);
|
|
84
|
+
if (bel)
|
|
85
|
+
floridayByOrder.set(o.id, bel);
|
|
86
|
+
}
|
|
87
|
+
let usedFloridayPacking = false;
|
|
66
88
|
const inputs = (items ?? []).map((it) => {
|
|
67
89
|
const prod = it.products ?? {};
|
|
90
|
+
// Volgorde: floriday_packing → orderregel → product. Niet coalescen per
|
|
91
|
+
// veld: een half-Floriday/half-orderregel-belading is geen belading.
|
|
92
|
+
const fromFloriday = floridayByOrder.get(it.order_id) ?? null;
|
|
93
|
+
const bel = fromFloriday ??
|
|
94
|
+
completeSource(it.plants_per_crate, it.crates_per_layer, it.layers_per_cart) ??
|
|
95
|
+
completeSource(prod.plants_per_crate, prod.crates_per_layer, prod.layers_per_cart);
|
|
96
|
+
if (bel && fromFloriday)
|
|
97
|
+
usedFloridayPacking = true;
|
|
68
98
|
return {
|
|
69
99
|
order_item_id: it.id,
|
|
70
100
|
product_id: it.product_id,
|
|
71
101
|
product_name: it.product_name,
|
|
72
102
|
plants: Math.max(0, Number(it.quantity ?? 0)),
|
|
73
|
-
plants_per_crate:
|
|
74
|
-
crates_per_layer:
|
|
75
|
-
layers_per_cart:
|
|
103
|
+
plants_per_crate: bel?.plants_per_crate ?? null,
|
|
104
|
+
crates_per_layer: bel?.crates_per_layer ?? null,
|
|
105
|
+
layers_per_cart: bel?.layers_per_cart ?? null,
|
|
76
106
|
};
|
|
77
107
|
});
|
|
78
108
|
// Item → order-map voor het toewijzen van cart.order_id bij homogene karren.
|
|
@@ -87,42 +117,14 @@ export async function replanScopeInternal(scope) {
|
|
|
87
117
|
const { error: delErr } = await cartScope(supabaseAdmin.from("order_carts").delete());
|
|
88
118
|
if (delErr)
|
|
89
119
|
throw new Error(delErr.message);
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
120
|
+
// De karindeling wordt hier altijd zelf gerekend. custom_fields.floriday_packing
|
|
121
|
+
// levert alleen de belading-VELDEN (pieces_per_package / packages_per_layer /
|
|
122
|
+
// layers_per_load_carrier) — de mirror schrijft nooit een voorgekauwde
|
|
123
|
+
// carts-array, dus die tak bestaat bewust niet meer.
|
|
93
124
|
let plannedCarts = [];
|
|
94
125
|
const warnings = [];
|
|
95
126
|
let blockedItems = [];
|
|
96
|
-
|
|
97
|
-
const soloCustom = solo?.custom_fields ?? {};
|
|
98
|
-
const floridayPacking = soloCustom?.floriday_packing ?? null;
|
|
99
|
-
const isFo = solo && (soloCustom?.source === "floriday" || floridayPacking?.pieces_per_package);
|
|
100
|
-
if (isFo && floridayPacking && Array.isArray(floridayPacking.carts)) {
|
|
101
|
-
usedFloridayPacking = true;
|
|
102
|
-
plannedCarts = floridayPacking.carts.map((c, idx) => {
|
|
103
|
-
const cItems = (c.items ?? []).map((i) => ({
|
|
104
|
-
order_item_id: i.order_item_id,
|
|
105
|
-
product_id: i.product_id ?? null,
|
|
106
|
-
product_name: i.product_name ?? "—",
|
|
107
|
-
crates: Number(i.crates ?? 0),
|
|
108
|
-
plants: Number(i.plants ?? Number(i.crates ?? 0) * Number(i.plants_per_crate ?? 1)),
|
|
109
|
-
plants_per_crate: Math.max(1, Number(i.plants_per_crate ?? 1)),
|
|
110
|
-
crates_per_layer: Math.max(1, Number(i.crates_per_layer ?? 1)),
|
|
111
|
-
layers_per_cart: Math.max(1, Number(i.layers_per_cart ?? 1)),
|
|
112
|
-
layer_fraction: Number(i.layer_fraction ?? 1),
|
|
113
|
-
layer_share_pct: Number(i.layer_share_pct ?? 100),
|
|
114
|
-
}));
|
|
115
|
-
return {
|
|
116
|
-
cart_number: idx + 1,
|
|
117
|
-
source: "auto",
|
|
118
|
-
is_complete: !!c.is_complete,
|
|
119
|
-
occupancy_pct: Number(c.occupancy_pct ?? 100),
|
|
120
|
-
total_layers_used: Number(c.total_layers_used ?? 1),
|
|
121
|
-
items: cItems,
|
|
122
|
-
};
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
127
|
+
{
|
|
126
128
|
const [{ data: settings }, { data: overrides }] = await Promise.all([
|
|
127
129
|
supabaseAdmin.from("cart_planning_settings").select("*").eq("id", 1).maybeSingle(),
|
|
128
130
|
supabaseAdmin.from("cart_planning_product_overrides").select("product_id, never_mix"),
|
|
@@ -154,9 +156,25 @@ export async function replanScopeInternal(scope) {
|
|
|
154
156
|
missing: b.missing,
|
|
155
157
|
}));
|
|
156
158
|
if (blockedOrderIds.length > 0) {
|
|
159
|
+
const FIELD_LABELS = {
|
|
160
|
+
plants_per_crate: "planten per fust",
|
|
161
|
+
crates_per_layer: "fusten per laag",
|
|
162
|
+
layers_per_cart: "lagen per kar",
|
|
163
|
+
};
|
|
164
|
+
const nummers = blockedOrderIds
|
|
165
|
+
.map((id) => orderNumberById.get(id) ?? id.slice(0, 8))
|
|
166
|
+
.join(", ");
|
|
167
|
+
const velden = [
|
|
168
|
+
...new Set(blockedItems.flatMap((b) => b.missing)),
|
|
169
|
+
]
|
|
170
|
+
.map((f) => FIELD_LABELS[f] ?? f)
|
|
171
|
+
.join(", ");
|
|
172
|
+
const reason = `Karindeling geblokkeerd voor order(s) ${nummers}: ` +
|
|
173
|
+
`${blockedItems.length} orderregel(s) zonder volledige belading` +
|
|
174
|
+
(velden ? ` (ontbreekt: ${velden})` : "") + ".";
|
|
157
175
|
const { error: blockErr } = await supabaseAdmin.rpc("logistics_plan_block", {
|
|
158
176
|
p_order_ids: blockedOrderIds,
|
|
159
|
-
p_reason:
|
|
177
|
+
p_reason: reason,
|
|
160
178
|
p_details: { source: "floriday-verkoop-module/cart-replan", items: blockedItems },
|
|
161
179
|
});
|
|
162
180
|
if (blockErr) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type MinimalClient = {
|
|
2
|
+
from: (table: string) => any;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Vertaalt Floriday sales-order-ids naar centrale ordernummers.
|
|
6
|
+
* Eén batch-query per 400 ids; ids zonder maprij ontbreken in de Map.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveOrderNumbers(supabase: MinimalClient, foIds: Iterable<string>): Promise<Map<string, string>>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=order-numbers.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order-numbers.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-numbers.server.ts"],"names":[],"mappings":"AAKA,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC;CAC9B,CAAC;AAIF;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoB9B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Eén bron voor het ophalen van centrale ordernummers van Floriday-orders.
|
|
2
|
+
// floriday_order_number_map is leidend; deze helper wordt gebruikt door de
|
|
3
|
+
// lijstquery, het orderdetail én de buffer-siblings, zodat er nooit meer
|
|
4
|
+
// verschillende implementaties naast elkaar bestaan.
|
|
5
|
+
const CHUNK = 400;
|
|
6
|
+
/**
|
|
7
|
+
* Vertaalt Floriday sales-order-ids naar centrale ordernummers.
|
|
8
|
+
* Eén batch-query per 400 ids; ids zonder maprij ontbreken in de Map.
|
|
9
|
+
*/
|
|
10
|
+
export async function resolveOrderNumbers(supabase, foIds) {
|
|
11
|
+
const ids = Array.from(new Set(Array.from(foIds).map((v) => String(v ?? "")).filter((v) => v.length > 0)));
|
|
12
|
+
const out = new Map();
|
|
13
|
+
if (ids.length === 0)
|
|
14
|
+
return out;
|
|
15
|
+
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
16
|
+
const chunk = ids.slice(i, i + CHUNK);
|
|
17
|
+
const { data, error } = await supabase
|
|
18
|
+
.from("floriday_order_number_map")
|
|
19
|
+
.select("floriday_id, order_number")
|
|
20
|
+
.in("floriday_id", chunk);
|
|
21
|
+
if (error)
|
|
22
|
+
throw new Error(error.message);
|
|
23
|
+
for (const row of (data ?? [])) {
|
|
24
|
+
const num = String(row?.order_number ?? "").trim();
|
|
25
|
+
if (num)
|
|
26
|
+
out.set(String(row.floriday_id), num);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"salesorders-list.server.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-list.server.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"salesorders-list.server.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-list.server.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjF,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,UAAU,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,GAAG,UAAU,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AA8ID,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,UAAU,CAAC,CAqsBrB"}
|
|
@@ -4,6 +4,7 @@ import { normalizeLoadCarrierCode } from "./load-carriers";
|
|
|
4
4
|
import { normalizeTime, resolveDeliveryTimeForDate } from "./carrier-availability";
|
|
5
5
|
import { expandOrderItemsForPlanner } from "./expand-group-order-items";
|
|
6
6
|
import { planCarts } from "./cart-planner";
|
|
7
|
+
import { resolveOrderNumbers } from "./order-numbers.server";
|
|
7
8
|
import { mergeStages, resolveOrderStage } from "./order-status";
|
|
8
9
|
const FO_CAP = 20000;
|
|
9
10
|
const LOCAL_CAP = 5000;
|
|
@@ -261,15 +262,7 @@ export async function buildSalesOrdersList(supabase, params) {
|
|
|
261
262
|
}
|
|
262
263
|
};
|
|
263
264
|
const [orderNumberRows, customerRows, floridayCustomerRows, tradeItemRows, carrierRows, crateDescriptions, groupPacking,] = await Promise.all([
|
|
264
|
-
safe("Ordernummers", () =>
|
|
265
|
-
const { data, error } = await supabase
|
|
266
|
-
.from("floriday_order_number_map")
|
|
267
|
-
.select("floriday_id, order_number")
|
|
268
|
-
.in("floriday_id", chunk);
|
|
269
|
-
if (error)
|
|
270
|
-
throw new Error(error.message);
|
|
271
|
-
return (data ?? []);
|
|
272
|
-
}), []),
|
|
265
|
+
safe("Ordernummers", () => resolveOrderNumbers(supabase, foIdsForNumbers), new Map()),
|
|
273
266
|
safe("Klanten", async () => {
|
|
274
267
|
const orFilter = [
|
|
275
268
|
neededOrgIds.length ? `floriday_organization_id.in.(${neededOrgIds.join(",")})` : null,
|
|
@@ -318,10 +311,7 @@ export async function buildSalesOrdersList(supabase, params) {
|
|
|
318
311
|
loadCrateDescriptions(supabase, warnings),
|
|
319
312
|
safe("Productgroepen", () => loadGroupPacking(supabase), new Map()),
|
|
320
313
|
]);
|
|
321
|
-
const orderNumberByFoId =
|
|
322
|
-
for (const m of orderNumberRows) {
|
|
323
|
-
orderNumberByFoId.set(String(m.floriday_id), String(m.order_number ?? ""));
|
|
324
|
-
}
|
|
314
|
+
const orderNumberByFoId = orderNumberRows;
|
|
325
315
|
const carrierById = new Map();
|
|
326
316
|
for (const c of carrierRows)
|
|
327
317
|
carrierById.set(String(c.id), c);
|