@flowselections/logistiek-engine 1.0.203 → 1.0.206

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.
@@ -9,22 +9,84 @@
9
9
  // - verticaal : totalHeightPct <= 100
10
10
  // - horizontaal: crateShare per laag <= 1
11
11
  // ============================================================================
12
- export const LOAD_CARRIER_PLANNER_VERSION = 3;
12
+ export const LOAD_CARRIER_PLANNER_VERSION = 4;
13
+ /** Grens vanaf waar "beladingen per laag" als onbeperkt geldt (instelling 999 = onbeperkt). */
14
+ export const BELADINGEN_ONBEPERKT = 999;
15
+ /**
16
+ * Leidt de menging per laag af uit de werkwijze.
17
+ * Niet vrij als: geen optimale vulling, maximaal N beladingen per laag (N < 999),
18
+ * maximaal N klanten per laag, of één klant per laag.
19
+ */
20
+ export function bepaalLaagMenging(w) {
21
+ if (w.optimalFill === false)
22
+ return { menging: "per_regel", groepen: { klantenPerLaag: null, beladingenPerLaag: null }, reden: "optimale vulling staat uit" };
23
+ const b = Number(w.maxBeladingenPerLayer);
24
+ const bel = w.maxBeladingenPerLayer != null && Number.isFinite(b) && b > 0 && b < BELADINGEN_ONBEPERKT ? Math.round(b) : null;
25
+ const k = Number(w.maxCustomersPerLayer);
26
+ const kl = w.oneCustomerPerLayer ? 1 : w.maxCustomersPerLayer != null && Number.isFinite(k) && k > 0 ? Math.round(k) : null;
27
+ const delen = [];
28
+ if (kl != null)
29
+ delen.push(`maximaal ${kl} klant(en) per laag`);
30
+ if (bel != null)
31
+ delen.push(`maximaal ${bel} beladingen per laag binnen een klant`);
32
+ return {
33
+ menging: "vrij",
34
+ groepen: { klantenPerLaag: kl, beladingenPerLaag: bel },
35
+ reden: delen.length ? delen.join("; ") : "mengen per laag toegestaan",
36
+ };
37
+ }
38
+ /**
39
+ * 1.0.205: lagen op een kar volgens de mengregels.
40
+ * - "per_regel" (optimale vulling uit): Σ ceil(fust ÷ fusten per laag) per regel.
41
+ * - anders: regels vormen groepen die een laag mogen delen (zelfde klant; klanten in
42
+ * groepen van N; binnen een klant hoogstens M regels). Eén groep op de kar → fractie.
43
+ * Meer groepen → elke groep telt in hele lagen: Σ ceil(groep).
44
+ */
45
+ export function karLagen(regels, menging = "vrij", groepen) {
46
+ const f = (r) => r.crates / Math.max(1, r.cpl);
47
+ if (menging === "per_regel")
48
+ return regels.reduce((s, r) => s + Math.ceil(f(r) - 1e-9), 0);
49
+ const N = groepen?.klantenPerLaag ?? null;
50
+ const M = groepen?.beladingenPerLaag ?? null;
51
+ if (N == null && M == null)
52
+ return regels.reduce((s, r) => s + f(r), 0);
53
+ // Per klant, vaste volgorde.
54
+ const perKlant = new Map();
55
+ for (const r of regels) {
56
+ const k = r.klantId ?? `regel:${r.regelId}`;
57
+ const l = perKlant.get(k) ?? [];
58
+ l.push(r);
59
+ perKlant.set(k, l);
60
+ }
61
+ const klanten = [...perKlant.keys()].sort();
62
+ const units = [];
63
+ if (M != null) {
64
+ // Binnen een klant groepen van M regels; klanten delen niet als N gezet is.
65
+ for (const k of klanten) {
66
+ const l = perKlant.get(k).sort((a, b) => a.regelId.localeCompare(b.regelId));
67
+ for (let i = 0; i < l.length; i += M)
68
+ units.push(l.slice(i, i + M).reduce((s, r) => s + f(r), 0));
69
+ }
70
+ }
71
+ else {
72
+ for (let i = 0; i < klanten.length; i += N) {
73
+ units.push(klanten.slice(i, i + N).reduce((s, k) => s + perKlant.get(k).reduce((t, r) => t + f(r), 0), 0));
74
+ }
75
+ }
76
+ if (units.length <= 1)
77
+ return units.reduce((s, u) => s + u, 0);
78
+ return units.reduce((s, u) => s + Math.ceil(u - 1e-9), 0);
79
+ }
80
+ /** Lagen van één regel op een kar volgens de menging per laag. */
81
+ export function regelLagen(crates, cpl, menging = "vrij") {
82
+ const f = crates / Math.max(1, cpl);
83
+ return menging === "per_regel" ? Math.ceil(f - 1e-9) : f;
84
+ }
13
85
  const EPS = 1e-9;
14
86
  function clampInt(value, min) {
15
87
  const n = typeof value === "string" ? Number(value) : value;
16
88
  return Math.max(min, Math.round(Number.isFinite(n) ? n : min));
17
89
  }
18
- /** Effectieve lagen: het minimum van de productbelading en de ladingdrager. */
19
- function effectiveLayers(item, carrier) {
20
- const carrierLayers = Math.max(1, Math.round(carrier.layers_per_carrier || 1));
21
- if (carrier.capacity_model === "single" || carrier.capacity_model === "none")
22
- return 1;
23
- // Floriday-verpakking is leidend en wordt niet afgeknepen op het profiel.
24
- if (item.packing_authoritative)
25
- return Math.max(1, Math.round(item.layers_per_cart));
26
- return Math.max(1, Math.min(item.layers_per_cart, carrierLayers));
27
- }
28
90
  /** Effectieve fusten per laag: begrensd door de ladingdrager. */
29
91
  function effectiveCratesPerLayer(item, carrier) {
30
92
  const cap = carrier.max_crates_per_layer;
@@ -33,197 +95,6 @@ function effectiveCratesPerLayer(item, carrier) {
33
95
  return Math.max(1, Math.min(base, Math.round(cap)));
34
96
  return base;
35
97
  }
36
- // ---------------------------------------------------------------------------
37
- // Plaatsingsregels
38
- // ---------------------------------------------------------------------------
39
- function placeAsFreshLayer(cart, batch, batchIdx, crates) {
40
- if (cart.totalHeightPct + batch.layerHeightPct > 100 + EPS)
41
- return false;
42
- cart.layers.push({
43
- heightPct: batch.layerHeightPct,
44
- crateShare: crates / batch.cratesPerLayer,
45
- entries: [{ batchIdx, crates }],
46
- customerIds: new Set(batch.input.customer_id ? [batch.input.customer_id] : []),
47
- });
48
- cart.totalHeightPct += batch.layerHeightPct;
49
- return true;
50
- }
51
- function placeInLayer(cart, batch, batchIdx, crates, r) {
52
- const partyShare = crates / batch.cratesPerLayer;
53
- const maxShare = r.opts.maxPartySharePerLayerPct / 100;
54
- // REGEL 1 — één partij mag nooit meer dan X% van een laag beslaan
55
- if (partyShare > maxShare + EPS)
56
- return false;
57
- // REGEL 2 — never-mix producten en niet-mengbare ladingdragers
58
- const neverMix = r.opts.carrier.allow_mixing === false ||
59
- (batch.input.product_id ? r.opts.neverMixProductIds.has(batch.input.product_id) : false);
60
- if (neverMix)
61
- return false;
62
- for (const layer of cart.layers) {
63
- // REGEL 3 — max aantal verschillende beladingen per laag
64
- if (layer.entries.length >= r.opts.maxBeladingenPerLayer)
65
- continue;
66
- // REGEL 4 — dezelfde partij niet twee keer in dezelfde laag
67
- if (layer.entries.some((e) => e.batchIdx === batchIdx))
68
- continue;
69
- // REGEL 4b — maximaal aantal verschillende klanten op één laag
70
- const layerCustomerLimit = r.opts.oneCustomerPerLayer
71
- ? 1
72
- : r.opts.maxCustomersPerLayer && r.opts.maxCustomersPerLayer > 0
73
- ? r.opts.maxCustomersPerLayer
74
- : null;
75
- const customerId = batch.input.customer_id ?? null;
76
- if (layerCustomerLimit != null &&
77
- customerId &&
78
- !layer.customerIds.has(customerId) &&
79
- layer.customerIds.size >= layerCustomerLimit) {
80
- continue;
81
- }
82
- // REGEL 5 — vloeroppervlak van de laag mag niet over 100%
83
- if (layer.crateShare + partyShare > 1 + EPS)
84
- continue;
85
- // REGEL 6 — hoogte: bij mengen telt de HOOGSTE partij
86
- const newHeight = Math.max(layer.heightPct, batch.layerHeightPct);
87
- const heightDelta = newHeight - layer.heightPct;
88
- if (cart.totalHeightPct + heightDelta > 100 + EPS)
89
- continue;
90
- layer.entries.push({ batchIdx, crates });
91
- if (customerId)
92
- layer.customerIds.add(customerId);
93
- layer.crateShare += partyShare;
94
- layer.heightPct = newHeight;
95
- cart.totalHeightPct += heightDelta;
96
- return true;
97
- }
98
- return false;
99
- }
100
- /** Extra grenzen die niet over hoogte of oppervlak gaan. */
101
- function acceptsBatch(cart, batch, crates, r) {
102
- const carrier = r.opts.carrier;
103
- const orderId = batch.input.order_id ?? null;
104
- if (orderId && !cart.orderIds.has(orderId) && r.opts.maxOrders && cart.orderIds.size >= r.opts.maxOrders) {
105
- return false;
106
- }
107
- const customerId = batch.input.customer_id ?? null;
108
- if (customerId &&
109
- !cart.customerIds.has(customerId) &&
110
- r.opts.maxCustomers &&
111
- cart.customerIds.size >= r.opts.maxCustomers) {
112
- return false;
113
- }
114
- if (typeof carrier.max_boxes === "number" && carrier.max_boxes > 0) {
115
- if (cart.boxes + crates > carrier.max_boxes)
116
- return false;
117
- }
118
- if (typeof carrier.max_weight_kg === "number" && carrier.max_weight_kg > 0) {
119
- const perCrate = Number(batch.input.weight_per_crate ?? 0) || 0;
120
- if (perCrate > 0 && cart.weightKg + perCrate * crates > carrier.max_weight_kg + EPS)
121
- return false;
122
- }
123
- return true;
124
- }
125
- function registerPlacement(cart, batch, crates) {
126
- if (batch.input.order_id)
127
- cart.orderIds.add(batch.input.order_id);
128
- if (batch.input.customer_id)
129
- cart.customerIds.add(batch.input.customer_id);
130
- cart.boxes += crates;
131
- cart.weightKg += (Number(batch.input.weight_per_crate ?? 0) || 0) * crates;
132
- }
133
- function tryPlaceBatch(cart, batch, batchIdx, r) {
134
- const cpl = batch.cratesPerLayer;
135
- let placed = 0;
136
- let remaining = batch.cratesRemaining;
137
- // 1) zo veel mogelijk VOLLE eigen lagen
138
- while (remaining >= cpl && cart.totalHeightPct + batch.layerHeightPct <= 100 + EPS) {
139
- if (!acceptsBatch(cart, batch, cpl, r))
140
- break;
141
- if (!placeAsFreshLayer(cart, batch, batchIdx, cpl))
142
- break;
143
- registerPlacement(cart, batch, cpl);
144
- remaining -= cpl;
145
- placed += cpl;
146
- }
147
- // 2) het restant: eerst bijmengen, anders eigen (deels lege) laag
148
- if (remaining > 0) {
149
- const partial = Math.min(remaining, Math.ceil(cpl * 0.5));
150
- if (acceptsBatch(cart, batch, partial, r) && placeInLayer(cart, batch, batchIdx, partial, r)) {
151
- registerPlacement(cart, batch, partial);
152
- remaining -= partial;
153
- placed += partial;
154
- }
155
- else if (acceptsBatch(cart, batch, remaining, r) && placeAsFreshLayer(cart, batch, batchIdx, remaining)) {
156
- registerPlacement(cart, batch, remaining);
157
- placed += remaining;
158
- remaining = 0;
159
- }
160
- }
161
- return placed;
162
- }
163
- // ---------------------------------------------------------------------------
164
- // Samenvatten
165
- // ---------------------------------------------------------------------------
166
- function summarizeCart(cart, rests, cartNumber, r) {
167
- const byBatch = new Map();
168
- for (const layer of cart.layers) {
169
- const shareSum = layer.entries.reduce((s, e) => s + e.crates, 0);
170
- for (const e of layer.entries) {
171
- const cur = byBatch.get(e.batchIdx) ?? { crates: 0, layerFraction: 0, heightPct: 0 };
172
- const ratio = shareSum > 0 ? e.crates / shareSum : 0;
173
- cur.crates += e.crates;
174
- cur.layerFraction += ratio;
175
- cur.heightPct += layer.heightPct * ratio;
176
- byBatch.set(e.batchIdx, cur);
177
- }
178
- }
179
- const totalHeight = cart.totalHeightPct;
180
- const items = [];
181
- let weight = 0;
182
- for (const [batchIdx, agg] of byBatch) {
183
- const input = rests[batchIdx].input;
184
- const perCrate = Number(input.weight_per_crate ?? 0) || 0;
185
- weight += perCrate * agg.crates;
186
- items.push({
187
- order_item_id: input.order_item_id,
188
- order_id: input.order_id ?? null,
189
- product_id: input.product_id,
190
- product_name: input.product_name,
191
- crates: agg.crates,
192
- plants: agg.crates * input.plants_per_crate,
193
- boxes: agg.crates,
194
- weight_kg: perCrate > 0 ? Math.round(perCrate * agg.crates * 100) / 100 : null,
195
- plants_per_crate: input.plants_per_crate,
196
- crates_per_layer: rests[batchIdx].cratesPerLayer,
197
- layers_per_cart: Math.max(1, Math.round(100 / rests[batchIdx].layerHeightPct)),
198
- // 1.0.190: layer_fraction = vulling per laag (0–1); lagen = fusten ÷ fusten per laag.
199
- layer_fraction: Math.min(1, Math.round((agg.crates / Math.max(1, rests[batchIdx].cratesPerLayer)) * 1000) / 1000),
200
- lagen: Math.round((agg.crates / Math.max(1, rests[batchIdx].cratesPerLayer)) * 1000) / 1000,
201
- layer_share_pct: agg.heightPct,
202
- });
203
- }
204
- return {
205
- cart_number: cartNumber,
206
- carrier_type: r.opts.carrier.code,
207
- carrier_type_id: r.opts.carrier.id ?? null,
208
- source: "auto",
209
- is_complete: Math.abs(cart.totalHeightPct - 100) < 1e-6,
210
- occupancy_pct: Math.round(cart.totalHeightPct * 100) / 100,
211
- total_layers_used: cart.layers.length,
212
- max_layers: Math.max(1, Math.round(r.opts.carrier.layers_per_carrier || 1)),
213
- weight_kg: weight > 0 ? Math.round(weight * 100) / 100 : null,
214
- items,
215
- };
216
- }
217
- function newCart() {
218
- return {
219
- layers: [],
220
- totalHeightPct: 0,
221
- boxes: 0,
222
- weightKg: 0,
223
- orderIds: new Set(),
224
- customerIds: new Set(),
225
- };
226
- }
227
98
  function fullCart(input, crates, cratesPerLayer, layers, cartNumber, r) {
228
99
  const perCrate = Number(input.weight_per_crate ?? 0) || 0;
229
100
  return {
@@ -232,9 +103,9 @@ function fullCart(input, crates, cratesPerLayer, layers, cartNumber, r) {
232
103
  carrier_type_id: r.opts.carrier.id ?? null,
233
104
  source: "auto",
234
105
  is_complete: true,
235
- occupancy_pct: 100,
236
- total_layers_used: layers,
237
- max_layers: Math.max(1, Math.round(r.opts.carrier.layers_per_carrier || 1)),
106
+ occupancy_pct: Math.round((10000 * crates) / Math.max(1, cratesPerLayer * layers)) / 100,
107
+ total_layers_used: Math.round((crates / Math.max(1, cratesPerLayer)) * 100) / 100,
108
+ max_layers: layers,
238
109
  weight_kg: perCrate > 0 ? Math.round(perCrate * crates * 100) / 100 : null,
239
110
  items: [
240
111
  {
@@ -272,8 +143,170 @@ function sortInputs(items, sortOrder) {
272
143
  }
273
144
  }
274
145
  // ---------------------------------------------------------------------------
275
- // Hoofdfunctie
146
+ // 1.0.204: capaciteit per kar (zelfde regel als Floriday)
276
147
  // ---------------------------------------------------------------------------
148
+ // lagen op de kar = Σ (fust ÷ fusten per laag) per regel
149
+ // capaciteit = hoogste "lagen per kar" van de producten op de kar
150
+ // vol = lagen ≥ capaciteit (− 0,001)
151
+ // verdelen = vul een kar tot de capaciteit, rest naar de volgende kar
152
+ // Voegt een product met meer lagen per kar toe, dan stijgt de capaciteit.
153
+ // ---------------------------------------------------------------------------
154
+ export const LAGEN_MARGE = 0.001;
155
+ /** Capaciteit van een kar: hoogste lagen-per-kar van de producten; anders de ladingdrager. */
156
+ export function karCapaciteit(productLagen, ladingdragerLagen) {
157
+ const geldig = productLagen.map(Number).filter((n) => Number.isFinite(n) && n > 0);
158
+ if (geldig.length > 0)
159
+ return { capaciteit: Math.max(...geldig), bron: "product" };
160
+ const l = Number(ladingdragerLagen);
161
+ if (ladingdragerLagen != null && Number.isFinite(l) && l > 0)
162
+ return { capaciteit: l, bron: "ladingdrager" };
163
+ return { capaciteit: null, bron: null };
164
+ }
165
+ /** Vulgraad in procenten (heel getal), of null als de capaciteit onbekend is. */
166
+ export function karVulgraad(lagen, capaciteit) {
167
+ if (capaciteit == null || !(capaciteit > 0) || !Number.isFinite(lagen))
168
+ return null;
169
+ return Math.round((100 * lagen) / capaciteit);
170
+ }
171
+ function productLagen(item, carrier) {
172
+ if (carrier.capacity_model === "single")
173
+ return 1;
174
+ let l = Math.max(1, Math.round(item.layers_per_cart));
175
+ const grens = carrier.werkwijze_max_lagen;
176
+ if (typeof grens === "number" && grens > 0 && !item.packing_authoritative)
177
+ l = Math.min(l, Math.round(grens));
178
+ return l;
179
+ }
180
+ function accepteert(kar, rest, crates, r) {
181
+ const c = r.opts.carrier;
182
+ const orderId = rest.input.order_id ?? null;
183
+ if (orderId && !kar.orderIds.has(orderId) && r.opts.maxOrders && kar.orderIds.size >= r.opts.maxOrders)
184
+ return false;
185
+ const klant = rest.input.customer_id ?? null;
186
+ if (klant && !kar.customerIds.has(klant) && r.opts.maxCustomers && kar.customerIds.size >= r.opts.maxCustomers)
187
+ return false;
188
+ if (typeof c.max_boxes === "number" && c.max_boxes > 0 && kar.boxes + crates > c.max_boxes)
189
+ return false;
190
+ if (typeof c.max_weight_kg === "number" && c.max_weight_kg > 0) {
191
+ const pc = Number(rest.input.weight_per_crate ?? 0) || 0;
192
+ if (pc > 0 && kar.weightKg + pc * crates > c.max_weight_kg + EPS)
193
+ return false;
194
+ }
195
+ return true;
196
+ }
197
+ /** Hoeveel fusten van deze rest passen nog op de kar (0 = niets). */
198
+ /** Lagen van een kar volgens de mengregels, optioneel met extra fusten van een rest. */
199
+ function lagenVan(kar, rests, r, extra) {
200
+ const per = new Map();
201
+ for (const e of kar.entries)
202
+ per.set(e.restIdx, (per.get(e.restIdx) ?? 0) + e.crates);
203
+ if (extra)
204
+ per.set(extra.restIdx, (per.get(extra.restIdx) ?? 0) + extra.crates);
205
+ const regels = [...per.entries()].map(([i, crates]) => ({
206
+ regelId: rests[i].input.order_item_id,
207
+ klantId: rests[i].input.customer_id ?? rests[i].input.order_id ?? null,
208
+ crates,
209
+ cpl: rests[i].cpl,
210
+ }));
211
+ return karLagen(regels, r.opts.laagMenging, r.opts.laagGroepen);
212
+ }
213
+ function pastErop(kar, rest, r, rests, restIdx) {
214
+ const neverMix = r.opts.carrier.allow_mixing === false ||
215
+ (rest.input.product_id ? r.opts.neverMixProductIds.has(rest.input.product_id) : false);
216
+ if (kar.entries.length > 0 && (neverMix || kar.neverMix))
217
+ return 0;
218
+ const cap = Math.max(kar.capaciteit, rest.lpc);
219
+ const vrij = cap - kar.lagen;
220
+ if (vrij <= LAGEN_MARGE)
221
+ return 0;
222
+ let n = Math.min(rest.crates, Math.floor((vrij + LAGEN_MARGE) * rest.cpl));
223
+ const c = r.opts.carrier;
224
+ if (typeof c.max_boxes === "number" && c.max_boxes > 0)
225
+ n = Math.min(n, c.max_boxes - kar.boxes);
226
+ if (typeof c.max_weight_kg === "number" && c.max_weight_kg > 0) {
227
+ const pc = Number(rest.input.weight_per_crate ?? 0) || 0;
228
+ if (pc > 0)
229
+ n = Math.min(n, Math.floor((c.max_weight_kg - kar.weightKg + EPS) / pc));
230
+ }
231
+ // Mengregels: grootste n waarbij de kar binnen de capaciteit blijft (monotoon → binair zoeken).
232
+ const past = (m) => lagenVan(kar, rests, r, { restIdx, crates: m }) <= cap + LAGEN_MARGE;
233
+ if (n > 0 && !past(n)) {
234
+ let lo = 0, hi = n;
235
+ while (lo < hi) {
236
+ const mid = Math.ceil((lo + hi) / 2);
237
+ if (past(mid))
238
+ lo = mid;
239
+ else
240
+ hi = mid - 1;
241
+ }
242
+ n = lo;
243
+ }
244
+ if (n <= 0 || !accepteert(kar, rest, n, r))
245
+ return 0;
246
+ return n;
247
+ }
248
+ function zetErop(kar, rest, restIdx, crates, r, rests) {
249
+ kar.entries.push({ restIdx, crates });
250
+ kar.capaciteit = Math.max(kar.capaciteit, rest.lpc);
251
+ kar.boxes += crates;
252
+ kar.weightKg += (Number(rest.input.weight_per_crate ?? 0) || 0) * crates;
253
+ if (rest.input.order_id)
254
+ kar.orderIds.add(rest.input.order_id);
255
+ if (rest.input.customer_id)
256
+ kar.customerIds.add(rest.input.customer_id);
257
+ if (r.opts.carrier.allow_mixing === false ||
258
+ (rest.input.product_id ? r.opts.neverMixProductIds.has(rest.input.product_id) : false))
259
+ kar.neverMix = true;
260
+ rest.crates -= crates;
261
+ kar.lagen = lagenVan(kar, rests, r);
262
+ }
263
+ function nieuweKar() {
264
+ return { entries: [], lagen: 0, capaciteit: 0, boxes: 0, weightKg: 0, orderIds: new Set(), customerIds: new Set(), neverMix: false, eigen: false };
265
+ }
266
+ function samenvatten(kar, rests, nr, r) {
267
+ const per = new Map();
268
+ for (const e of kar.entries)
269
+ per.set(e.restIdx, (per.get(e.restIdx) ?? 0) + e.crates);
270
+ const items = [];
271
+ let weight = 0;
272
+ const sleutel = (i) => `${rests[i].input.product_name}\u0000${rests[i].input.order_item_id}`;
273
+ for (const [idx, crates] of [...per.entries()].sort((a, b) => sleutel(a[0]).localeCompare(sleutel(b[0])))) {
274
+ const rest = rests[idx];
275
+ const input = rest.input;
276
+ const pc = Number(input.weight_per_crate ?? 0) || 0;
277
+ weight += pc * crates;
278
+ const lagen = regelLagen(crates, rest.cpl, r.opts.laagMenging);
279
+ items.push({
280
+ order_item_id: input.order_item_id,
281
+ order_id: input.order_id ?? null,
282
+ product_id: input.product_id,
283
+ product_name: input.product_name,
284
+ crates,
285
+ plants: crates * input.plants_per_crate,
286
+ boxes: crates,
287
+ weight_kg: pc > 0 ? Math.round(pc * crates * 100) / 100 : null,
288
+ plants_per_crate: input.plants_per_crate,
289
+ crates_per_layer: rest.cpl,
290
+ layers_per_cart: rest.lpc,
291
+ layer_fraction: Math.min(1, Math.round(lagen * 1000) / 1000),
292
+ lagen: Math.round(lagen * 1000) / 1000,
293
+ layer_share_pct: Math.round((10000 * lagen) / kar.capaciteit) / 100,
294
+ });
295
+ }
296
+ const lagen = Math.round(kar.lagen * 100) / 100;
297
+ return {
298
+ cart_number: nr,
299
+ carrier_type: r.opts.carrier.code,
300
+ carrier_type_id: r.opts.carrier.id ?? null,
301
+ source: "auto",
302
+ is_complete: kar.lagen >= kar.capaciteit - LAGEN_MARGE,
303
+ occupancy_pct: Math.round((10000 * kar.lagen) / kar.capaciteit) / 100,
304
+ total_layers_used: lagen,
305
+ max_layers: kar.capaciteit,
306
+ weight_kg: weight > 0 ? Math.round(weight * 100) / 100 : null,
307
+ items,
308
+ };
309
+ }
277
310
  export function planLoadUnits(inputs, options) {
278
311
  const carrier = options.carrier;
279
312
  const r = {
@@ -288,6 +321,8 @@ export function planLoadUnits(inputs, options) {
288
321
  oneCustomerPerLayer: options.oneCustomerPerLayer ?? false,
289
322
  ownCarrierFromLayers: options.ownCarrierFromLayers ?? null,
290
323
  sortOrder: options.sortOrder ?? "none",
324
+ laagMenging: options.laagMenging ?? "vrij",
325
+ laagGroepen: options.laagGroepen ?? null,
291
326
  },
292
327
  };
293
328
  const warnings = [];
@@ -301,7 +336,6 @@ export function planLoadUnits(inputs, options) {
301
336
  layers_per_cart: clampInt(i.layers_per_cart, 1),
302
337
  }))
303
338
  .filter((i) => i.plants > 0), r.opts.sortOrder);
304
- // --- Model "none": elke partij is één losse eenheid, geen menging ---------
305
339
  if (carrier.capacity_model === "none") {
306
340
  let n = 0;
307
341
  for (const it of items) {
@@ -310,91 +344,70 @@ export function planLoadUnits(inputs, options) {
310
344
  }
311
345
  return { carts, warnings };
312
346
  }
347
+ // STAP A: volle karren per product (capaciteit = lagen per kar van dat product).
313
348
  const rests = [];
314
- let cartNumber = 0;
315
- // --- STAP A: volledige ladingdragers per artikel --------------------------
316
349
  for (const it of items) {
317
- const layers = effectiveLayers(it, carrier);
350
+ const lpc = productLagen(it, carrier);
318
351
  const cpl = effectiveCratesPerLayer(it, carrier);
319
- let cratesPerCart = cpl * layers;
320
- if (typeof carrier.max_boxes === "number" && carrier.max_boxes > 0) {
321
- cratesPerCart = Math.min(cratesPerCart, carrier.max_boxes);
322
- }
323
- const totalCrates = Math.ceil(it.plants / it.plants_per_crate);
324
- const fullCarts = Math.floor(totalCrates / cratesPerCart);
325
- let remainderCrates = totalCrates - fullCarts * cratesPerCart;
326
- for (let c = 0; c < fullCarts; c++) {
327
- carts.push(fullCart(it, cratesPerCart, cpl, layers, ++cartNumber, r));
328
- }
329
- // Partij die zelfstandig genoeg volle lagen vult krijgt een eigen drager.
330
- // 1.0.190: die drager blijft open voor restfusten (hele fusten, eigen laag
331
- // of bijmengen volgens de laagregels), zodat er geen extra kar ontstaat.
332
- const own = r.opts.ownCarrierFromLayers;
333
- const ownCarrier = Boolean(own && own > 0 && remainderCrates >= cpl * (own ?? 0));
334
- if (remainderCrates > 0) {
335
- rests.push({
336
- input: it,
337
- cratesRemaining: remainderCrates,
338
- layerHeightPct: 100 / layers,
339
- cratesPerLayer: cpl,
340
- beladingKey: `${it.plants_per_crate}x${cpl}x${layers}`,
341
- ownCarrier,
342
- });
352
+ let perKar = cpl * lpc;
353
+ if (typeof carrier.max_boxes === "number" && carrier.max_boxes > 0)
354
+ perKar = Math.min(perKar, carrier.max_boxes);
355
+ const totaal = Math.ceil(it.plants / it.plants_per_crate);
356
+ const vol = Math.floor(totaal / perKar);
357
+ for (let c = 0; c < vol; c++)
358
+ carts.push(fullCart(it, perKar, cpl, lpc, carts.length + 1, r));
359
+ const rest = totaal - vol * perKar;
360
+ if (rest > 0) {
361
+ const own = r.opts.ownCarrierFromLayers;
362
+ rests.push({ input: it, crates: rest, cpl, lpc, ownCarrier: Boolean(own && own > 0 && rest >= cpl * own) });
343
363
  }
344
364
  }
345
- // --- Sorteren van de restpartijen: eigen dragers eerst, dan first-fit decreasing
346
- rests.sort((a, b) => {
347
- if (Boolean(a.ownCarrier) !== Boolean(b.ownCarrier))
348
- return a.ownCarrier ? -1 : 1;
349
- if (a.beladingKey !== b.beladingKey)
350
- return a.beladingKey.localeCompare(b.beladingKey);
351
- return b.cratesRemaining - a.cratesRemaining;
365
+ // STAP B: restpartijen. Vaste volgorde (deterministisch): eigen drager eerst,
366
+ // dan hoogste capaciteit, dan meeste lagen, dan productnaam en regel-id.
367
+ const volgorde = rests
368
+ .map((rest, idx) => ({ rest, idx }))
369
+ .sort((a, b) => {
370
+ if (a.rest.ownCarrier !== b.rest.ownCarrier)
371
+ return a.rest.ownCarrier ? -1 : 1;
372
+ if (a.rest.lpc !== b.rest.lpc)
373
+ return b.rest.lpc - a.rest.lpc;
374
+ const la = a.rest.crates / a.rest.cpl, lb = b.rest.crates / b.rest.cpl;
375
+ if (Math.abs(la - lb) > 1e-9)
376
+ return lb - la;
377
+ return (a.rest.input.product_name.localeCompare(b.rest.input.product_name) ||
378
+ a.rest.input.order_item_id.localeCompare(b.rest.input.order_item_id));
352
379
  });
353
- // --- STAP B: de hoofdlus --------------------------------------------------
354
- const openCarts = [];
355
- for (let bi = 0; bi < rests.length; bi++) {
356
- const batch = rests[bi];
380
+ const open = [];
381
+ for (const { rest, idx } of volgorde) {
357
382
  let guard = 0;
358
- let eigenDrager = null;
359
- while (batch.cratesRemaining > 0 && guard++ < 1000) {
360
- let placed = 0;
361
- // Eigen drager: op een nieuwe drager beginnen en daar blijven (niet bij een ander inschuiven).
362
- const kandidaten = batch.ownCarrier ? (eigenDrager ? [eigenDrager] : []) : openCarts;
363
- for (const cart of kandidaten) {
364
- placed = tryPlaceBatch(cart, batch, bi, r);
365
- if (placed > 0) {
366
- batch.cratesRemaining -= placed;
383
+ while (rest.crates > 0 && guard++ < 10000) {
384
+ let geplaatst = false;
385
+ const kandidaten = rest.ownCarrier ? [] : open;
386
+ for (const kar of kandidaten) {
387
+ const n = pastErop(kar, rest, r, rests, idx);
388
+ if (n > 0) {
389
+ zetErop(kar, rest, idx, n, r, rests);
390
+ geplaatst = true;
367
391
  break;
368
392
  }
369
393
  }
370
- if (placed === 0) {
371
- const cart = newCart();
372
- placed = tryPlaceBatch(cart, batch, bi, r);
373
- if (placed === 0) {
374
- warnings.push(`Partij "${batch.input.product_name}" past niet volgens de veiligheidsregels van ${carrier.name} — geplaatst als losse ladingdrager.`);
375
- cart.layers.push({
376
- heightPct: 100,
377
- crateShare: 1,
378
- entries: [{ batchIdx: bi, crates: batch.cratesRemaining }],
379
- customerIds: new Set(batch.input.customer_id ? [batch.input.customer_id] : []),
380
- });
381
- cart.totalHeightPct = 100;
382
- registerPlacement(cart, batch, batch.cratesRemaining);
383
- placed = batch.cratesRemaining;
384
- }
385
- openCarts.push(cart);
386
- if (batch.ownCarrier && !eigenDrager)
387
- eigenDrager = cart;
388
- batch.cratesRemaining -= placed;
394
+ if (geplaatst)
395
+ continue;
396
+ const kar = nieuweKar();
397
+ kar.eigen = rest.ownCarrier;
398
+ const n = pastErop(kar, rest, r, rests, idx);
399
+ if (n > 0) {
400
+ zetErop(kar, rest, idx, n, r, rests);
389
401
  }
402
+ else {
403
+ warnings.push(`Partij "${rest.input.product_name}" past niet volgens de grenzen van ${carrier.name} — geplaatst als losse ladingdrager.`);
404
+ zetErop(kar, rest, idx, rest.crates, r, rests);
405
+ }
406
+ open.push(kar);
390
407
  }
391
408
  }
392
- for (const cart of openCarts) {
393
- carts.push(summarizeCart(cart, rests, ++cartNumber, r));
394
- }
395
- // Doornummeren zodat de volgorde altijd 1..n is.
396
- carts.forEach((c, idx) => {
397
- c.cart_number = idx + 1;
398
- });
409
+ for (const kar of open)
410
+ carts.push(samenvatten(kar, rests, carts.length + 1, r));
411
+ carts.forEach((c, i) => { c.cart_number = i + 1; });
399
412
  return { carts, warnings };
400
413
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loadCarrierPlanning.server.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierPlanning.server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAiE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGvH,OAAO,EAWL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAY1B,wBAAsB,6BAA6B,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwB/G;AAMD,UAAU,QAAQ;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,aAAa,GACb,SAAS,GACT,MAAM,CAAC;AAgBX;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAQ7E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAazF;AAaD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,wGAAwG;IACxG,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,gEAAgE;IAChE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACxB,OAAO,EAAE,QAAQ,GAAG,IAAI,EACxB,WAAW,GAAE,YAAY,GAAG,IAAW,GACtC,gBAAgB,CA+BlB;AAGD,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IAC5D,kEAAkE;IAClE,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IACzC,iHAAiH;IACjH,gBAAgB,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,qEAAqE;IACrE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C;;;OAGG;IACH,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,iEAAiE;IACjE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACtC,gGAAgG;IAChG,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1E,kFAAkF;IAClF,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjF;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,YAAY,EAAE,CAAA;CAAE,GAAG,SAAS,EAClE,YAAY,EAAE,MAAM,GACnB,YAAY,EAAE,GAAG,IAAI,CAmBvB;AAaD;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAChC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA0I7B;AAqID,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AA6BD,oGAAoG;AACpG,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAQ9F;AAMD,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAO3F;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAcxD,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAMnG;AA8FD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,8CAA8C;IAC9C,YAAY,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,sGAAsG;IACtG,wBAAwB,EAAE,OAAO,CAAC;IAClC,4FAA4F;IAC5F,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAOD,wFAAwF;AACxF,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA0BzF;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,cAAc,GACjB,OAAO,CAAC,UAAU,GAAG,oBAAoB,CAAC,CAW5C;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,cAAc,CAaxG;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,QAAQ,GAChB,eAAe,EAAE,CAkEnB;AAUD,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAK1D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,GAClB,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA4BhC;AAyCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,eAAe,EAAE,CAwCtF;AAwED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,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,2GAA2G;IAC3G,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACxC,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;CAC3B;AAGD,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAGD,UAAU,qBAAqB;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,eAAe,EAAE,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,eAAe,GAAE,mBAAwB,GACxC,qBAAqB,EAAE,CAoCzB;AAqBD;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCnB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAyCvB;AAED,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,gFAC+C,CAAC;AA8/ChF,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAI9B"}
1
+ {"version":3,"file":"loadCarrierPlanning.server.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierPlanning.server.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EAEtB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAErF,OAAO,EAAiE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGvH,OAAO,EAWL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAY1B,wBAAsB,6BAA6B,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwB/G;AAMD,UAAU,QAAQ;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,eAAe,GACf,kBAAkB,GAClB,aAAa,GACb,SAAS,GACT,MAAM,CAAC;AAgBX;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAQ7E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAazF;AAaD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,wGAAwG;IACxG,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,gEAAgE;IAChE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACxB,OAAO,EAAE,QAAQ,GAAG,IAAI,EACxB,WAAW,GAAE,YAAY,GAAG,IAAW,GACtC,gBAAgB,CA+BlB;AAGD,MAAM,WAAW,aAAa;IAC5B,mFAAmF;IACnF,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IAC5D,kEAAkE;IAClE,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IACzC,iHAAiH;IACjH,gBAAgB,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,qEAAqE;IACrE,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C;;;OAGG;IACH,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,iEAAiE;IACjE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACtC,gGAAgG;IAChG,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1E,kFAAkF;IAClF,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjF;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,YAAY,EAAE,CAAA;CAAE,GAAG,SAAS,EAClE,YAAY,EAAE,MAAM,GACnB,YAAY,EAAE,GAAG,IAAI,CAmBvB;AAaD;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAChC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA0I7B;AAqID,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AA6BD,oGAAoG;AACpG,wBAAgB,eAAe,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAQ9F;AAMD,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAO3F;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAcxD,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAMnG;AA8FD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,8CAA8C;IAC9C,YAAY,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,sGAAsG;IACtG,wBAAwB,EAAE,OAAO,CAAC;IAClC,4FAA4F;IAC5F,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAOD,wFAAwF;AACxF,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA0BzF;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,cAAc,GACjB,OAAO,CAAC,UAAU,GAAG,oBAAoB,CAAC,CAW5C;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,cAAc,CAcxG;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,QAAQ,GAChB,eAAe,EAAE,CAkEnB;AAUD,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAK1D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,GAClB,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA4BhC;AAyCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,eAAe,EAAE,CAwCtF;AAwED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,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,2GAA2G;IAC3G,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACxC,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;CAC3B;AAGD,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAGD,UAAU,qBAAqB;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,eAAe,EAAE,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,eAAe,GAAE,mBAAwB,GACxC,qBAAqB,EAAE,CAoCzB;AAqBD;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCnB;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAyCvB;AAED,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,gFAC+C,CAAC;AAygDhF,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAI9B"}