@flowselections/floriday-afhandeling-module 1.0.8 → 1.0.10
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/DagDetailDialog.d.ts +9 -0
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/DagDetailDialog.js +145 -0
- package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/LeveringenPage.js +17 -69
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts +6 -0
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/PickAgendaView.js +40 -0
- package/dist-lib/components/afhandeling/PickingPage.d.ts +2 -0
- package/dist-lib/components/afhandeling/PickingPage.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/PickingPage.js +5 -0
- package/dist-lib/components/afhandeling/PlanningAgendaPage.d.ts +9 -0
- package/dist-lib/components/afhandeling/PlanningAgendaPage.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/PlanningAgendaPage.js +7 -0
- package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts +12 -0
- package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/ServiceAreaSelect.js +79 -0
- package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerderForm.js +73 -12
- package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/VervoerdersPage.js +23 -5
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts +13 -0
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/WeekAgendaView.js +142 -0
- package/dist-lib/components/pickstation/LanguagePicker.d.ts +5 -0
- package/dist-lib/components/pickstation/LanguagePicker.d.ts.map +1 -0
- package/dist-lib/components/pickstation/LanguagePicker.js +5 -0
- package/dist-lib/components/pickstation/PakbonTemplate.d.ts +20 -0
- package/dist-lib/components/pickstation/PakbonTemplate.d.ts.map +1 -0
- package/dist-lib/components/pickstation/PakbonTemplate.js +76 -0
- package/dist-lib/components/pickstation/printDocuments.d.ts +3 -0
- package/dist-lib/components/pickstation/printDocuments.d.ts.map +1 -0
- package/dist-lib/components/pickstation/printDocuments.js +53 -0
- package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/AfhandelingSettingsCard.js +72 -0
- package/dist-lib/components/settings/VervoerdersSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/VervoerdersSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/VervoerdersSettingsCard.js +30 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts +17 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -0
- package/dist-lib/hooks/useOrderPlanning.js +131 -0
- package/dist-lib/index.d.ts +5 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +16 -6
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2793 -137
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2793 -137
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2793 -137
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2870 -131
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +2 -0
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +18597 -0
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -0
- package/dist-lib/lib/afhandelingSettings.functions.js +98 -0
- package/dist-lib/lib/carrierSignupMail.server.d.ts +56 -0
- package/dist-lib/lib/carrierSignupMail.server.d.ts.map +1 -0
- package/dist-lib/lib/carrierSignupMail.server.js +229 -0
- package/dist-lib/lib/carriers.functions.d.ts +34091 -14161
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/carriers.functions.js +69 -6
- package/dist-lib/lib/cartAssignments.functions.d.ts +46479 -0
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -0
- package/dist-lib/lib/cartAssignments.functions.js +130 -0
- package/dist-lib/lib/cartMoveImpact.d.ts +8 -0
- package/dist-lib/lib/cartMoveImpact.d.ts.map +1 -0
- package/dist-lib/lib/cartMoveImpact.js +26 -0
- package/dist-lib/lib/floriday.functions.d.ts +2798 -137
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +178 -93
- package/dist-lib/lib/geo/euServiceAreas.d.ts +30 -0
- package/dist-lib/lib/geo/euServiceAreas.d.ts.map +1 -0
- package/dist-lib/lib/geo/euServiceAreas.js +277 -0
- package/dist-lib/lib/invalidatePlanning.d.ts +11 -0
- package/dist-lib/lib/invalidatePlanning.d.ts.map +1 -0
- package/dist-lib/lib/invalidatePlanning.js +25 -0
- package/dist-lib/lib/pickDate.d.ts +21 -0
- package/dist-lib/lib/pickDate.d.ts.map +1 -0
- package/dist-lib/lib/pickDate.js +71 -0
- package/dist-lib/lib/pickstationI18n.d.ts +35 -0
- package/dist-lib/lib/pickstationI18n.d.ts.map +1 -0
- package/dist-lib/lib/pickstationI18n.js +171 -0
- package/dist-lib/lib/pickstationOrders.server.d.ts +9 -0
- package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -0
- package/dist-lib/lib/pickstationOrders.server.js +372 -0
- package/dist-lib/lib/pickstationTypes.d.ts +26 -0
- package/dist-lib/lib/pickstationTypes.d.ts.map +1 -0
- package/dist-lib/lib/pickstationTypes.js +1 -0
- package/dist-lib/lib/planning.d.ts +82 -0
- package/dist-lib/lib/planning.d.ts.map +1 -0
- package/dist-lib/lib/planning.js +243 -0
- package/package.json +12 -6
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
// Server-only helper. Imported by /api/public/pickstation.* routes.
|
|
2
|
+
// Uses the publishable-key Supabase client + SECURITY DEFINER RPCs so the
|
|
3
|
+
// public Pickstation works without a service-role key.
|
|
4
|
+
import { createClient } from "@supabase/supabase-js";
|
|
5
|
+
import { calculateOrderPlanning, distributePickDates, DEFAULT_PICKUP_MOMENT, } from "./planning";
|
|
6
|
+
const LOG_SCOPE = "[pickstation]";
|
|
7
|
+
function debug(action, details) {
|
|
8
|
+
console.info(`${LOG_SCOPE} ${action}`, {
|
|
9
|
+
at: new Date().toISOString(),
|
|
10
|
+
...(details ?? {}),
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function warn(action, details) {
|
|
14
|
+
console.warn(`${LOG_SCOPE} ${action}`, {
|
|
15
|
+
at: new Date().toISOString(),
|
|
16
|
+
...(details ?? {}),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function errorLog(action, err, details) {
|
|
20
|
+
console.error(`${LOG_SCOPE} ${action}`, {
|
|
21
|
+
at: new Date().toISOString(),
|
|
22
|
+
message: err instanceof Error ? err.message : String(err),
|
|
23
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
24
|
+
...(details ?? {}),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function getPublicClient() {
|
|
28
|
+
const url = process.env.SUPABASE_URL ?? process.env.VITE_SUPABASE_URL;
|
|
29
|
+
const key = process.env.SUPABASE_PUBLISHABLE_KEY ?? process.env.VITE_SUPABASE_PUBLISHABLE_KEY;
|
|
30
|
+
if (!url || !key) {
|
|
31
|
+
throw new Error("Supabase publishable env vars ontbreken");
|
|
32
|
+
}
|
|
33
|
+
return createClient(url, key, {
|
|
34
|
+
auth: { storage: undefined, persistSession: false, autoRefreshToken: false },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function pickStr(r, ...keys) {
|
|
38
|
+
for (const k of keys) {
|
|
39
|
+
const v = r[k];
|
|
40
|
+
if (typeof v === "string" && v.length > 0)
|
|
41
|
+
return v;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function pickCustomer(raw) {
|
|
46
|
+
const buyer = raw.buyer;
|
|
47
|
+
const customer = raw.customer;
|
|
48
|
+
return (pickStr(buyer ?? {}, "organizationName", "name") ??
|
|
49
|
+
pickStr(customer ?? {}, "organizationName", "name") ??
|
|
50
|
+
pickStr(raw, "customerName", "buyerName") ??
|
|
51
|
+
"—");
|
|
52
|
+
}
|
|
53
|
+
function pickCountry(raw) {
|
|
54
|
+
const buyer = raw.buyer;
|
|
55
|
+
const customer = raw.customer;
|
|
56
|
+
const addr = raw.deliveryAddress ??
|
|
57
|
+
raw.shipTo ??
|
|
58
|
+
buyer?.address ??
|
|
59
|
+
customer?.address;
|
|
60
|
+
return (pickStr(addr ?? {}, "countryCode", "country") ??
|
|
61
|
+
pickStr(raw, "deliveryCountry", "countryCode") ??
|
|
62
|
+
null);
|
|
63
|
+
}
|
|
64
|
+
function pickDeliveryDate(raw) {
|
|
65
|
+
const candidates = [
|
|
66
|
+
raw.deliveryDate,
|
|
67
|
+
raw.requestedDeliveryDate,
|
|
68
|
+
raw.expectedDeliveryDate,
|
|
69
|
+
raw.loadingDateTime,
|
|
70
|
+
raw.deliveryDateTime,
|
|
71
|
+
];
|
|
72
|
+
const found = candidates.find((v) => typeof v === "string" && v);
|
|
73
|
+
return found ?? null;
|
|
74
|
+
}
|
|
75
|
+
function toIsoDay(d) {
|
|
76
|
+
const y = d.getFullYear();
|
|
77
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
78
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
79
|
+
return `${y}-${m}-${day}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Verdeel de regels van een handmatige order over karren.
|
|
83
|
+
* Eén product per kar; de laatste kar van een product mag minder fusten bevatten.
|
|
84
|
+
*/
|
|
85
|
+
function buildManualCarts(items) {
|
|
86
|
+
const carts = [];
|
|
87
|
+
for (const it of items) {
|
|
88
|
+
const qty = Number(it.quantity ?? 0);
|
|
89
|
+
const plantsPerCrate = Number(it.plants_per_crate ?? 0);
|
|
90
|
+
const cratesPerLayer = Number(it.crates_per_layer ?? 0);
|
|
91
|
+
const layersPerCart = Number(it.layers_per_cart ?? 0);
|
|
92
|
+
const description = it.product ?? "—";
|
|
93
|
+
if (!Number.isFinite(qty) || qty <= 0)
|
|
94
|
+
continue;
|
|
95
|
+
const cratesPerCart = cratesPerLayer > 0 && layersPerCart > 0 ? cratesPerLayer * layersPerCart : 0;
|
|
96
|
+
const totalCrates = plantsPerCrate > 0 ? Math.ceil(qty / plantsPerCrate) : 0;
|
|
97
|
+
if (cratesPerCart > 0 && totalCrates > 0) {
|
|
98
|
+
const cartsNeeded = Math.ceil(totalCrates / cratesPerCart);
|
|
99
|
+
for (let c = 0; c < cartsNeeded; c++) {
|
|
100
|
+
const crates = Math.min(cratesPerCart, totalCrates - c * cratesPerCart);
|
|
101
|
+
carts.push({
|
|
102
|
+
lines: [{ description, crates, unit: "fust" }],
|
|
103
|
+
cratesOnCart: crates,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Onbekende verpakking → één kar met onbekend aantal fusten.
|
|
109
|
+
carts.push({
|
|
110
|
+
lines: [{ description, crates: null, unit: "fust" }],
|
|
111
|
+
cratesOnCart: null,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (carts.length === 0) {
|
|
116
|
+
carts.push({ lines: [], cratesOnCart: null });
|
|
117
|
+
}
|
|
118
|
+
return carts;
|
|
119
|
+
}
|
|
120
|
+
function buildFloridayCarts(raw) {
|
|
121
|
+
const list = (raw.salesOrderLines ?? raw.lines ?? raw.orderLines);
|
|
122
|
+
const lines = Array.isArray(list)
|
|
123
|
+
? list.map((l) => {
|
|
124
|
+
const r = (l ?? {});
|
|
125
|
+
const tradeItem = r.tradeItem;
|
|
126
|
+
const product = r.product;
|
|
127
|
+
return {
|
|
128
|
+
description: pickStr(r, "description", "name") ??
|
|
129
|
+
pickStr(tradeItem ?? {}, "name", "description") ??
|
|
130
|
+
pickStr(product ?? {}, "name") ??
|
|
131
|
+
"—",
|
|
132
|
+
crates: null,
|
|
133
|
+
unit: "fust",
|
|
134
|
+
};
|
|
135
|
+
})
|
|
136
|
+
: [];
|
|
137
|
+
return [{ lines, cratesOnCart: null }];
|
|
138
|
+
}
|
|
139
|
+
export async function loadTodaysPickstationOrders() {
|
|
140
|
+
const startedAt = Date.now();
|
|
141
|
+
const supabase = getPublicClient();
|
|
142
|
+
debug("load orders started", {
|
|
143
|
+
query: "rpc pickstation_get_settings + pickstation_get_orders_raw",
|
|
144
|
+
});
|
|
145
|
+
const [settingsRes, ordersRes] = await Promise.all([
|
|
146
|
+
supabase.rpc("pickstation_get_settings"),
|
|
147
|
+
supabase.rpc("pickstation_get_orders_raw"),
|
|
148
|
+
]);
|
|
149
|
+
if (settingsRes.error) {
|
|
150
|
+
errorLog("settings rpc failed", new Error(settingsRes.error.message), {
|
|
151
|
+
code: settingsRes.error.code,
|
|
152
|
+
details: settingsRes.error.details,
|
|
153
|
+
hint: settingsRes.error.hint,
|
|
154
|
+
query: "rpc pickstation_get_settings",
|
|
155
|
+
});
|
|
156
|
+
throw new Error(`Pickstation-instellingen: ${settingsRes.error.message}`);
|
|
157
|
+
}
|
|
158
|
+
if (ordersRes.error) {
|
|
159
|
+
errorLog("orders rpc failed", new Error(ordersRes.error.message), {
|
|
160
|
+
code: ordersRes.error.code,
|
|
161
|
+
details: ordersRes.error.details,
|
|
162
|
+
hint: ordersRes.error.hint,
|
|
163
|
+
query: "rpc pickstation_get_orders_raw",
|
|
164
|
+
});
|
|
165
|
+
throw new Error(`Pickstation-orders: ${ordersRes.error.message}`);
|
|
166
|
+
}
|
|
167
|
+
const settingsRaw = (settingsRes.data ?? {});
|
|
168
|
+
const pickDays = typeof settingsRaw.pick_days === "number" ? settingsRaw.pick_days : 1;
|
|
169
|
+
const distributionEnabled = Boolean(settingsRaw.pick_distribution_enabled);
|
|
170
|
+
const maxEarlier = typeof settingsRaw.max_workdays_earlier_pick === "number"
|
|
171
|
+
? settingsRaw.max_workdays_earlier_pick
|
|
172
|
+
: 2;
|
|
173
|
+
const pickupMoments = Array.isArray(settingsRaw.pickup_moments) && settingsRaw.pickup_moments.length > 0
|
|
174
|
+
? settingsRaw.pickup_moments
|
|
175
|
+
: [DEFAULT_PICKUP_MOMENT];
|
|
176
|
+
const payload = (ordersRes.data ?? {
|
|
177
|
+
floriday: [],
|
|
178
|
+
manual: [],
|
|
179
|
+
overrides: [],
|
|
180
|
+
print_counts: {},
|
|
181
|
+
});
|
|
182
|
+
debug("raw data loaded", {
|
|
183
|
+
floridayOrders: payload.floriday?.length ?? 0,
|
|
184
|
+
manualOrders: payload.manual?.length ?? 0,
|
|
185
|
+
overrides: payload.overrides?.length ?? 0,
|
|
186
|
+
printLogEntries: Object.keys(payload.print_counts ?? {}).length,
|
|
187
|
+
pickDays,
|
|
188
|
+
distributionEnabled,
|
|
189
|
+
maxEarlier,
|
|
190
|
+
pickupMoments: pickupMoments.length,
|
|
191
|
+
});
|
|
192
|
+
const items = [];
|
|
193
|
+
const skippedWithoutPlanning = [];
|
|
194
|
+
// Batch: reserve/lookup FO-nummers voor Floriday-orders
|
|
195
|
+
const foNumberById = new Map();
|
|
196
|
+
const floridayIds = (payload.floriday ?? [])
|
|
197
|
+
.map((r) => (r.id ?? r.data?.salesOrderId) ?? null)
|
|
198
|
+
.filter((v) => !!v);
|
|
199
|
+
if (floridayIds.length > 0) {
|
|
200
|
+
const { data: mapped, error: mapErr } = await supabase.rpc("assign_floriday_order_numbers", { p_ids: floridayIds });
|
|
201
|
+
if (mapErr) {
|
|
202
|
+
warn("assign_floriday_order_numbers failed", { message: mapErr.message });
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
for (const m of (mapped ?? [])) {
|
|
206
|
+
foNumberById.set(m.floriday_id, m.order_number);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (const row of payload.floriday ?? []) {
|
|
211
|
+
const data = (row.data ?? {});
|
|
212
|
+
const deliveryDate = pickDeliveryDate(data);
|
|
213
|
+
const planning = calculateOrderPlanning(deliveryDate, {
|
|
214
|
+
pickupMoments,
|
|
215
|
+
pickDurationWorkdays: pickDays,
|
|
216
|
+
orderCreatedAt: data.creationDateTime ??
|
|
217
|
+
data.createDateTime ??
|
|
218
|
+
data.orderDateTime ??
|
|
219
|
+
null,
|
|
220
|
+
});
|
|
221
|
+
const id = row.id ?? data.salesOrderId ?? crypto.randomUUID();
|
|
222
|
+
if (!planning) {
|
|
223
|
+
skippedWithoutPlanning.push({
|
|
224
|
+
source: "floriday",
|
|
225
|
+
id,
|
|
226
|
+
reason: deliveryDate ? "invalid deliveryDate" : "missing deliveryDate",
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
const carts = buildFloridayCarts(data);
|
|
230
|
+
items.push({
|
|
231
|
+
meta: {
|
|
232
|
+
source: "floriday",
|
|
233
|
+
id,
|
|
234
|
+
orderNumber: foNumberById.get(id) ??
|
|
235
|
+
pickStr(data, "customerOrderId", "salesChannelOrderId", "salesOrderNumber", "orderNumber") ??
|
|
236
|
+
"—",
|
|
237
|
+
customerName: row.customer_name || pickCustomer(data),
|
|
238
|
+
country: pickCountry(data),
|
|
239
|
+
deliveryDate,
|
|
240
|
+
},
|
|
241
|
+
carts,
|
|
242
|
+
cartCount: carts.length,
|
|
243
|
+
planning,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
for (const row of payload.manual ?? []) {
|
|
247
|
+
const planning = calculateOrderPlanning(row.delivery_date, {
|
|
248
|
+
pickupMoments,
|
|
249
|
+
pickDurationWorkdays: pickDays,
|
|
250
|
+
orderCreatedAt: row.created_at,
|
|
251
|
+
});
|
|
252
|
+
if (!planning) {
|
|
253
|
+
skippedWithoutPlanning.push({
|
|
254
|
+
source: "manual",
|
|
255
|
+
id: row.id,
|
|
256
|
+
reason: row.delivery_date ? "invalid deliveryDate" : "missing deliveryDate",
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const carts = buildManualCarts(row.items ?? []);
|
|
260
|
+
items.push({
|
|
261
|
+
meta: {
|
|
262
|
+
source: "manual",
|
|
263
|
+
id: row.id,
|
|
264
|
+
orderNumber: row.order_number ?? "—",
|
|
265
|
+
customerName: row.customer_name ?? "—",
|
|
266
|
+
country: null,
|
|
267
|
+
deliveryDate: row.delivery_date,
|
|
268
|
+
},
|
|
269
|
+
carts,
|
|
270
|
+
cartCount: carts.length,
|
|
271
|
+
planning,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (skippedWithoutPlanning.length > 0) {
|
|
275
|
+
warn("orders skipped because planning could not be calculated", {
|
|
276
|
+
count: skippedWithoutPlanning.length,
|
|
277
|
+
sample: skippedWithoutPlanning.slice(0, 10),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
const overrideMap = new Map();
|
|
281
|
+
for (const o of payload.overrides ?? []) {
|
|
282
|
+
overrideMap.set(`${o.order_source}#${o.order_id}#${o.cart_index}`, {
|
|
283
|
+
date: new Date(o.pick_date),
|
|
284
|
+
movedFromDate: o.moved_from_date ? new Date(o.moved_from_date) : null,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
const distributed = distributePickDates(items, {
|
|
288
|
+
enabled: distributionEnabled,
|
|
289
|
+
maxWorkdaysEarlier: maxEarlier,
|
|
290
|
+
getCartCount: (it) => it.cartCount,
|
|
291
|
+
getCartOverride: (it, idx) => overrideMap.get(`${it.meta.source}#${it.meta.id}#${idx}`) ?? null,
|
|
292
|
+
});
|
|
293
|
+
const todayIso = toIsoDay(new Date());
|
|
294
|
+
const counts = payload.print_counts ?? {};
|
|
295
|
+
const out = [];
|
|
296
|
+
for (const it of distributed) {
|
|
297
|
+
if (!it.planning)
|
|
298
|
+
continue;
|
|
299
|
+
const todayAssignment = (it.planning.pickAssignments ?? []).find((a) => toIsoDay(a.date) === todayIso);
|
|
300
|
+
if (!todayAssignment)
|
|
301
|
+
continue;
|
|
302
|
+
const cartIndexes = todayAssignment.cartIndexes ?? [];
|
|
303
|
+
for (const idx of cartIndexes) {
|
|
304
|
+
const cart = it.carts[idx] ?? { lines: [], cratesOnCart: null };
|
|
305
|
+
const compoundId = `${it.meta.id}#${idx}`;
|
|
306
|
+
const c = counts[compoundId];
|
|
307
|
+
out.push({
|
|
308
|
+
id: compoundId,
|
|
309
|
+
orderId: it.meta.id,
|
|
310
|
+
orderNumber: it.meta.orderNumber,
|
|
311
|
+
customerName: it.meta.customerName,
|
|
312
|
+
country: it.meta.country,
|
|
313
|
+
deliveryDate: it.meta.deliveryDate,
|
|
314
|
+
pickDate: todayIso,
|
|
315
|
+
cartIndex: idx + 1,
|
|
316
|
+
cartTotal: it.cartCount,
|
|
317
|
+
cratesOnCart: cart.cratesOnCart,
|
|
318
|
+
lines: cart.lines,
|
|
319
|
+
printedCount: typeof c === "number" ? c : 0,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
out.sort((a, b) => {
|
|
324
|
+
if ((a.printedCount === 0) !== (b.printedCount === 0)) {
|
|
325
|
+
return a.printedCount === 0 ? -1 : 1;
|
|
326
|
+
}
|
|
327
|
+
const d = (a.deliveryDate ?? "").localeCompare(b.deliveryDate ?? "");
|
|
328
|
+
if (d !== 0)
|
|
329
|
+
return d;
|
|
330
|
+
if (a.orderNumber !== b.orderNumber)
|
|
331
|
+
return a.orderNumber.localeCompare(b.orderNumber);
|
|
332
|
+
return a.cartIndex - b.cartIndex;
|
|
333
|
+
});
|
|
334
|
+
debug("load orders completed", {
|
|
335
|
+
returnedCarts: out.length,
|
|
336
|
+
totalSourceOrders: items.length,
|
|
337
|
+
today: todayIso,
|
|
338
|
+
durationMs: Date.now() - startedAt,
|
|
339
|
+
});
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
export async function fetchFloridayAuctionLetterPdf(_orderId) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
export async function logPrint(orderId, orderNumber, customerName, documents, ipAddress) {
|
|
346
|
+
const supabase = getPublicClient();
|
|
347
|
+
debug("log print started", {
|
|
348
|
+
query: "rpc pickstation_log_print",
|
|
349
|
+
orderId,
|
|
350
|
+
orderNumber,
|
|
351
|
+
customerName,
|
|
352
|
+
documents,
|
|
353
|
+
});
|
|
354
|
+
const { error } = await supabase.rpc("pickstation_log_print", {
|
|
355
|
+
p_order_id: orderId,
|
|
356
|
+
p_order_number: orderNumber,
|
|
357
|
+
p_customer_name: customerName,
|
|
358
|
+
p_documents: documents,
|
|
359
|
+
p_ip_address: ipAddress,
|
|
360
|
+
});
|
|
361
|
+
if (error) {
|
|
362
|
+
errorLog("log print failed", new Error(error.message), {
|
|
363
|
+
code: error.code,
|
|
364
|
+
details: error.details,
|
|
365
|
+
hint: error.hint,
|
|
366
|
+
orderId,
|
|
367
|
+
query: "rpc pickstation_log_print",
|
|
368
|
+
});
|
|
369
|
+
throw new Error(`Pickstation-log: ${error.message}`);
|
|
370
|
+
}
|
|
371
|
+
debug("log print completed", { orderId, documents: documents.length });
|
|
372
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface PickstationLine {
|
|
2
|
+
description: string;
|
|
3
|
+
/** Aantal fusten op deze kar voor deze regel. */
|
|
4
|
+
crates: number | null;
|
|
5
|
+
unit: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface PickstationOrder {
|
|
8
|
+
/** Unieke id per kar: `${orderId}#${cartIndex}`. */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Order-id (zonder kar-suffix). */
|
|
11
|
+
orderId: string;
|
|
12
|
+
orderNumber: string;
|
|
13
|
+
customerName: string;
|
|
14
|
+
country: string | null;
|
|
15
|
+
deliveryDate: string | null;
|
|
16
|
+
pickDate: string;
|
|
17
|
+
/** 1-based volgnummer van de kar binnen de order. */
|
|
18
|
+
cartIndex: number;
|
|
19
|
+
/** Totaal aantal karren van de order. */
|
|
20
|
+
cartTotal: number;
|
|
21
|
+
/** Totaal aantal fusten op deze kar (som van alle regels). null = onbekend. */
|
|
22
|
+
cratesOnCart: number | null;
|
|
23
|
+
lines: PickstationLine[];
|
|
24
|
+
printedCount: number;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=pickstationTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pickstationTypes.d.ts","sourceRoot":"","sources":["../../src/lib/pickstationTypes.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centrale planningsmodule — werkdagen-rekenkern, ophaalmomenten,
|
|
3
|
+
* order-planning en pickverdeling. Eén bron van waarheid voor alle agenda-views.
|
|
4
|
+
*
|
|
5
|
+
* Model — bezorgdag = dag 0. Elk ophaalmoment definieert per stap (aanmelden,
|
|
6
|
+
* ophalen, bezorgen) hoeveel werkdagen vóór de bezorgdag de stap plaatsvindt
|
|
7
|
+
* en op welk tijdstip.
|
|
8
|
+
*/
|
|
9
|
+
export interface PickupMoment {
|
|
10
|
+
/** Werkdagen vóór bezorgdag waarop transport aangemeld wordt. */
|
|
11
|
+
signup_days: number;
|
|
12
|
+
/** Aanmeldtijd ("HH:MM"). */
|
|
13
|
+
signup_time: string;
|
|
14
|
+
/** Werkdagen vóór bezorgdag waarop opgehaald wordt. */
|
|
15
|
+
pickup_days: number;
|
|
16
|
+
/** Ophaaltijd ("HH:MM"). */
|
|
17
|
+
pickup_time: string;
|
|
18
|
+
/** Werkdagen vóór bezorgdag waarop bezorgd wordt (meestal 0). */
|
|
19
|
+
delivery_days: number;
|
|
20
|
+
/** Bezorgtijd ("HH:MM"). */
|
|
21
|
+
delivery_time: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const DEFAULT_PICKUP_MOMENT: PickupMoment;
|
|
24
|
+
export interface PlanningRules {
|
|
25
|
+
/** Beschikbare ophaalmomenten — minimaal één. */
|
|
26
|
+
pickupMoments: PickupMoment[];
|
|
27
|
+
/** Werkdagen pickduur — terugtellen vanaf ophaaldag. */
|
|
28
|
+
pickDurationWorkdays: number;
|
|
29
|
+
/** Tijdstip waarop de order is binnengekomen (voor moment-selectie). */
|
|
30
|
+
orderCreatedAt?: string | Date | null;
|
|
31
|
+
holidays?: Date[];
|
|
32
|
+
}
|
|
33
|
+
export interface PickAssignment {
|
|
34
|
+
date: Date;
|
|
35
|
+
cartCount: number;
|
|
36
|
+
/** Concrete kar-indexen op deze datum; nodig voor handmatig verplaatsen. */
|
|
37
|
+
cartIndexes?: number[];
|
|
38
|
+
}
|
|
39
|
+
export interface OrderPlanning {
|
|
40
|
+
deliveryDate: Date;
|
|
41
|
+
pickupDate: Date;
|
|
42
|
+
transportRegisterDate: Date;
|
|
43
|
+
pickStartDate: Date;
|
|
44
|
+
scheduledPickDate: Date;
|
|
45
|
+
/** Verdeling van karren over één of meerdere pickdagen. */
|
|
46
|
+
pickAssignments: PickAssignment[];
|
|
47
|
+
movedEarlier: boolean;
|
|
48
|
+
/** Welk moment is gekozen (handig voor debug / UI). */
|
|
49
|
+
selectedMoment: PickupMoment;
|
|
50
|
+
}
|
|
51
|
+
export declare function isWorkday(d: Date, holidays?: Date[]): boolean;
|
|
52
|
+
export declare function subtractWorkdays(date: Date, n: number, holidays?: Date[]): Date;
|
|
53
|
+
export declare function addWorkdays(date: Date, n: number, holidays?: Date[]): Date;
|
|
54
|
+
/**
|
|
55
|
+
* Bepaal de aanmelddeadline van een moment voor een gegeven bezorgdag.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getSignupDeadline(deliveryDate: Date, moment: PickupMoment, holidays?: Date[]): Date;
|
|
58
|
+
/**
|
|
59
|
+
* Kies het juiste ophaalmoment op basis van het ordertijdstip.
|
|
60
|
+
* - Voorkeursvolgorde: vroegst gesorteerd op aanmelddeadline.
|
|
61
|
+
* - Eerste moment waarvan deadline nog niet is verstreken wint.
|
|
62
|
+
* - Als alle deadlines verstreken zijn → laatste moment (best effort).
|
|
63
|
+
*/
|
|
64
|
+
export declare function selectPickupMoment(deliveryDate: Date, orderCreatedAt: Date | null | undefined, moments: PickupMoment[], holidays?: Date[]): PickupMoment;
|
|
65
|
+
export declare function calculateOrderPlanning(deliveryDate: string | Date | null | undefined, rules: PlanningRules): OrderPlanning | null;
|
|
66
|
+
export interface PickDistributionOptions<T = unknown> {
|
|
67
|
+
enabled: boolean;
|
|
68
|
+
maxWorkdaysEarlier: number;
|
|
69
|
+
holidays?: Date[];
|
|
70
|
+
/** Aantal karren per item — bepaalt het gewicht in de verdeling. */
|
|
71
|
+
getCartCount?: (item: T) => number;
|
|
72
|
+
/** Manual per-cart overrides — geïndexeerd op (itemKey, cartIndex) → Date. */
|
|
73
|
+
getCartOverride?: (item: T, cartIndex: number) => Date | PickCartOverride | null;
|
|
74
|
+
}
|
|
75
|
+
export interface PickCartOverride {
|
|
76
|
+
date: Date;
|
|
77
|
+
movedFromDate?: Date | null;
|
|
78
|
+
}
|
|
79
|
+
export declare function distributePickDates<T extends {
|
|
80
|
+
planning: OrderPlanning | null;
|
|
81
|
+
}>(items: T[], options: PickDistributionOptions<T>): T[];
|
|
82
|
+
//# sourceMappingURL=planning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../../src/lib/planning.ts"],"names":[],"mappings":"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"}
|