@flowselections/floriday-afhandeling-module 1.0.25 → 1.0.27
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/afhandeling/CapacityWarning.d.ts +10 -0
- package/dist-lib/components/afhandeling/CapacityWarning.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/CapacityWarning.js +12 -0
- package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControlePage.js +7 -2
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +158 -62
- package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/LeveringenPage.js +2 -10
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickAgendaView.js +4 -11
- package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerdersPage.js +2 -5
- package/dist-lib/components/afhandeling/VerzetOrderDialog.d.ts +14 -0
- package/dist-lib/components/afhandeling/VerzetOrderDialog.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/VerzetOrderDialog.js +203 -0
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +7 -3
- package/dist-lib/components/pickstation/PickstationPage.d.ts.map +1 -1
- package/dist-lib/components/pickstation/PickstationPage.js +1 -0
- package/dist-lib/components/settings/VervoerdersSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/VervoerdersSettingsCard.js +2 -5
- package/dist-lib/hooks/useCapacityCheck.d.ts +10 -0
- package/dist-lib/hooks/useCapacityCheck.d.ts.map +1 -0
- package/dist-lib/hooks/useCapacityCheck.js +22 -0
- package/dist-lib/hooks/useEngineTasks.d.ts +14 -0
- package/dist-lib/hooks/useEngineTasks.d.ts.map +1 -0
- package/dist-lib/hooks/useEngineTasks.js +49 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts +8 -2
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderPlanning.js +95 -93
- package/dist-lib/index.d.ts +0 -3
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -5
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +9010 -5433
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +9010 -5433
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +9010 -5433
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +9184 -5544
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +7186 -32
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/carriers.functions.d.ts +17965 -80
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.d.ts +17965 -80
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.d.ts +35962 -170
- package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.js +103 -13
- package/dist-lib/lib/floriday.functions.d.ts +9010 -5433
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/logisticsEngine/capacity.d.ts +19 -0
- package/dist-lib/lib/logisticsEngine/capacity.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/capacity.js +91 -0
- package/dist-lib/lib/logisticsEngine/engine.d.ts +99 -0
- package/dist-lib/lib/logisticsEngine/engine.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/engine.js +102 -0
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts +65 -0
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/reschedule.js +86 -0
- package/dist-lib/lib/logisticsEngine/staffPlanning.d.ts +37 -0
- package/dist-lib/lib/logisticsEngine/staffPlanning.d.ts.map +1 -0
- package/dist-lib/lib/logisticsEngine/staffPlanning.js +100 -0
- package/dist-lib/lib/serverFnError.d.ts +10 -0
- package/dist-lib/lib/serverFnError.d.ts.map +1 -0
- package/dist-lib/lib/serverFnError.js +29 -0
- package/dist-lib/lib/serverRoutes/index.d.ts +0 -1
- package/dist-lib/lib/serverRoutes/index.d.ts.map +1 -1
- package/dist-lib/lib/serverRoutes/index.js +0 -1
- package/dist-lib/lib/timezone.d.ts +27 -0
- package/dist-lib/lib/timezone.d.ts.map +1 -0
- package/dist-lib/lib/timezone.js +68 -0
- package/package.json +6 -21
- package/dist-lib/components/engine/CapaciteitsmaatregelenPage.d.ts +0 -2
- package/dist-lib/components/engine/CapaciteitsmaatregelenPage.d.ts.map +0 -1
- package/dist-lib/components/engine/CapaciteitsmaatregelenPage.js +0 -40
- package/dist-lib/components/engine/CapaciteitsmonitorPage.d.ts +0 -2
- package/dist-lib/components/engine/CapaciteitsmonitorPage.d.ts.map +0 -1
- package/dist-lib/components/engine/CapaciteitsmonitorPage.js +0 -49
- package/dist-lib/components/engine/FormulesPage.d.ts +0 -2
- package/dist-lib/components/engine/FormulesPage.d.ts.map +0 -1
- package/dist-lib/components/engine/FormulesPage.js +0 -252
- package/dist-lib/components/engine/PlaceholderPage.d.ts +0 -6
- package/dist-lib/components/engine/PlaceholderPage.d.ts.map +0 -1
- package/dist-lib/components/engine/PlaceholderPage.js +0 -6
- package/dist-lib/components/engine/StrategieenPage.d.ts +0 -2
- package/dist-lib/components/engine/StrategieenPage.d.ts.map +0 -1
- package/dist-lib/components/engine/StrategieenPage.js +0 -80
- package/dist-lib/lib/logisticsEngine/capacity.functions.d.ts +0 -64978
- package/dist-lib/lib/logisticsEngine/capacity.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/capacity.functions.js +0 -90
- package/dist-lib/lib/logisticsEngine/config.d.ts +0 -140
- package/dist-lib/lib/logisticsEngine/config.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/config.js +0 -488
- package/dist-lib/lib/logisticsEngine/enginePickTime.d.ts +0 -29
- package/dist-lib/lib/logisticsEngine/enginePickTime.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/enginePickTime.js +0 -136
- package/dist-lib/lib/logisticsEngine/evaluate.functions.d.ts +0 -16278
- package/dist-lib/lib/logisticsEngine/evaluate.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/evaluate.functions.js +0 -104
- package/dist-lib/lib/logisticsEngine/expression.d.ts +0 -21
- package/dist-lib/lib/logisticsEngine/expression.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/expression.js +0 -263
- package/dist-lib/lib/logisticsEngine/formulas.functions.d.ts +0 -32512
- package/dist-lib/lib/logisticsEngine/formulas.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/formulas.functions.js +0 -58
- package/dist-lib/lib/logisticsEngine/safetyLayer.d.ts +0 -83
- package/dist-lib/lib/logisticsEngine/safetyLayer.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/safetyLayer.js +0 -182
- package/dist-lib/lib/logisticsEngine/strategies.functions.d.ts +0 -129961
- package/dist-lib/lib/logisticsEngine/strategies.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/strategies.functions.js +0 -176
- package/dist-lib/lib/logisticsEngine/variables.d.ts +0 -29
- package/dist-lib/lib/logisticsEngine/variables.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/variables.js +0 -144
- package/dist-lib/lib/logisticsEngine/wizard.functions.d.ts +0 -64976
- package/dist-lib/lib/logisticsEngine/wizard.functions.d.ts.map +0 -1
- package/dist-lib/lib/logisticsEngine/wizard.functions.js +0 -175
- package/dist-lib/lib/serverRoutes/logisticsTick.handler.d.ts +0 -3
- package/dist-lib/lib/serverRoutes/logisticsTick.handler.d.ts.map +0 -1
- package/dist-lib/lib/serverRoutes/logisticsTick.handler.js +0 -42
|
@@ -1,488 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wizard-configuratie voor de Logistieke Configurator.
|
|
3
|
-
* De gebruiker beantwoordt vragen in gewone taal; deze module vertaalt
|
|
4
|
-
* de antwoorden naar de onderliggende formules per slot.
|
|
5
|
-
*/
|
|
6
|
-
export const CONTINUOUS_PRESET = {
|
|
7
|
-
enabled: true,
|
|
8
|
-
normalPickHours: 2,
|
|
9
|
-
allowEarlier: true,
|
|
10
|
-
maxEarlierHours: 2,
|
|
11
|
-
incompleteCartsMode: "after-registration",
|
|
12
|
-
ordersAfterRegistration: "next-round",
|
|
13
|
-
keepStationFilled: true,
|
|
14
|
-
evenWorkload: true,
|
|
15
|
-
};
|
|
16
|
-
export const DEFAULT_CONFIG = {
|
|
17
|
-
orderMode: "per-cart",
|
|
18
|
-
workingSchedule: {
|
|
19
|
-
weekdays: [1, 2, 3, 4, 5],
|
|
20
|
-
startTime: "07:00",
|
|
21
|
-
endTime: "16:00",
|
|
22
|
-
breaks: [{ start: "12:00", end: "12:30", label: "Lunch" }],
|
|
23
|
-
},
|
|
24
|
-
combine: {
|
|
25
|
-
mode: "auction",
|
|
26
|
-
maxLayersOwnCart: 3,
|
|
27
|
-
maxCustomersPerCart: 5,
|
|
28
|
-
sameCarrier: true,
|
|
29
|
-
sameRoute: true,
|
|
30
|
-
sameLocation: true,
|
|
31
|
-
sameCrateType: true,
|
|
32
|
-
sameDeliveryDate: true,
|
|
33
|
-
customersPerLayer: "multi",
|
|
34
|
-
},
|
|
35
|
-
cartFull: {
|
|
36
|
-
mode: "auto",
|
|
37
|
-
fillPct: 98,
|
|
38
|
-
maxLayers: 5,
|
|
39
|
-
maxWeight: 350,
|
|
40
|
-
customExpression: "cartFillPct >= 100 OR cartWeight >= cartMaxWeight",
|
|
41
|
-
},
|
|
42
|
-
cartClose: {
|
|
43
|
-
whenFull: true,
|
|
44
|
-
whenCapacityReached: true,
|
|
45
|
-
when30minLeft: true,
|
|
46
|
-
whenTransportRegistered: true,
|
|
47
|
-
onlyManual: false,
|
|
48
|
-
},
|
|
49
|
-
documents: {
|
|
50
|
-
packingSlip: "cart-full",
|
|
51
|
-
deliveryNote: "floriday",
|
|
52
|
-
},
|
|
53
|
-
pickPlanningRelease: {
|
|
54
|
-
mode: "latest",
|
|
55
|
-
hoursBeforeDeparture: 2,
|
|
56
|
-
fixedTime: "07:00",
|
|
57
|
-
daysBeforeDelivery: 1,
|
|
58
|
-
safetyBufferMinutes: 30,
|
|
59
|
-
customExpression: "logistiekeDruk >= sluitDrempel",
|
|
60
|
-
hoursBeforeSignupWindow: 4,
|
|
61
|
-
earlyPickHours: 2,
|
|
62
|
-
incompleteCartsBeforeSignup: false,
|
|
63
|
-
safetyBufferMode: "minutes",
|
|
64
|
-
safetyBufferPercent: 20,
|
|
65
|
-
},
|
|
66
|
-
pickstationVisibility: {
|
|
67
|
-
mode: "auto",
|
|
68
|
-
logistics: {
|
|
69
|
-
batchClosed: true,
|
|
70
|
-
cartBuilt: true,
|
|
71
|
-
cartFull: true,
|
|
72
|
-
plannerApproved: false,
|
|
73
|
-
capacityReleased: false,
|
|
74
|
-
},
|
|
75
|
-
documents: {
|
|
76
|
-
packingSlip: true,
|
|
77
|
-
deliveryNote: false,
|
|
78
|
-
transportLabel: false,
|
|
79
|
-
},
|
|
80
|
-
control: "none",
|
|
81
|
-
},
|
|
82
|
-
startMoment: {
|
|
83
|
-
autoWhenCapacity: true,
|
|
84
|
-
safetyBufferMinutes: 30,
|
|
85
|
-
capacityMode: "carts-per-hour",
|
|
86
|
-
pickCapacityPerHour: 10,
|
|
87
|
-
minutesPerCart: 6,
|
|
88
|
-
},
|
|
89
|
-
continuous: {
|
|
90
|
-
enabled: false,
|
|
91
|
-
normalPickHours: 2,
|
|
92
|
-
allowEarlier: true,
|
|
93
|
-
maxEarlierHours: 2,
|
|
94
|
-
incompleteCartsMode: "after-registration",
|
|
95
|
-
ordersAfterRegistration: "next-round",
|
|
96
|
-
keepStationFilled: true,
|
|
97
|
-
evenWorkload: true,
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
/** Slot-key gebruikt om de wizard-JSON zelf op te slaan. */
|
|
101
|
-
export const CONFIG_SLOT = "_wizard_config";
|
|
102
|
-
/** Vertaal wizard-config naar formule-expressies per slot. */
|
|
103
|
-
export function compileConfig(c) {
|
|
104
|
-
const out = [];
|
|
105
|
-
// Stap 2 — Beschikbare capaciteit
|
|
106
|
-
const cartsPerHour = c.startMoment.capacityMode === "minutes-per-cart"
|
|
107
|
-
? (c.startMoment.minutesPerCart > 0 ? 60 / c.startMoment.minutesPerCart : 0)
|
|
108
|
-
: c.startMoment.pickCapacityPerHour;
|
|
109
|
-
out.push({
|
|
110
|
-
slot: "beschikbare_capaciteit",
|
|
111
|
-
expression: `nogBeschikbarePicktijd * ${Number(cartsPerHour.toFixed(2))}`,
|
|
112
|
-
});
|
|
113
|
-
// Stap 3 — Benodigde capaciteit (standaard-formule)
|
|
114
|
-
out.push({
|
|
115
|
-
slot: "benodigde_capaciteit",
|
|
116
|
-
expression: `volleKarren + verwachteOpenKarren`,
|
|
117
|
-
});
|
|
118
|
-
// Stap 4 — Logistieke druk
|
|
119
|
-
out.push({
|
|
120
|
-
slot: "logistieke_druk",
|
|
121
|
-
expression: `benodigdeCapaciteit / beschikbareCapaciteit`,
|
|
122
|
-
});
|
|
123
|
-
// Stap 5 — Sluit batch (op basis van kar-close keuzes)
|
|
124
|
-
const closeParts = [];
|
|
125
|
-
if (c.cartClose.whenCapacityReached)
|
|
126
|
-
closeParts.push("logistiekeDruk >= sluitDrempel");
|
|
127
|
-
if (c.cartClose.when30minLeft)
|
|
128
|
-
closeParts.push("nogBeschikbarePicktijd <= 0.5");
|
|
129
|
-
if (c.cartClose.whenTransportRegistered)
|
|
130
|
-
closeParts.push("karAangemeldVoorTransport == true");
|
|
131
|
-
if (c.cartClose.onlyManual || closeParts.length === 0) {
|
|
132
|
-
out.push({ slot: "sluit_batch", expression: "false" });
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
out.push({ slot: "sluit_batch", expression: closeParts.join(" OR ") });
|
|
136
|
-
}
|
|
137
|
-
// Stap 6 — Startmoment
|
|
138
|
-
const buffer = Math.max(0, c.startMoment.safetyBufferMinutes | 0);
|
|
139
|
-
out.push({ slot: "start_moment", expression: `leadtimeMinutes + ${buffer}` });
|
|
140
|
-
// Stap 7 — Mag combineren
|
|
141
|
-
let combineExpr = "false";
|
|
142
|
-
if (c.combine.mode !== "none") {
|
|
143
|
-
const parts = [];
|
|
144
|
-
if (c.combine.mode === "auction")
|
|
145
|
-
parts.push(`orderType == "veiling"`);
|
|
146
|
-
else if (c.combine.mode === "bvo")
|
|
147
|
-
parts.push(`orderType == "bvo"`);
|
|
148
|
-
// "all" → geen filter op ordertype
|
|
149
|
-
parts.push(`layerCount <= ${c.combine.maxLayersOwnCart}`);
|
|
150
|
-
parts.push(`customerCount <= ${c.combine.maxCustomersPerCart}`);
|
|
151
|
-
if (c.combine.customersPerLayer === "single")
|
|
152
|
-
parts.push(`customerCount <= layerCount`);
|
|
153
|
-
combineExpr = parts.join(" AND ");
|
|
154
|
-
}
|
|
155
|
-
out.push({ slot: "may_combine", expression: combineExpr });
|
|
156
|
-
// Stap 8 — Kar is vol
|
|
157
|
-
let fullExpr = "";
|
|
158
|
-
switch (c.cartFull.mode) {
|
|
159
|
-
case "auto":
|
|
160
|
-
// Auto-fit: kar sluit pas als geen laag/fust/combi meer past binnen alle regels.
|
|
161
|
-
fullExpr = "cartFillPct >= 100 OR cartWeight >= cartMaxWeight OR layerCount >= cartMaxLayers";
|
|
162
|
-
break;
|
|
163
|
-
case "pct":
|
|
164
|
-
fullExpr = `cartFillPct >= ${c.cartFull.fillPct}`;
|
|
165
|
-
break;
|
|
166
|
-
case "layers":
|
|
167
|
-
fullExpr = `layerCount >= ${c.cartFull.maxLayers}`;
|
|
168
|
-
break;
|
|
169
|
-
case "weight":
|
|
170
|
-
fullExpr = `cartWeight >= ${c.cartFull.maxWeight}`;
|
|
171
|
-
break;
|
|
172
|
-
case "custom":
|
|
173
|
-
fullExpr = c.cartFull.customExpression || "cartFillPct >= 100";
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
out.push({ slot: "cart_full", expression: fullExpr });
|
|
177
|
-
// Stap 6 — Vrijgave aan de pickplanning
|
|
178
|
-
const pp = c.pickPlanningRelease;
|
|
179
|
-
let releaseExpr;
|
|
180
|
-
switch (pp.mode) {
|
|
181
|
-
case "latest":
|
|
182
|
-
releaseExpr = `nogBeschikbarePicktijd <= (benodigdePicktijd + ${Math.max(0, pp.safetyBufferMinutes | 0)} / 60)`;
|
|
183
|
-
break;
|
|
184
|
-
case "hours-before-departure":
|
|
185
|
-
releaseExpr = `urenTotVertrek <= ${Math.max(0, pp.hoursBeforeDeparture)}`;
|
|
186
|
-
break;
|
|
187
|
-
case "between-signup-window": {
|
|
188
|
-
const base = `werkurenTotAanmeldmoment <= ${Math.max(0, pp.hoursBeforeSignupWindow)} AND werkurenTotAanmeldmoment >= 0`;
|
|
189
|
-
const busy = `capaciteitstekort AND werkurenTotAanmeldmoment <= ${Math.max(0, pp.hoursBeforeSignupWindow + pp.earlyPickHours)}`;
|
|
190
|
-
const incomplete = pp.incompleteCartsBeforeSignup ? "" : " AND (cartFull OR werkurenTotAanmeldmoment <= 0)";
|
|
191
|
-
releaseExpr = `(${base} OR ${busy})${incomplete}`;
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case "fixed-time":
|
|
195
|
-
releaseExpr = `huidigeTijd >= "${pp.fixedTime}"`;
|
|
196
|
-
break;
|
|
197
|
-
case "days-before-delivery":
|
|
198
|
-
releaseExpr = `werkdagenTotLevering <= ${Math.max(0, pp.daysBeforeDelivery | 0)}`;
|
|
199
|
-
break;
|
|
200
|
-
case "manual":
|
|
201
|
-
releaseExpr = "plannerManualRelease";
|
|
202
|
-
break;
|
|
203
|
-
case "custom":
|
|
204
|
-
releaseExpr = pp.customExpression || "false";
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
out.push({ slot: "pickplanning_release", expression: releaseExpr });
|
|
208
|
-
// Stap 10 — Kar zichtbaar op Pickstation (Kar gereed voor picken)
|
|
209
|
-
const pv = c.pickstationVisibility;
|
|
210
|
-
let visExpr;
|
|
211
|
-
if (pv.mode === "auto") {
|
|
212
|
-
// Aanbevolen: alle standaardvoorwaarden.
|
|
213
|
-
visExpr = [
|
|
214
|
-
"batchClosed",
|
|
215
|
-
"cartBuilt",
|
|
216
|
-
"cartFull",
|
|
217
|
-
"packingSlipReady",
|
|
218
|
-
"(deliveryNoteReady OR NOT requiresDeliveryNote)",
|
|
219
|
-
].join(" AND ");
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
const parts = [];
|
|
223
|
-
if (pv.logistics.batchClosed)
|
|
224
|
-
parts.push("batchClosed");
|
|
225
|
-
if (pv.logistics.cartBuilt)
|
|
226
|
-
parts.push("cartBuilt");
|
|
227
|
-
if (pv.logistics.cartFull)
|
|
228
|
-
parts.push("cartFull");
|
|
229
|
-
if (pv.logistics.plannerApproved)
|
|
230
|
-
parts.push("plannerApproved");
|
|
231
|
-
if (pv.logistics.capacityReleased)
|
|
232
|
-
parts.push("capacityReleased");
|
|
233
|
-
if (pv.documents.packingSlip)
|
|
234
|
-
parts.push("packingSlipReady");
|
|
235
|
-
if (pv.documents.deliveryNote)
|
|
236
|
-
parts.push("(deliveryNoteReady OR NOT requiresDeliveryNote)");
|
|
237
|
-
if (pv.documents.transportLabel)
|
|
238
|
-
parts.push("transportLabelReady");
|
|
239
|
-
switch (pv.control) {
|
|
240
|
-
case "quality":
|
|
241
|
-
parts.push("qualityChecked");
|
|
242
|
-
break;
|
|
243
|
-
case "cart":
|
|
244
|
-
parts.push("cartChecked");
|
|
245
|
-
break;
|
|
246
|
-
case "both":
|
|
247
|
-
parts.push("qualityChecked AND cartChecked");
|
|
248
|
-
break;
|
|
249
|
-
case "manual":
|
|
250
|
-
parts.push("plannerManualRelease");
|
|
251
|
-
break;
|
|
252
|
-
case "none":
|
|
253
|
-
default: break;
|
|
254
|
-
}
|
|
255
|
-
visExpr = parts.length ? parts.join(" AND ") : "true";
|
|
256
|
-
}
|
|
257
|
-
out.push({ slot: "pickstation_visible", expression: visExpr });
|
|
258
|
-
// Continu Pickstation strategie (aanbevolen preset)
|
|
259
|
-
const co = c.continuous;
|
|
260
|
-
if (co.enabled) {
|
|
261
|
-
out.push({
|
|
262
|
-
slot: "continuous_full_cart_window",
|
|
263
|
-
expression: `urenTotAanmelding <= ${Math.max(0, co.normalPickHours)}`,
|
|
264
|
-
});
|
|
265
|
-
if (co.allowEarlier) {
|
|
266
|
-
out.push({
|
|
267
|
-
slot: "continuous_early_start_allowed",
|
|
268
|
-
expression: `capaciteitstekort AND urenTotAanmelding <= ${co.normalPickHours + Math.max(0, co.maxEarlierHours)}`,
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
out.push({ slot: "continuous_early_start_allowed", expression: "false" });
|
|
273
|
-
}
|
|
274
|
-
out.push({
|
|
275
|
-
slot: "continuous_incomplete_cart_release",
|
|
276
|
-
expression: co.incompleteCartsMode === "immediate" ? "true" : "aanmeldmomentBereikt",
|
|
277
|
-
});
|
|
278
|
-
out.push({
|
|
279
|
-
slot: "continuous_orders_after_registration",
|
|
280
|
-
expression: `"${co.ordersAfterRegistration}"`,
|
|
281
|
-
});
|
|
282
|
-
out.push({
|
|
283
|
-
slot: "continuous_keep_station_filled",
|
|
284
|
-
expression: co.keepStationFilled ? "true" : "false",
|
|
285
|
-
});
|
|
286
|
-
out.push({
|
|
287
|
-
slot: "continuous_even_workload",
|
|
288
|
-
expression: co.evenWorkload ? "true" : "false",
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
return out;
|
|
292
|
-
}
|
|
293
|
-
/** Genereer een leesbare samenvatting in gewone Nederlandse taal. */
|
|
294
|
-
export function summarizeConfig(c) {
|
|
295
|
-
const s = [];
|
|
296
|
-
const modeLabel = {
|
|
297
|
-
"per-order": "per order",
|
|
298
|
-
"per-customer": "per klant",
|
|
299
|
-
"per-location": "per afleverlocatie",
|
|
300
|
-
"per-cart": "per kar",
|
|
301
|
-
custom: "op eigen manier",
|
|
302
|
-
};
|
|
303
|
-
s.push(`Orders worden verwerkt ${modeLabel[c.orderMode]}.`);
|
|
304
|
-
const ws = c.workingSchedule;
|
|
305
|
-
const dayNames = ["ma", "di", "wo", "do", "vr", "za", "zo"];
|
|
306
|
-
const wdLabel = (ws.weekdays ?? []).map((d) => dayNames[d - 1]).filter(Boolean).join(", ") || "geen dagen";
|
|
307
|
-
const breaksLabel = (ws.breaks ?? []).length
|
|
308
|
-
? ` met pauze${(ws.breaks ?? []).length > 1 ? "s" : ""} ${(ws.breaks ?? []).map((b) => `${b.start}–${b.end}${b.label ? ` (${b.label})` : ""}`).join(", ")}`
|
|
309
|
-
: "";
|
|
310
|
-
s.push(`Het pickteam werkt op ${wdLabel} van ${ws.startTime} tot ${ws.endTime}${breaksLabel}.`);
|
|
311
|
-
if (c.combine.mode === "none") {
|
|
312
|
-
s.push("Orders worden nooit gecombineerd.");
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
const t = c.combine.mode === "auction" ? "Veilingorders"
|
|
316
|
-
: c.combine.mode === "bvo" ? "BVO-orders" : "Alle ordertypes";
|
|
317
|
-
s.push(`${t} tot ${c.combine.maxLayersOwnCart} lagen mogen gecombineerd worden (max ${c.combine.maxCustomersPerCart} klanten per kar).`);
|
|
318
|
-
const constraints = [];
|
|
319
|
-
if (c.combine.sameCarrier)
|
|
320
|
-
constraints.push("dezelfde vervoerder");
|
|
321
|
-
if (c.combine.sameRoute)
|
|
322
|
-
constraints.push("dezelfde route");
|
|
323
|
-
if (c.combine.sameLocation)
|
|
324
|
-
constraints.push("dezelfde afleverlocatie");
|
|
325
|
-
if (c.combine.sameCrateType)
|
|
326
|
-
constraints.push("hetzelfde fusttype");
|
|
327
|
-
if (c.combine.sameDeliveryDate)
|
|
328
|
-
constraints.push("dezelfde leverdatum");
|
|
329
|
-
if (constraints.length)
|
|
330
|
-
s.push(`Combineren alleen bij ${constraints.join(", ")}.`);
|
|
331
|
-
s.push(c.combine.customersPerLayer === "single"
|
|
332
|
-
? "Maximaal één klant per laag."
|
|
333
|
-
: "Meerdere klanten per laag toegestaan.");
|
|
334
|
-
}
|
|
335
|
-
switch (c.cartFull.mode) {
|
|
336
|
-
case "auto":
|
|
337
|
-
s.push("Een kar wordt afgesloten zodra er fysiek niets meer bij past (aanbevolen).");
|
|
338
|
-
break;
|
|
339
|
-
case "pct":
|
|
340
|
-
s.push(`Een kar is vol bij ${c.cartFull.fillPct}% vulling.`);
|
|
341
|
-
break;
|
|
342
|
-
case "layers":
|
|
343
|
-
s.push(`Een kar is vol bij ${c.cartFull.maxLayers} lagen.`);
|
|
344
|
-
break;
|
|
345
|
-
case "weight":
|
|
346
|
-
s.push(`Een kar is vol bij ${c.cartFull.maxWeight} kg.`);
|
|
347
|
-
break;
|
|
348
|
-
case "custom":
|
|
349
|
-
s.push("Een kar is vol volgens een eigen regel.");
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
const closes = [];
|
|
353
|
-
if (c.cartClose.whenFull)
|
|
354
|
-
closes.push("zodra deze vol is");
|
|
355
|
-
if (c.cartClose.whenCapacityReached)
|
|
356
|
-
closes.push("wanneer de pickcapaciteit bereikt is");
|
|
357
|
-
if (c.cartClose.when30minLeft)
|
|
358
|
-
closes.push("wanneer nog 30 min. picktijd over is");
|
|
359
|
-
if (c.cartClose.whenTransportRegistered)
|
|
360
|
-
closes.push("zodra de kar is aangemeld voor transport (dan kan er niets meer bij)");
|
|
361
|
-
if (c.cartClose.onlyManual || closes.length === 0)
|
|
362
|
-
s.push("Karren worden alleen handmatig afgesloten.");
|
|
363
|
-
else
|
|
364
|
-
s.push(`Een kar wordt automatisch afgesloten ${closes.join(" of ")}.`);
|
|
365
|
-
const docLabel = {
|
|
366
|
-
"cart-full": "zodra de kar vol is",
|
|
367
|
-
"batch-close": "zodra de batch sluit",
|
|
368
|
-
pickstation: "bij opname in het Pickstation (wanneer de kar wordt opgepakt)",
|
|
369
|
-
manual: "handmatig",
|
|
370
|
-
floriday: "zodra Floriday deze beschikbaar stelt",
|
|
371
|
-
};
|
|
372
|
-
s.push(`Pakbonnen worden aangemaakt ${docLabel[c.documents.packingSlip]}.`);
|
|
373
|
-
s.push(`Afleverbrieven worden aangemaakt ${docLabel[c.documents.deliveryNote]}.`);
|
|
374
|
-
const pp = c.pickPlanningRelease;
|
|
375
|
-
switch (pp.mode) {
|
|
376
|
-
case "latest":
|
|
377
|
-
s.push(`Orders blijven zo lang mogelijk buiten de pickplanning. Pas wanneer het systeem berekent dat er nog precies genoeg tijd is om alle karren vóór vertrek te picken (met ${pp.safetyBufferMinutes} min. veiligheidsbuffer), worden ze definitief ingepland. Zo blijven orders maximaal combineerbaar.`);
|
|
378
|
-
break;
|
|
379
|
-
case "hours-before-departure":
|
|
380
|
-
s.push(`Orders worden ${pp.hoursBeforeDeparture} uur vóór vertrek van de vervoerder vrijgegeven aan de pickplanning.`);
|
|
381
|
-
break;
|
|
382
|
-
case "between-signup-window":
|
|
383
|
-
s.push(`Karren mogen vanaf ${pp.hoursBeforeSignupWindow} werkuren vóór het aanmeldmoment naar het Pickstation. Bij drukte mogen ze ${pp.earlyPickHours} werkuren eerder. Niet-volle karren ${pp.incompleteCartsBeforeSignup ? "mogen ook binnen dit venster al naar het Pickstation" : "gaan pas ná het aanmeldmoment naar het Pickstation"}.`);
|
|
384
|
-
break;
|
|
385
|
-
case "fixed-time":
|
|
386
|
-
s.push(`Orders worden dagelijks om ${pp.fixedTime} vrijgegeven aan de pickplanning.`);
|
|
387
|
-
break;
|
|
388
|
-
case "days-before-delivery":
|
|
389
|
-
s.push(`Orders worden ${pp.daysBeforeDelivery} werkdag(en) vóór levering vrijgegeven aan de pickplanning.`);
|
|
390
|
-
break;
|
|
391
|
-
case "manual":
|
|
392
|
-
s.push("De planner geeft orders handmatig vrij aan de pickplanning.");
|
|
393
|
-
break;
|
|
394
|
-
case "custom":
|
|
395
|
-
s.push("Orders worden vrijgegeven aan de pickplanning volgens een eigen logistieke regel.");
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
const pv = c.pickstationVisibility;
|
|
399
|
-
if (pv.mode === "auto") {
|
|
400
|
-
s.push("Een kar wordt automatisch vrijgegeven aan het Pickstation zodra de batch is afgesloten, de kar volledig is opgebouwd, er geen producten meer toegevoegd kunnen worden en alle benodigde documenten beschikbaar zijn. Documenten die niet van toepassing zijn op het ordertype worden automatisch genegeerd.");
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
const vis = [];
|
|
404
|
-
if (pv.logistics.batchClosed)
|
|
405
|
-
vis.push("de batch gesloten is");
|
|
406
|
-
if (pv.logistics.cartBuilt)
|
|
407
|
-
vis.push("de kar volledig is opgebouwd");
|
|
408
|
-
if (pv.logistics.cartFull)
|
|
409
|
-
vis.push("er geen producten meer op de kar passen");
|
|
410
|
-
if (pv.logistics.plannerApproved)
|
|
411
|
-
vis.push("de planner de kar heeft vrijgegeven");
|
|
412
|
-
if (pv.logistics.capacityReleased)
|
|
413
|
-
vis.push("de capaciteit is vrijgegeven");
|
|
414
|
-
if (pv.documents.packingSlip)
|
|
415
|
-
vis.push("de pakbon aanwezig is");
|
|
416
|
-
if (pv.documents.deliveryNote)
|
|
417
|
-
vis.push("de afleverbrief aanwezig is (indien vereist)");
|
|
418
|
-
if (pv.documents.transportLabel)
|
|
419
|
-
vis.push("het transportlabel aanwezig is");
|
|
420
|
-
const ctrl = {
|
|
421
|
-
none: null,
|
|
422
|
-
quality: "kwaliteitscontrole is uitgevoerd",
|
|
423
|
-
cart: "karcontrole is uitgevoerd",
|
|
424
|
-
both: "kwaliteits- én karcontrole zijn uitgevoerd",
|
|
425
|
-
manual: "de planner de kar handmatig heeft vrijgegeven",
|
|
426
|
-
};
|
|
427
|
-
const ctrlMsg = ctrl[pv.control];
|
|
428
|
-
if (ctrlMsg)
|
|
429
|
-
vis.push(ctrlMsg);
|
|
430
|
-
if (vis.length)
|
|
431
|
-
s.push(`Een kar wordt aan het Pickstation vrijgegeven zodra ${vis.join(" en ")}.`);
|
|
432
|
-
else
|
|
433
|
-
s.push("Er zijn geen voorwaarden ingesteld — elke kar wordt direct vrijgegeven.");
|
|
434
|
-
}
|
|
435
|
-
if (c.startMoment.autoWhenCapacity) {
|
|
436
|
-
const cph = c.startMoment.capacityMode === "minutes-per-cart"
|
|
437
|
-
? (c.startMoment.minutesPerCart > 0 ? Math.round((60 / c.startMoment.minutesPerCart) * 10) / 10 : 0)
|
|
438
|
-
: c.startMoment.pickCapacityPerHour;
|
|
439
|
-
const detail = c.startMoment.capacityMode === "minutes-per-cart"
|
|
440
|
-
? `${c.startMoment.minutesPerCart} min. per kar → ${cph} karren/uur`
|
|
441
|
-
: `${cph} karren/uur`;
|
|
442
|
-
s.push(`Picken start automatisch met een veiligheidsbuffer van ${c.startMoment.safetyBufferMinutes} minuten (${detail}).`);
|
|
443
|
-
}
|
|
444
|
-
if (c.continuous.enabled) {
|
|
445
|
-
const co = c.continuous;
|
|
446
|
-
const parts = [];
|
|
447
|
-
parts.push(`Volledige karren worden zo laat mogelijk gepickt: normaal binnen ${co.normalPickHours} uur vóór het aanmeldmoment.`);
|
|
448
|
-
if (co.allowEarlier) {
|
|
449
|
-
parts.push(`Bij capaciteitstekort mag het systeem maximaal ${co.maxEarlierHours} uur eerder starten.`);
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
parts.push("Eerder starten bij drukte is niet toegestaan.");
|
|
453
|
-
}
|
|
454
|
-
parts.push(co.incompleteCartsMode === "immediate"
|
|
455
|
-
? "Niet-volledige karren mogen direct gepickt worden."
|
|
456
|
-
: "Niet-volledige karren blijven open tot het aanmeldmoment, zodat orders maximaal gecombineerd blijven.");
|
|
457
|
-
const afterMap = {
|
|
458
|
-
"current-batch": "worden toegevoegd aan de huidige batch",
|
|
459
|
-
"new-batch": "krijgen een nieuwe batch",
|
|
460
|
-
"next-round": "schuiven automatisch door naar de volgende pickronde",
|
|
461
|
-
};
|
|
462
|
-
parts.push(`Nieuwe orders na het aanmeldmoment ${afterMap[co.ordersAfterRegistration]}.`);
|
|
463
|
-
if (co.keepStationFilled)
|
|
464
|
-
parts.push("Het Pickstation wordt continu gevuld gehouden — de picker hoeft nooit te wachten.");
|
|
465
|
-
if (co.evenWorkload)
|
|
466
|
-
parts.push("De werkdruk wordt zo gelijkmatig mogelijk over de beschikbare picktijd verdeeld.");
|
|
467
|
-
s.push(`⭐ Continu Pickstation actief. ${parts.join(" ")}`);
|
|
468
|
-
}
|
|
469
|
-
return s;
|
|
470
|
-
}
|
|
471
|
-
export function computePreview(c) {
|
|
472
|
-
const orders = 37;
|
|
473
|
-
const combineActive = c.combine.mode !== "none";
|
|
474
|
-
const combined = combineActive ? Math.floor(orders * 0.15) : 0;
|
|
475
|
-
const rest = combineActive ? 1 : 0;
|
|
476
|
-
const full = Math.max(0, Math.ceil((orders - combined - rest) / 2));
|
|
477
|
-
const carts = full + combined + rest;
|
|
478
|
-
// Simpele tijdsberekening voor demo (leadtime 60 min ophaal 08:00)
|
|
479
|
-
const pickupHour = 8;
|
|
480
|
-
const pickupMin = 0;
|
|
481
|
-
const totalOffset = 60 + c.startMoment.safetyBufferMinutes;
|
|
482
|
-
const startTotal = pickupHour * 60 + pickupMin - totalOffset;
|
|
483
|
-
const fmt = (m) => `${String(Math.floor(m / 60)).padStart(2, "0")}:${String(m % 60).padStart(2, "0")}`;
|
|
484
|
-
const pickStart = fmt(startTotal);
|
|
485
|
-
const packingSlipTime = fmt(startTotal + 2);
|
|
486
|
-
const pickstationStart = fmt(startTotal + 3);
|
|
487
|
-
return { orders, carts, full, combined, rest, pickStart, packingSlipTime, pickstationStart };
|
|
488
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { WizardConfig, WorkingSchedule } from "./config";
|
|
2
|
-
/**
|
|
3
|
-
* Trek `minutes` werkminuten af van `deadline`, binnen `schedule`.
|
|
4
|
-
* Retourneert een Date binnen de werktijden.
|
|
5
|
-
*/
|
|
6
|
-
export declare function subtractWorkMinutes(deadline: Date, minutes: number, schedule: WorkingSchedule, holidays?: Date[]): Date;
|
|
7
|
-
/**
|
|
8
|
-
* Bepaal hoeveel picktijd (in minuten) een order kost o.b.v. de wizard-config.
|
|
9
|
-
*/
|
|
10
|
-
export declare function computePickMinutes(cartCount: number, config: WizardConfig): number;
|
|
11
|
-
/**
|
|
12
|
-
* Bepaal de totale veiligheidsbuffer o.b.v. de wizard-config.
|
|
13
|
-
*/
|
|
14
|
-
export declare function computeBufferMinutes(pickMinutes: number, config: WizardConfig): number;
|
|
15
|
-
/**
|
|
16
|
-
* Bereken het exacte pick-startmoment ("Zo laat mogelijk").
|
|
17
|
-
* Retourneert `null` als de configuratie geen berekening toelaat.
|
|
18
|
-
*/
|
|
19
|
-
export declare function computeEnginePickStart(input: {
|
|
20
|
-
signupDeadline: Date;
|
|
21
|
-
cartCount: number;
|
|
22
|
-
config: WizardConfig;
|
|
23
|
-
holidays?: Date[];
|
|
24
|
-
}): {
|
|
25
|
-
pickStart: Date;
|
|
26
|
-
pickMinutes: number;
|
|
27
|
-
bufferMinutes: number;
|
|
28
|
-
} | null;
|
|
29
|
-
//# sourceMappingURL=enginePickTime.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"enginePickTime.d.ts","sourceRoot":"","sources":["../../../src/lib/logisticsEngine/enginePickTime.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAsC9D;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,IAAI,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,IAAI,EAAE,GAChB,IAAI,CAiDN;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAQlF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAOtF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,cAAc,EAAE,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;CACnB,GAAG;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQzE"}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Engine-gestuurde pickmoment-berekening.
|
|
3
|
-
*
|
|
4
|
-
* Gebruikt de wizard-config van de actieve strategie om te bepalen op
|
|
5
|
-
* welk exact moment een order gepickt moet worden ("Zo laat mogelijk"):
|
|
6
|
-
*
|
|
7
|
-
* pickStart = signupDeadline − (benodigde picktijd + veiligheidsbuffer)
|
|
8
|
-
*
|
|
9
|
-
* De aftrek gebeurt binnen de werkdagen/werktijden en houdt rekening
|
|
10
|
-
* met pauzes.
|
|
11
|
-
*/
|
|
12
|
-
import { isoWeekday, normalizeWeekdays } from "../planning";
|
|
13
|
-
function toMin(t) {
|
|
14
|
-
const [h = "0", m = "0"] = (t ?? "").split(":");
|
|
15
|
-
return (Number(h) || 0) * 60 + (Number(m) || 0);
|
|
16
|
-
}
|
|
17
|
-
function sameDay(a, b) {
|
|
18
|
-
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
19
|
-
}
|
|
20
|
-
function isHoliday(d, holidays) {
|
|
21
|
-
return !!holidays && holidays.some((h) => sameDay(h, d));
|
|
22
|
-
}
|
|
23
|
-
function dayIntervals(schedule) {
|
|
24
|
-
const start = toMin(schedule.startTime || "07:00");
|
|
25
|
-
const end = toMin(schedule.endTime || "16:00");
|
|
26
|
-
const breaks = (schedule.breaks ?? [])
|
|
27
|
-
.map((b) => ({ s: toMin(b.start), e: toMin(b.end) }))
|
|
28
|
-
.filter((b) => b.e > b.s && b.s >= start && b.e <= end)
|
|
29
|
-
.sort((a, b) => a.s - b.s);
|
|
30
|
-
const ints = [];
|
|
31
|
-
let cur = start;
|
|
32
|
-
for (const b of breaks) {
|
|
33
|
-
if (b.s > cur)
|
|
34
|
-
ints.push({ start: cur, end: b.s });
|
|
35
|
-
cur = Math.max(cur, b.e);
|
|
36
|
-
}
|
|
37
|
-
if (end > cur)
|
|
38
|
-
ints.push({ start: cur, end });
|
|
39
|
-
return ints;
|
|
40
|
-
}
|
|
41
|
-
function dayMinutes(ints) {
|
|
42
|
-
return ints.reduce((a, iv) => a + (iv.end - iv.start), 0);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Trek `minutes` werkminuten af van `deadline`, binnen `schedule`.
|
|
46
|
-
* Retourneert een Date binnen de werktijden.
|
|
47
|
-
*/
|
|
48
|
-
export function subtractWorkMinutes(deadline, minutes, schedule, holidays) {
|
|
49
|
-
const ints = dayIntervals(schedule);
|
|
50
|
-
const perDay = dayMinutes(ints);
|
|
51
|
-
if (perDay <= 0 || minutes <= 0)
|
|
52
|
-
return new Date(deadline);
|
|
53
|
-
const allowed = new Set(normalizeWeekdays(schedule.weekdays));
|
|
54
|
-
const availableBefore = (date) => {
|
|
55
|
-
const t = date.getHours() * 60 + date.getMinutes();
|
|
56
|
-
let sum = 0;
|
|
57
|
-
for (const iv of ints) {
|
|
58
|
-
if (t <= iv.start)
|
|
59
|
-
break;
|
|
60
|
-
sum += Math.min(t, iv.end) - iv.start;
|
|
61
|
-
}
|
|
62
|
-
return sum;
|
|
63
|
-
};
|
|
64
|
-
const positionInDay = (date, minutesFromStart) => {
|
|
65
|
-
let acc = 0;
|
|
66
|
-
for (const iv of ints) {
|
|
67
|
-
const len = iv.end - iv.start;
|
|
68
|
-
if (acc + len >= minutesFromStart) {
|
|
69
|
-
const t = iv.start + (minutesFromStart - acc);
|
|
70
|
-
const out = new Date(date);
|
|
71
|
-
out.setHours(Math.floor(t / 60), t % 60, 0, 0);
|
|
72
|
-
return out;
|
|
73
|
-
}
|
|
74
|
-
acc += len;
|
|
75
|
-
}
|
|
76
|
-
const t = ints[ints.length - 1].end;
|
|
77
|
-
const out = new Date(date);
|
|
78
|
-
out.setHours(Math.floor(t / 60), t % 60, 0, 0);
|
|
79
|
-
return out;
|
|
80
|
-
};
|
|
81
|
-
const cursor = new Date(deadline);
|
|
82
|
-
let remaining = minutes;
|
|
83
|
-
let guard = 365;
|
|
84
|
-
if (allowed.has(isoWeekday(cursor)) && !isHoliday(cursor, holidays)) {
|
|
85
|
-
const avail = availableBefore(cursor);
|
|
86
|
-
if (avail >= remaining)
|
|
87
|
-
return positionInDay(cursor, avail - remaining);
|
|
88
|
-
remaining -= avail;
|
|
89
|
-
}
|
|
90
|
-
while (remaining > 0 && guard-- > 0) {
|
|
91
|
-
cursor.setDate(cursor.getDate() - 1);
|
|
92
|
-
if (!allowed.has(isoWeekday(cursor)) || isHoliday(cursor, holidays))
|
|
93
|
-
continue;
|
|
94
|
-
if (perDay >= remaining)
|
|
95
|
-
return positionInDay(cursor, perDay - remaining);
|
|
96
|
-
remaining -= perDay;
|
|
97
|
-
}
|
|
98
|
-
return cursor;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Bepaal hoeveel picktijd (in minuten) een order kost o.b.v. de wizard-config.
|
|
102
|
-
*/
|
|
103
|
-
export function computePickMinutes(cartCount, config) {
|
|
104
|
-
const carts = Math.max(1, Math.floor(cartCount || 1));
|
|
105
|
-
const sm = config.startMoment;
|
|
106
|
-
if (sm.capacityMode === "minutes-per-cart") {
|
|
107
|
-
return carts * Math.max(0, sm.minutesPerCart || 0);
|
|
108
|
-
}
|
|
109
|
-
const perHour = Math.max(0.0001, sm.pickCapacityPerHour || 1);
|
|
110
|
-
return (carts / perHour) * 60;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Bepaal de totale veiligheidsbuffer o.b.v. de wizard-config.
|
|
114
|
-
*/
|
|
115
|
-
export function computeBufferMinutes(pickMinutes, config) {
|
|
116
|
-
const r = config.pickPlanningRelease;
|
|
117
|
-
if (r.safetyBufferMode === "percent") {
|
|
118
|
-
const pct = Math.max(0, r.safetyBufferPercent || 0);
|
|
119
|
-
return (pickMinutes * pct) / 100;
|
|
120
|
-
}
|
|
121
|
-
return Math.max(0, r.safetyBufferMinutes || 0);
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Bereken het exacte pick-startmoment ("Zo laat mogelijk").
|
|
125
|
-
* Retourneert `null` als de configuratie geen berekening toelaat.
|
|
126
|
-
*/
|
|
127
|
-
export function computeEnginePickStart(input) {
|
|
128
|
-
const { config, signupDeadline } = input;
|
|
129
|
-
if (config.pickPlanningRelease.mode !== "latest")
|
|
130
|
-
return null;
|
|
131
|
-
const pickMinutes = computePickMinutes(input.cartCount, config);
|
|
132
|
-
const bufferMinutes = computeBufferMinutes(pickMinutes, config);
|
|
133
|
-
const total = pickMinutes + bufferMinutes;
|
|
134
|
-
const pickStart = subtractWorkMinutes(signupDeadline, total, config.workingSchedule, input.holidays);
|
|
135
|
-
return { pickStart, pickMinutes, bufferMinutes };
|
|
136
|
-
}
|