@flowselections/logistiek-engine 1.0.27 → 1.0.28
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/README.md +24 -24
- package/dist-lib/components/AfmetingenBronnenCard.d.ts +2 -0
- package/dist-lib/components/AfmetingenBronnenCard.d.ts.map +1 -0
- package/dist-lib/components/AfmetingenBronnenCard.js +46 -0
- package/dist-lib/components/BufferDebugCard.d.ts.map +1 -1
- package/dist-lib/components/BufferDebugCard.js +6 -1
- package/dist-lib/components/ConsistentieCard.d.ts.map +1 -1
- package/dist-lib/components/ConsistentieCard.js +1 -1
- package/dist-lib/components/KarplanStatusCard.d.ts +2 -0
- package/dist-lib/components/KarplanStatusCard.d.ts.map +1 -0
- package/dist-lib/components/KarplanStatusCard.js +31 -0
- package/dist-lib/components/RegiePage.d.ts.map +1 -1
- package/dist-lib/components/RegiePage.js +3 -1
- package/dist-lib/components/RuleEnginePage.d.ts.map +1 -1
- package/dist-lib/components/RuleEnginePage.js +7 -4
- package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts +2 -0
- package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts.map +1 -0
- package/dist-lib/components/rule-engine/FloridayConnectionBanner.js +41 -0
- package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -1
- package/dist-lib/components/rule-engine/RuleFieldInput.js +9 -0
- package/dist-lib/hooks/useRecalcJob.d.ts.map +1 -1
- package/dist-lib/hooks/useRecalcJob.js +26 -1
- package/dist-lib/hooks/useScannersPrinters.d.ts +11 -0
- package/dist-lib/hooks/useScannersPrinters.d.ts.map +1 -0
- package/dist-lib/hooks/useScannersPrinters.js +65 -0
- package/dist-lib/index.d.ts +2 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +2 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2063 -78
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +20293 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/client.js +13 -0
- package/dist-lib/integrations/supabase/client.server.d.ts +2063 -78
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +2063 -78
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2097 -68
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/attachAuthToken.d.ts +2 -0
- package/dist-lib/lib/attachAuthToken.d.ts.map +1 -0
- package/dist-lib/lib/attachAuthToken.js +50 -0
- package/dist-lib/lib/bufferMatching.d.ts.map +1 -1
- package/dist-lib/lib/bufferMatching.js +1 -0
- package/dist-lib/lib/cartPlanRequests.server.d.ts +13 -0
- package/dist-lib/lib/cartPlanRequests.server.d.ts.map +1 -0
- package/dist-lib/lib/cartPlanRequests.server.js +168 -0
- package/dist-lib/lib/controlScan.server.d.ts +12 -0
- package/dist-lib/lib/controlScan.server.d.ts.map +1 -0
- package/dist-lib/lib/controlScan.server.js +197 -0
- package/dist-lib/lib/dimensionData.server.d.ts +11 -1
- package/dist-lib/lib/dimensionData.server.d.ts.map +1 -1
- package/dist-lib/lib/dimensionData.server.js +58 -48
- package/dist-lib/lib/dimensionPlanner.d.ts +26 -1
- package/dist-lib/lib/dimensionPlanner.d.ts.map +1 -1
- package/dist-lib/lib/dimensionPlanner.js +37 -6
- package/dist-lib/lib/floridayDocuments.functions.d.ts +6240 -285
- package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayDocuments.functions.js +4 -3
- package/dist-lib/lib/floridayPush.functions.d.ts +2064 -79
- package/dist-lib/lib/floridayPush.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayPush.functions.js +2 -1
- package/dist-lib/lib/handlers/logistics-tick.server.d.ts.map +1 -1
- package/dist-lib/lib/handlers/logistics-tick.server.js +57 -0
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +10302 -377
- package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.functions.js +6 -5
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts +42 -0
- package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -1
- package/dist-lib/lib/loadCarrierPlanning.server.js +563 -31
- package/dist-lib/lib/logisticsPlanner.d.ts.map +1 -1
- package/dist-lib/lib/logisticsPlanner.js +24 -9
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +4163 -193
- package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.functions.js +3 -2
- package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalcJob.server.js +1 -0
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts +2064 -79
- package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsRecalculate.functions.js +3 -1
- package/dist-lib/lib/modulePrompts.d.ts.map +1 -1
- package/dist-lib/lib/modulePrompts.js +2560 -1586
- package/dist-lib/lib/planQueue.server.d.ts.map +1 -1
- package/dist-lib/lib/planQueue.server.js +6 -0
- package/dist-lib/lib/readableServerErrors.d.ts +2 -0
- package/dist-lib/lib/readableServerErrors.d.ts.map +1 -0
- package/dist-lib/lib/readableServerErrors.js +36 -0
- package/dist-lib/lib/ruleEngineSchema.d.ts +1 -1
- package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineSchema.js +72 -51
- package/dist-lib/lib/ruleEngineWizard.d.ts.map +1 -1
- package/dist-lib/lib/ruleEngineWizard.js +5 -6
- package/package.json +1 -1
|
@@ -57,31 +57,29 @@ function missingText(subject, name, field) {
|
|
|
57
57
|
* Haalt fust- en planthoogtegegevens op bij de producten van de plannerinvoer.
|
|
58
58
|
* Groepsleden gebruiken hun eigen product; regels zonder product_id kunnen niet
|
|
59
59
|
* op afmetingen worden ingedeeld.
|
|
60
|
+
*
|
|
61
|
+
* Ontbreekt alleen de planthoogte, dan wordt die afgeleid uit de belading:
|
|
62
|
+
* de bruikbare hoogte van de ladingdrager gedeeld door het aantal lagen dat
|
|
63
|
+
* er volgens de belading op hoort, min de hoogte van het fust. Lukt dat niet
|
|
64
|
+
* (geen ladingdragerhoogte, geen lagen, of een uitkomst van nul of minder),
|
|
65
|
+
* dan blijft het een ontbrekend gegeven en wordt er niets ingedeeld.
|
|
60
66
|
*/
|
|
61
|
-
export async function buildDimensionItems(sb, inputs
|
|
67
|
+
export async function buildDimensionItems(sb, inputs, carrier,
|
|
68
|
+
/** Aantal lagen dat volgens het beheerscherm op deze ladingdrager past. */
|
|
69
|
+
carrierLayers) {
|
|
62
70
|
const missing = [];
|
|
71
|
+
const derived = [];
|
|
63
72
|
const items = [];
|
|
64
73
|
const productIds = [...new Set(inputs.map((i) => i.product_id).filter((v) => Boolean(v)))];
|
|
65
|
-
const
|
|
74
|
+
const dimById = new Map();
|
|
66
75
|
if (productIds.length > 0) {
|
|
67
76
|
const { data } = await sb
|
|
68
|
-
.from("
|
|
69
|
-
.select("
|
|
70
|
-
.in("
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const fustIds = [
|
|
75
|
-
...new Set([...productById.values()].map((p) => p.fust_type_id).filter((v) => Boolean(v))),
|
|
76
|
-
];
|
|
77
|
-
const fustById = new Map();
|
|
78
|
-
if (fustIds.length > 0) {
|
|
79
|
-
const { data } = await sb
|
|
80
|
-
.from("fust_types")
|
|
81
|
-
.select("id, name, length_mm, width_mm, height_mm")
|
|
82
|
-
.in("id", fustIds);
|
|
83
|
-
for (const f of data ?? [])
|
|
84
|
-
fustById.set(String(f.id), f);
|
|
77
|
+
.from("logistics_product_dimensions")
|
|
78
|
+
.select("product_id, product_name, plants_per_crate, fust_type_id, fust_name, fust_length_mm, fust_width_mm, fust_height_mm, plant_height_cm, height_source, dimension_source, missing_reason")
|
|
79
|
+
.in("product_id", productIds);
|
|
80
|
+
const safeRows = Array.isArray(data) ? data : [];
|
|
81
|
+
for (const d of safeRows)
|
|
82
|
+
dimById.set(String(d.product_id), d);
|
|
85
83
|
}
|
|
86
84
|
const seen = new Set();
|
|
87
85
|
const addMissing = (m) => {
|
|
@@ -95,8 +93,8 @@ export async function buildDimensionItems(sb, inputs) {
|
|
|
95
93
|
if (input.plants <= 0)
|
|
96
94
|
continue;
|
|
97
95
|
const label = input.product_name || "onbekend product";
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
96
|
+
const dim = input.product_id ? dimById.get(input.product_id) : null;
|
|
97
|
+
if (!dim) {
|
|
100
98
|
addMissing({
|
|
101
99
|
subject: "product",
|
|
102
100
|
name: label,
|
|
@@ -106,37 +104,46 @@ export async function buildDimensionItems(sb, inputs) {
|
|
|
106
104
|
});
|
|
107
105
|
continue;
|
|
108
106
|
}
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
const name = dim.product_name ?? label;
|
|
108
|
+
const l = posNum(dim.fust_length_mm);
|
|
109
|
+
const w = posNum(dim.fust_width_mm);
|
|
110
|
+
const h = posNum(dim.fust_height_mm);
|
|
111
|
+
let plantHeight = posNum(dim.plant_height_cm);
|
|
112
|
+
// Planthoogte ontbreekt, maar fust- en dragermaten zijn er wel: afleiden
|
|
113
|
+
// uit het aantal lagen dat op de drager hoort. Eerst de belading van het
|
|
114
|
+
// product zelf; past dat fysiek niet (fust hoger dan de laaghoogte), dan
|
|
115
|
+
// het aantal lagen van de ladingdrager uit het beheerscherm.
|
|
116
|
+
if (plantHeight == null && h != null && carrier && carrier.height_cm > 0) {
|
|
117
|
+
const productLayers = Math.round(Number(input.layers_per_cart));
|
|
118
|
+
const carrierLayerCount = Math.round(Number(carrierLayers));
|
|
119
|
+
const kandidaten = [];
|
|
120
|
+
if (Number.isFinite(productLayers) && productLayers > 0)
|
|
121
|
+
kandidaten.push({ layers: productLayers, bron: "de belading van het product" });
|
|
122
|
+
if (Number.isFinite(carrierLayerCount) && carrierLayerCount > 0)
|
|
123
|
+
kandidaten.push({ layers: carrierLayerCount, bron: `de lagen van ${carrier.name}` });
|
|
124
|
+
for (const kandidaat of kandidaten) {
|
|
125
|
+
const perLayer = carrier.height_cm / kandidaat.layers;
|
|
126
|
+
const afgeleid = Number((perLayer - h / 10).toFixed(1));
|
|
127
|
+
if (afgeleid > 0) {
|
|
128
|
+
plantHeight = afgeleid;
|
|
129
|
+
derived.push(`De planthoogte van ${name} was niet ingevuld en is berekend uit ${kandidaat.bron}: ` +
|
|
130
|
+
`${kandidaat.layers} lagen op ${carrier.name} (${carrier.height_cm} cm) min de fusthoogte ` +
|
|
131
|
+
`(${round1(h / 10)} cm) geeft ${afgeleid} cm.`);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
117
135
|
}
|
|
118
|
-
|
|
119
|
-
if (!fust) {
|
|
136
|
+
if (l == null || w == null || h == null || plantHeight == null) {
|
|
120
137
|
addMissing({
|
|
121
|
-
subject: "product",
|
|
122
|
-
name
|
|
123
|
-
field: "fusttype",
|
|
124
|
-
message:
|
|
138
|
+
subject: l == null && w == null && h == null ? "product" : "fust",
|
|
139
|
+
name,
|
|
140
|
+
field: plantHeight == null && l != null ? "planthoogte" : "fusttype",
|
|
141
|
+
message: dim.missing_reason ??
|
|
142
|
+
`De karrenindeling kan niet worden berekend. De afmetingen van ${name} zijn onvolledig.`,
|
|
125
143
|
});
|
|
126
144
|
continue;
|
|
127
145
|
}
|
|
128
|
-
const
|
|
129
|
-
const w = posNum(fust.width_mm);
|
|
130
|
-
const h = posNum(fust.height_mm);
|
|
131
|
-
if (l == null)
|
|
132
|
-
addMissing({ subject: "fust", name: fust.name, field: "lengte", message: missingText("fust", fust.name, "lengte") });
|
|
133
|
-
if (w == null)
|
|
134
|
-
addMissing({ subject: "fust", name: fust.name, field: "breedte", message: missingText("fust", fust.name, "breedte") });
|
|
135
|
-
if (h == null)
|
|
136
|
-
addMissing({ subject: "fust", name: fust.name, field: "hoogte", message: missingText("fust", fust.name, "hoogte") });
|
|
137
|
-
if (plantHeight == null || l == null || w == null || h == null)
|
|
138
|
-
continue;
|
|
139
|
-
const ppc = Math.max(1, Math.round(input.plants_per_crate || product.plants_per_crate || 1));
|
|
146
|
+
const ppc = Math.max(1, Math.round(input.plants_per_crate || dim.plants_per_crate || 1));
|
|
140
147
|
const crates = Math.max(1, Math.ceil(input.plants / ppc));
|
|
141
148
|
items.push({
|
|
142
149
|
order_item_id: input.order_item_id,
|
|
@@ -153,5 +160,8 @@ export async function buildDimensionItems(sb, inputs) {
|
|
|
153
160
|
weight_per_crate: input.weight_per_crate ?? null,
|
|
154
161
|
});
|
|
155
162
|
}
|
|
156
|
-
return { items, missing };
|
|
163
|
+
return { items, missing, derived };
|
|
164
|
+
}
|
|
165
|
+
function round1(n) {
|
|
166
|
+
return Number(n.toFixed(1));
|
|
157
167
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const DIMENSION_PLANNER_VERSION =
|
|
1
|
+
export declare const DIMENSION_PLANNER_VERSION = 5;
|
|
2
2
|
export interface DimensionItem {
|
|
3
3
|
order_item_id: string;
|
|
4
4
|
order_id: string | null;
|
|
@@ -42,12 +42,29 @@ export interface DimensionPlacement {
|
|
|
42
42
|
/** Rotatie waarmee de fusten op deze laag staan. */
|
|
43
43
|
rotated: boolean;
|
|
44
44
|
}
|
|
45
|
+
/** Eén fust op zijn werkelijke plek binnen een laag. Alle maten in cm. */
|
|
46
|
+
export interface DimensionKegPosition {
|
|
47
|
+
keg_id: string;
|
|
48
|
+
order_item_id: string;
|
|
49
|
+
order_id: string | null;
|
|
50
|
+
product_id: string | null;
|
|
51
|
+
label: string;
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
width: number;
|
|
55
|
+
height: number;
|
|
56
|
+
rotation: number;
|
|
57
|
+
}
|
|
45
58
|
export interface DimensionLayer {
|
|
46
59
|
layer_index: number;
|
|
47
60
|
height_cm: number;
|
|
48
61
|
/** Benutting van het laagoppervlak in procenten. */
|
|
49
62
|
area_used_pct: number;
|
|
63
|
+
/** Bruikbare afmetingen van de laag in cm. */
|
|
64
|
+
layer_length_cm: number;
|
|
65
|
+
layer_width_cm: number;
|
|
50
66
|
placements: DimensionPlacement[];
|
|
67
|
+
positions: DimensionKegPosition[];
|
|
51
68
|
}
|
|
52
69
|
export interface DimensionCart {
|
|
53
70
|
cart_number: number;
|
|
@@ -66,5 +83,13 @@ export interface DimensionPlanResult {
|
|
|
66
83
|
/** Gevuld als er niets ingedeeld kan worden; dan is carts leeg. */
|
|
67
84
|
error?: string;
|
|
68
85
|
}
|
|
86
|
+
export interface PlacedRect {
|
|
87
|
+
rotated: boolean;
|
|
88
|
+
/** Linkerbovenhoek en werkelijke maten in de gekozen oriëntatie (cm). */
|
|
89
|
+
x: number;
|
|
90
|
+
y: number;
|
|
91
|
+
width: number;
|
|
92
|
+
height: number;
|
|
93
|
+
}
|
|
69
94
|
export declare function planByDimensions(items: DimensionItem[], carrier: DimensionCarrier): DimensionPlanResult;
|
|
70
95
|
//# sourceMappingURL=dimensionPlanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dimensionPlanner.d.ts","sourceRoot":"","sources":["../../src/lib/dimensionPlanner.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,kBAAkB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"dimensionPlanner.d.ts","sourceRoot":"","sources":["../../src/lib/dimensionPlanner.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oDAAoD;IACpD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,0EAA0E;AAC1E,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AAGD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAwED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EAAE,EACtB,OAAO,EAAE,gBAAgB,GACxB,mBAAmB,CA4LrB"}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// Er wordt nooit een afmeting verzonnen: ontbrekende gegevens worden vooraf
|
|
14
14
|
// gecontroleerd door de aanroeper (zie dimensionData / collectMissingDimensions).
|
|
15
15
|
// ============================================================================
|
|
16
|
-
export const DIMENSION_PLANNER_VERSION =
|
|
16
|
+
export const DIMENSION_PLANNER_VERSION = 5;
|
|
17
17
|
class LayerSpace {
|
|
18
18
|
constructor(length, width) {
|
|
19
19
|
this.length = length;
|
|
@@ -22,7 +22,7 @@ class LayerSpace {
|
|
|
22
22
|
this.totalDepth = 0;
|
|
23
23
|
this.usedArea = 0;
|
|
24
24
|
}
|
|
25
|
-
/** Probeert één fust te plaatsen. Geeft de
|
|
25
|
+
/** Probeert één fust te plaatsen. Geeft de plaatsing terug, of null. */
|
|
26
26
|
place(l, w) {
|
|
27
27
|
const options = [
|
|
28
28
|
{ a: l, b: w, rotated: false },
|
|
@@ -38,23 +38,26 @@ class LayerSpace {
|
|
|
38
38
|
if (!fitsLength)
|
|
39
39
|
continue;
|
|
40
40
|
if (o.b <= shelf.depth + 1e-9) {
|
|
41
|
+
const rect = { rotated: o.rotated, x: shelf.usedLength, y: shelf.y, width: o.a, height: o.b };
|
|
41
42
|
shelf.usedLength += o.a;
|
|
42
43
|
this.usedArea += o.a * o.b;
|
|
43
|
-
return
|
|
44
|
+
return rect;
|
|
44
45
|
}
|
|
45
46
|
if (last && this.totalDepth - shelf.depth + o.b <= this.width + 1e-9) {
|
|
47
|
+
const rect = { rotated: o.rotated, x: shelf.usedLength, y: shelf.y, width: o.a, height: o.b };
|
|
46
48
|
this.totalDepth = this.totalDepth - shelf.depth + o.b;
|
|
47
49
|
shelf.depth = o.b;
|
|
48
50
|
shelf.usedLength += o.a;
|
|
49
51
|
this.usedArea += o.a * o.b;
|
|
50
|
-
return
|
|
52
|
+
return rect;
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
if (this.totalDepth + o.b <= this.width + 1e-9) {
|
|
54
|
-
|
|
56
|
+
const rect = { rotated: o.rotated, x: 0, y: this.totalDepth, width: o.a, height: o.b };
|
|
57
|
+
this.shelves.push({ y: this.totalDepth, depth: o.b, usedLength: o.a });
|
|
55
58
|
this.totalDepth += o.b;
|
|
56
59
|
this.usedArea += o.a * o.b;
|
|
57
|
-
return
|
|
60
|
+
return rect;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
return null;
|
|
@@ -127,6 +130,7 @@ export function planByDimensions(items, carrier) {
|
|
|
127
130
|
break;
|
|
128
131
|
const space = new LayerSpace(carrier.length_cm, carrier.width_cm);
|
|
129
132
|
const perItem = new Map();
|
|
133
|
+
const positions = [];
|
|
130
134
|
let layerHeight = 0;
|
|
131
135
|
for (const b of pool) {
|
|
132
136
|
while (b.left > 0) {
|
|
@@ -142,6 +146,20 @@ export function planByDimensions(items, carrier) {
|
|
|
142
146
|
const acc = perItem.get(b) ?? { crates: 0, rotated: placed.rotated };
|
|
143
147
|
acc.crates += 1;
|
|
144
148
|
perItem.set(b, acc);
|
|
149
|
+
const seq = positions.length + 1;
|
|
150
|
+
positions.push({
|
|
151
|
+
keg_id: `L${layers.length + 1}-${seq}`,
|
|
152
|
+
order_item_id: b.item.order_item_id,
|
|
153
|
+
order_id: b.item.order_id,
|
|
154
|
+
product_id: b.item.product_id,
|
|
155
|
+
label: `${b.item.product_name} — fust ${seq} (laag ${layers.length + 1})` +
|
|
156
|
+
(placed.rotated ? " — gedraaid" : ""),
|
|
157
|
+
x: round1(placed.x),
|
|
158
|
+
y: round1(placed.y),
|
|
159
|
+
width: round1(placed.width),
|
|
160
|
+
height: round1(placed.height),
|
|
161
|
+
rotation: 0,
|
|
162
|
+
});
|
|
145
163
|
}
|
|
146
164
|
}
|
|
147
165
|
if (space.isEmpty || perItem.size === 0)
|
|
@@ -169,7 +187,10 @@ export function planByDimensions(items, carrier) {
|
|
|
169
187
|
layer_index: layers.length + 1,
|
|
170
188
|
height_cm: round1(layerHeight),
|
|
171
189
|
area_used_pct: space.areaPct,
|
|
190
|
+
layer_length_cm: carrier.length_cm,
|
|
191
|
+
layer_width_cm: carrier.width_cm,
|
|
172
192
|
placements,
|
|
193
|
+
positions,
|
|
173
194
|
});
|
|
174
195
|
heightLeft = round1(heightLeft - layerHeight);
|
|
175
196
|
}
|
|
@@ -201,6 +222,16 @@ export function planByDimensions(items, carrier) {
|
|
|
201
222
|
carts.forEach((c, idx) => {
|
|
202
223
|
c.is_complete = idx < carts.length - 1 || c.occupancy_pct >= 99.9;
|
|
203
224
|
});
|
|
225
|
+
const expectedCrates = batches.reduce((sum, batch) => sum + Math.max(0, Math.round(batch.item.crates)), 0);
|
|
226
|
+
const plannedCrates = carts.reduce((cartSum, cart) => cartSum + cart.layers.reduce((layerSum, layer) => layerSum + layer.positions.length, 0), 0);
|
|
227
|
+
if (plannedCrates !== expectedCrates) {
|
|
228
|
+
return {
|
|
229
|
+
carts: [],
|
|
230
|
+
warnings,
|
|
231
|
+
error: `De fysieke indeling is onvolledig: ${expectedCrates} fusten verwacht, maar ${plannedCrates} ` +
|
|
232
|
+
`fustposities berekend. Er is niets ingedeeld.`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
204
235
|
return { carts, warnings };
|
|
205
236
|
}
|
|
206
237
|
function allowedOnCart(batch, orders, customers, carrier) {
|