@flowselections/floriday-verkoop-module 1.0.90 → 1.0.93

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.
Files changed (28) hide show
  1. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  2. package/dist-lib/components/verkoop/CreateOrderPage.js +1 -1
  3. package/dist-lib/components/verkoop/OrderPickupMomentPicker.d.ts +2 -2
  4. package/dist-lib/components/verkoop/OrderPickupMomentPicker.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/OrderPickupMomentPicker.js +10 -23
  6. package/dist-lib/integrations/supabase/types.d.ts +162 -0
  7. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  8. package/dist-lib/lib/invoice-timing.check.d.ts +2 -0
  9. package/dist-lib/lib/invoice-timing.check.d.ts.map +1 -0
  10. package/dist-lib/lib/invoice-timing.check.js +19 -0
  11. package/dist-lib/lib/invoice-timing.d.ts +2 -0
  12. package/dist-lib/lib/invoice-timing.d.ts.map +1 -1
  13. package/dist-lib/lib/invoice-timing.js +22 -4
  14. package/dist-lib/lib/order-departure.check.d.ts +2 -0
  15. package/dist-lib/lib/order-departure.check.d.ts.map +1 -0
  16. package/dist-lib/lib/order-departure.check.js +25 -0
  17. package/dist-lib/lib/order-departure.d.ts +25 -0
  18. package/dist-lib/lib/order-departure.d.ts.map +1 -0
  19. package/dist-lib/lib/order-departure.js +54 -0
  20. package/dist-lib/lib/order-departure.server.d.ts +4 -0
  21. package/dist-lib/lib/order-departure.server.d.ts.map +1 -0
  22. package/dist-lib/lib/order-departure.server.js +62 -0
  23. package/dist-lib/lib/order-pickup.functions.d.ts +8 -13
  24. package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/order-pickup.functions.js +48 -75
  26. package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
  27. package/dist-lib/lib/salesorders-list.server.js +23 -37
  28. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=invoice-timing.check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invoice-timing.check.d.ts","sourceRoot":"","sources":["../../src/lib/invoice-timing.check.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ // Controle factuurtijdstip in Nederlandse tijd. Draai: bun src/lib/invoice-timing.check.ts
2
+ import { computeInvoiceDueAt, DEFAULT_INVOICE_TIMING } from "./invoice-timing.js";
3
+ let failed = 0;
4
+ function check(name, got, want) {
5
+ const ok = got === want;
6
+ if (!ok)
7
+ failed++;
8
+ console.log(`${ok ? "OK " : "FOUT"} ${name}: ${got}${ok ? "" : ` (verwacht ${want})`}`);
9
+ }
10
+ const s = { ...DEFAULT_INVOICE_TIMING, timeOfDay: "18:00" };
11
+ check("zomertijd 21-10 18:00 NL = 16:00 UTC", computeInvoiceDueAt({ expectedDeliveryDate: "2026-10-21" }, s), "2026-10-21T16:00:00.000Z");
12
+ check("wintertijd 2-11 18:00 NL = 17:00 UTC", computeInvoiceDueAt({ expectedDeliveryDate: "2026-11-02" }, s), "2026-11-02T17:00:00.000Z");
13
+ check("+1 dag over de omschakeling (24-10 → 25-10, wintertijd)", computeInvoiceDueAt({ expectedDeliveryDate: "2026-10-24" }, { ...s, offsetDays: 1 }), "2026-10-25T17:00:00.000Z");
14
+ check("tijdstip-ijkpunt laat op de avond telt Nederlandse kalenderdag", computeInvoiceDueAt({ createdAt: "2026-10-20T22:30:00Z" }, { ...s, anchor: "order_created", offsetDays: 1 }), "2026-10-22T16:00:00.000Z");
15
+ if (failed) {
16
+ console.error(`${failed} controle(s) mislukt`);
17
+ process.exit(1);
18
+ }
19
+ console.log("Alle controles geslaagd");
@@ -29,6 +29,8 @@ export interface InvoiceAnchorInput {
29
29
  selfPickup?: boolean;
30
30
  }
31
31
  export declare function normalizeTimeOfDay(value: unknown): string;
32
+ /** Zet de kalenderdag van `date` op tijdstip T in Nederlandse tijd (zomer-/wintertijd). */
33
+ export declare function applyTime(date: Date, timeOfDay: string): Date;
32
34
  /**
33
35
  * Berekent het moment waarop de order gefactureerd mag worden.
34
36
  * Geeft `null` wanneer het ijkpunt (nog) onbekend is.
@@ -1 +1 @@
1
- {"version":3,"file":"invoice-timing.d.ts","sourceRoot":"","sources":["../../src/lib/invoice-timing.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,WAAW,GACX,QAAQ,GACR,SAAS,GACT,mBAAmB,GACnB,aAAa,GACb,eAAe,CAAC;AAEpB,eAAO,MAAM,eAAe,EAAE;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAQ/E,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,aAAa,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,OAAO,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,sBAAsB,EAAE,qBAUpC,CAAC;AAEF,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOzD;AA+ED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,GAAG,IAAI,CAef;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,SAAS,UAAQ,GAAG,MAAM,CAWhG"}
1
+ {"version":3,"file":"invoice-timing.d.ts","sourceRoot":"","sources":["../../src/lib/invoice-timing.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,WAAW,GACX,QAAQ,GACR,SAAS,GACT,mBAAmB,GACnB,aAAa,GACb,eAAe,CAAC;AAEpB,eAAO,MAAM,eAAe,EAAE;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAQ/E,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,aAAa,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,OAAO,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,sBAAsB,EAAE,qBAUpC,CAAC;AAEF,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOzD;AAoDD,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAS7D;AAqCD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,GAAG,IAAI,CAef;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,SAAS,UAAQ,GAAG,MAAM,CAWhG"}
@@ -66,11 +66,29 @@ function addDays(date, days, businessOnly) {
66
66
  }
67
67
  return out;
68
68
  }
69
- function applyTime(date, timeOfDay) {
69
+ const INVOICE_TIME_ZONE = "Europe/Amsterdam";
70
+ /** Verschil (ms) tussen Amsterdamse kloktijd en UTC op dit moment. */
71
+ function zoneOffsetMs(at) {
72
+ const parts = new Intl.DateTimeFormat("en-US", {
73
+ timeZone: INVOICE_TIME_ZONE,
74
+ hourCycle: "h23",
75
+ year: "numeric", month: "2-digit", day: "2-digit",
76
+ hour: "2-digit", minute: "2-digit", second: "2-digit",
77
+ }).formatToParts(at);
78
+ const get = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
79
+ const asUtc = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour"), get("minute"), get("second"));
80
+ return asUtc - at.getTime();
81
+ }
82
+ /** Zet de kalenderdag van `date` op tijdstip T in Nederlandse tijd (zomer-/wintertijd). */
83
+ export function applyTime(date, timeOfDay) {
70
84
  const [h, m] = normalizeTimeOfDay(timeOfDay).split(":").map(Number);
71
- const out = new Date(date.getTime());
72
- out.setUTCHours(h ?? 18, m ?? 0, 0, 0);
73
- return out;
85
+ // Kalenderdag volgens Amsterdam (bij tijdstip-ijkpunten), anders de UTC-datum.
86
+ const off0 = zoneOffsetMs(date);
87
+ const local = new Date(date.getTime() + off0);
88
+ const wall = Date.UTC(local.getUTCFullYear(), local.getUTCMonth(), local.getUTCDate(), h ?? 18, m ?? 0, 0, 0);
89
+ let guess = wall - zoneOffsetMs(new Date(wall));
90
+ guess = wall - zoneOffsetMs(new Date(guess));
91
+ return new Date(guess);
74
92
  }
75
93
  function anchorDate(anchor, input) {
76
94
  switch (anchor) {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=order-departure.check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-departure.check.d.ts","sourceRoot":"","sources":["../../src/lib/order-departure.check.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ // Controle weergave ophaalmoment. Draai: bun src/lib/order-departure.check.ts
2
+ import { departureView, normalizeDeparture } from "./order-departure.js";
3
+ let failed = 0;
4
+ function check(name, got, want) {
5
+ const ok = got === want;
6
+ if (!ok)
7
+ failed++;
8
+ console.log(`${ok ? "OK " : "FOUT"} ${name}: ${got}${ok ? "" : ` (verwacht ${want})`}`);
9
+ }
10
+ const ho = departureView(normalizeDeparture({
11
+ ok: true, departureAt: "2026-10-20T13:00:00Z", signupDeadline: "2026-10-20T11:00:00Z", lateSignup: false,
12
+ }));
13
+ check("HO-2601441", ho.text, "di 20 okt 15:00 ophalen · aanmelden di 20 okt 13:00");
14
+ check("wintertijd", departureView(normalizeDeparture({ ok: true, departureAt: "2026-11-02T14:00:00Z" })).short, "ma 2 nov 15:00");
15
+ check("te laat aangemeld", departureView(normalizeDeparture({ ok: true, departureAt: "2026-10-20T13:00:00Z", lateSignup: true })).lateSignup, true);
16
+ const bad = departureView(normalizeDeparture({ ok: false, reason: "Geen vervoerder gekozen", departureAt: "2026-10-20T13:00:00Z" }));
17
+ check("ok=false: geen tijd", bad.text, null);
18
+ check("ok=false: reden", bad.reason, "Geen vervoerder gekozen");
19
+ check("geen antwoord: geen tijd", departureView(null).text, null);
20
+ check("ontbrekende velden", normalizeDeparture({ ok: true })?.departureAt, null);
21
+ if (failed) {
22
+ console.error(`${failed} controle(s) mislukt`);
23
+ process.exit(1);
24
+ }
25
+ console.log("Alle controles geslaagd");
@@ -0,0 +1,25 @@
1
+ export interface OrderDeparture {
2
+ ok: boolean;
3
+ reason: string | null;
4
+ carrierId: string | null;
5
+ momentKey: string | null;
6
+ departureAt: string | null;
7
+ signupDeadline: string | null;
8
+ lateSignup: boolean;
9
+ }
10
+ /** Engine-antwoord veilig omzetten; ontbrekende velden worden null. */
11
+ export declare function normalizeDeparture(raw: unknown): OrderDeparture | null;
12
+ /** "di 20 okt 15:00" in Nederlandse tijd. */
13
+ export declare function formatMoment(iso: string | null): string | null;
14
+ export declare const DEPARTURE_UNKNOWN = "Ophaalmoment nu niet bekend. Ververs over een minuut.";
15
+ export interface DepartureView {
16
+ /** Hoofdtekst, bv. "di 20 okt 15:00 ophalen · aanmelden di 20 okt 13:00". */
17
+ text: string | null;
18
+ /** Kort label voor de lijst, bv. "di 20 okt 15:00". */
19
+ short: string | null;
20
+ lateSignup: boolean;
21
+ /** Reden als er geen geldig ophaalmoment is (nooit met een tijd erbij). */
22
+ reason: string | null;
23
+ }
24
+ export declare function departureView(dep: OrderDeparture | null | undefined): DepartureView;
25
+ //# sourceMappingURL=order-departure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-departure.d.ts","sourceRoot":"","sources":["../../src/lib/order-departure.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;CACrB;AAQD,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAYtE;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAS9D;AAED,eAAO,MAAM,iBAAiB,0DAA0D,CAAC;AAEzF,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,uDAAuD;IACvD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAenF"}
@@ -0,0 +1,54 @@
1
+ // Ophaal- en aanmeldmoment van een order, zoals de Logistiek Engine het
2
+ // berekent (logistics_order_departure). Deze module rekent zelf niets uit:
3
+ // alleen normaliseren en weergeven.
4
+ const TIME_ZONE = "Europe/Amsterdam";
5
+ function str(v) {
6
+ return typeof v === "string" && v.trim() !== "" ? v : null;
7
+ }
8
+ /** Engine-antwoord veilig omzetten; ontbrekende velden worden null. */
9
+ export function normalizeDeparture(raw) {
10
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
11
+ return null;
12
+ const r = raw;
13
+ return {
14
+ ok: r.ok === true,
15
+ reason: str(r.reason),
16
+ carrierId: str(r.carrierId),
17
+ momentKey: str(r.momentKey),
18
+ departureAt: str(r.departureAt),
19
+ signupDeadline: str(r.signupDeadline),
20
+ lateSignup: r.lateSignup === true,
21
+ };
22
+ }
23
+ /** "di 20 okt 15:00" in Nederlandse tijd. */
24
+ export function formatMoment(iso) {
25
+ if (!iso)
26
+ return null;
27
+ const d = new Date(iso);
28
+ if (Number.isNaN(d.getTime()))
29
+ return null;
30
+ const date = d
31
+ .toLocaleDateString("nl-NL", { timeZone: TIME_ZONE, weekday: "short", day: "numeric", month: "short" })
32
+ .replace(/\./g, "");
33
+ const time = d.toLocaleTimeString("nl-NL", { timeZone: TIME_ZONE, hour: "2-digit", minute: "2-digit", hour12: false });
34
+ return `${date} ${time}`;
35
+ }
36
+ export const DEPARTURE_UNKNOWN = "Ophaalmoment nu niet bekend. Ververs over een minuut.";
37
+ export function departureView(dep) {
38
+ if (!dep)
39
+ return { text: null, short: null, lateSignup: false, reason: DEPARTURE_UNKNOWN };
40
+ const pickup = dep.ok ? formatMoment(dep.departureAt) : null;
41
+ if (!pickup) {
42
+ return {
43
+ text: null,
44
+ short: null,
45
+ lateSignup: false,
46
+ reason: dep.reason ?? "Er is geen ophaalmoment bekend voor deze order.",
47
+ };
48
+ }
49
+ const signup = formatMoment(dep.signupDeadline);
50
+ const parts = [`${pickup} ophalen`];
51
+ if (signup)
52
+ parts.push(`aanmelden ${signup}`);
53
+ return { text: parts.join(" · "), short: pickup, lateSignup: dep.lateSignup, reason: dep.ok ? null : dep.reason };
54
+ }
@@ -0,0 +1,4 @@
1
+ import { type OrderDeparture } from "./order-departure.js";
2
+ export declare function loadOrderDepartures(supabase: any, orderIds: string[]): Promise<Map<string, OrderDeparture>>;
3
+ export declare function forgetOrderDeparture(orderId: string): void;
4
+ //# sourceMappingURL=order-departure.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-departure.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-departure.server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAM5E,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAiDtC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,QAEnD"}
@@ -0,0 +1,62 @@
1
+ // Leest ophaal-/aanmeldmomenten bij de Engine. Gebruikt de lijstversie
2
+ // (logistics_order_departure_list) als die bestaat, anders één aanroep per
3
+ // order (maximaal 12 tegelijk). Fout => order ontbreekt in het resultaat.
4
+ import { normalizeDeparture } from "./order-departure.js";
5
+ const PARALLEL = 12;
6
+ const CACHE_MS = 30000;
7
+ const cache = new Map();
8
+ export async function loadOrderDepartures(supabase, orderIds) {
9
+ const out = new Map();
10
+ const now = Date.now();
11
+ const todo = [];
12
+ for (const id of Array.from(new Set(orderIds))) {
13
+ const hit = cache.get(id);
14
+ if (hit && now - hit.at < CACHE_MS)
15
+ out.set(id, hit.dep);
16
+ else
17
+ todo.push(id);
18
+ }
19
+ if (todo.length === 0)
20
+ return out;
21
+ // Lijstversie (Engine 1.0.162+): één aanroep.
22
+ try {
23
+ const { data, error } = await supabase.rpc("logistics_order_departure_list", { p_order_ids: todo });
24
+ if (!error) {
25
+ const rows = Array.isArray(data) ? data : [];
26
+ for (const row of rows) {
27
+ const r = (row && typeof row === "object" && "departure" in row ? row.departure : row);
28
+ const id = String(row?.order_id ?? row?.orderId ?? r?.orderId ?? "");
29
+ const dep = normalizeDeparture(r);
30
+ if (id && dep) {
31
+ out.set(id, dep);
32
+ cache.set(id, { at: now, dep });
33
+ }
34
+ }
35
+ return out;
36
+ }
37
+ }
38
+ catch {
39
+ /* lijstversie niet beschikbaar: per order */
40
+ }
41
+ for (let i = 0; i < todo.length; i += PARALLEL) {
42
+ await Promise.all(todo.slice(i, i + PARALLEL).map(async (id) => {
43
+ try {
44
+ const { data, error } = await supabase.rpc("logistics_order_departure", { p_order_id: id });
45
+ if (error)
46
+ return;
47
+ const dep = normalizeDeparture(data);
48
+ if (dep) {
49
+ out.set(id, dep);
50
+ cache.set(id, { at: now, dep });
51
+ }
52
+ }
53
+ catch {
54
+ /* fail closed: onbekend */
55
+ }
56
+ }));
57
+ }
58
+ return out;
59
+ }
60
+ export function forgetOrderDeparture(orderId) {
61
+ cache.delete(orderId);
62
+ }
@@ -1,14 +1,12 @@
1
+ import { type DepartureView, type OrderDeparture } from "./order-departure.js";
1
2
  export interface PickupMomentOption {
2
- /** Stabiele sleutel: pickupdatum|aanmeldtijd|aanlevertijd. */
3
+ /** Sleutel in Engine-formaat: "dagen@ophaaltijd|dagen@aanmeldtijd". */
3
4
  key: string;
4
- pickupDate: string;
5
- pickupTime: string | null;
6
- signupDate: string | null;
7
- signupTime: string | null;
8
5
  deliveryTime: string | null;
9
6
  label: string;
10
7
  }
11
- export declare function buildPickupOptions(carrier: any | null, deliveryISO: string | null): PickupMomentOption[];
8
+ /** Keuzes uit de vervoerdersmomenten — alleen tijden en dagen, geen datum. */
9
+ export declare function carrierMomentOptions(carrier: any | null): PickupMomentOption[];
12
10
  export declare const getOrderPickupMoment: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
13
11
  supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
14
12
  userId: string;
@@ -16,15 +14,12 @@ export declare const getOrderPickupMoment: import("@tanstack/start-client-core")
16
14
  }, undefined, undefined, undefined>], (input: unknown) => {
17
15
  orderId: string;
18
16
  }, Promise<{
19
- selectedKey: null;
20
- selected: null;
21
- options: PickupMomentOption[];
22
- hasCarrier?: undefined;
23
- } | {
24
- selectedKey: string;
25
- selected: PickupMomentOption;
17
+ view: DepartureView;
18
+ departure: OrderDeparture | null;
19
+ selectedKey: string | null;
26
20
  options: PickupMomentOption[];
27
21
  hasCarrier: boolean;
22
+ deliveryTime: string | null;
28
23
  }>>;
29
24
  export declare const setOrderPickupMoment: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
30
25
  supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
@@ -1 +1 @@
1
- {"version":3,"file":"order-pickup.functions.d.ts","sourceRoot":"","sources":["../../src/lib/order-pickup.functions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,GAAG,GAAG,IAAI,EACnB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,kBAAkB,EAAE,CAiDtB;AAqBD,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;aAYgC,kBAAkB,EAAE;;;;;;;GAiCjF,CAAC;AAEL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;GAqB7B,CAAC"}
1
+ {"version":3,"file":"order-pickup.functions.d.ts","sourceRoot":"","sources":["../../src/lib/order-pickup.functions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG3F,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAOD,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAoB9E;AAaD,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;;;;;GAyC7B,CAAC;AAEL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;GAoB7B,CAAC"}
@@ -1,75 +1,47 @@
1
- // Ophaalmoment (pickup moment) van een order lezen en wijzigen.
1
+ // Ophaalmoment van een order: tonen uit de Logistiek Engine, kiezen uit de
2
+ // momenten van de vervoerder. Deze module rekent zelf geen datum uit.
2
3
  import { createServerFn } from "@tanstack/react-start";
3
4
  import { z } from "zod";
4
5
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
5
6
  import { normalizeTime } from "./carrier-availability.js";
7
+ import { departureView } from "./order-departure.js";
6
8
  import { assertRowsAffected, resolveOrderRow } from "./order-row.server.js";
7
- function addDaysISO(iso, n) {
8
- const [y, m, d] = iso.split("-").map(Number);
9
- const dt = new Date(y, (m ?? 1) - 1, d ?? 1);
10
- dt.setDate(dt.getDate() + n);
11
- return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, "0")}-${String(dt.getDate()).padStart(2, "0")}`;
9
+ function daysText(n, what) {
10
+ if (n <= 0)
11
+ return `${what} op de leverdag`;
12
+ return `${what} ${n} werkdag${n === 1 ? "" : "en"} vóór levering`;
12
13
  }
13
- function fmtDate(iso) {
14
- const [y, m, d] = iso.split("-").map(Number);
15
- const dt = new Date(y, (m ?? 1) - 1, d ?? 1);
16
- if (Number.isNaN(dt.getTime()))
17
- return iso;
18
- return dt.toLocaleDateString("nl-NL", { weekday: "short", day: "numeric", month: "short" });
19
- }
20
- export function buildPickupOptions(carrier, deliveryISO) {
21
- if (!carrier || !deliveryISO)
22
- return [];
23
- const raw = Array.isArray(carrier.pickup_moments) ? carrier.pickup_moments : [];
24
- const moments = raw.length > 0
25
- ? raw
26
- : [carrier.signup_time_1, carrier.signup_time_2]
27
- .filter(Boolean)
28
- .map((t) => ({
29
- signup_time: t,
30
- signup_days: carrier.signup_days_before_delivery ?? 0,
31
- delivery_days: carrier.delivery_days ?? 0,
32
- }));
14
+ /** Keuzes uit de vervoerdersmomenten — alleen tijden en dagen, geen datum. */
15
+ export function carrierMomentOptions(carrier) {
16
+ const raw = Array.isArray(carrier?.pickup_moments) ? carrier.pickup_moments : [];
33
17
  const seen = new Set();
34
18
  const out = [];
35
- for (const m of moments) {
36
- const deliveryDays = Math.max(0, Number(m.delivery_days ?? carrier.delivery_days ?? 0) || 0);
37
- const pickupDate = addDaysISO(deliveryISO, -deliveryDays);
38
- const signupDays = Math.max(0, Number(m.signup_days ?? carrier.signup_days_before_delivery ?? 0) || 0);
39
- const signupTime = normalizeTime(m.signup_time);
40
- const signupDate = signupTime ? addDaysISO(pickupDate, -signupDays) : null;
41
- const pickupTime = normalizeTime(m.pickup_time);
42
- const deliveryTime = normalizeTime(m.delivery_time);
43
- const key = `${pickupDate}|${signupTime ?? ""}|${deliveryTime ?? ""}`;
19
+ for (const m of raw) {
20
+ const pt = normalizeTime(m.pickup_time);
21
+ if (!pt)
22
+ continue;
23
+ const pd = Math.max(0, Number(m.pickup_days ?? 0) || 0);
24
+ const st = normalizeTime(m.signup_time);
25
+ const sd = Math.max(0, Number(m.signup_days ?? pd) || 0);
26
+ const key = `${pd}@${pt}${st ? `|${sd}@${st}` : ""}`;
44
27
  if (seen.has(key))
45
28
  continue;
46
29
  seen.add(key);
47
- const parts = [`${fmtDate(pickupDate)}${pickupTime ? ` ${pickupTime}` : ""} ophalen`];
48
- if (signupDate && signupTime)
49
- parts.push(`aanmelden ${fmtDate(signupDate)} ${signupTime}`);
50
- if (deliveryTime)
51
- parts.push(`aanlevering ${deliveryTime}`);
52
- out.push({
53
- key,
54
- pickupDate,
55
- pickupTime,
56
- signupDate,
57
- signupTime,
58
- deliveryTime,
59
- label: parts.join(" · "),
60
- });
30
+ const dt = normalizeTime(m.delivery_time);
31
+ const parts = [`${daysText(pd, "ophalen")} ${pt}`];
32
+ if (st)
33
+ parts.push(`aanmelden ${st}`);
34
+ if (dt)
35
+ parts.push(`aanlevering ${dt}`);
36
+ out.push({ key, deliveryTime: dt, label: parts.join(" · ") });
61
37
  }
62
- out.sort((a, b) => a.pickupDate === b.pickupDate
63
- ? (a.deliveryTime ?? "99:99").localeCompare(b.deliveryTime ?? "99:99")
64
- : a.pickupDate.localeCompare(b.pickupDate));
65
38
  return out;
66
39
  }
67
40
  async function findOrder(supabase, orderId) {
68
- // DTO-id kan een Floriday salesOrderId zijn; resolveOrderRow vertaalt dat.
69
41
  const row = await resolveOrderRow(supabase, orderId);
70
42
  const { data, error } = await supabase
71
43
  .from("orders")
72
- .select("id, buffer_id, delivery_date, custom_fields")
44
+ .select("id, custom_fields")
73
45
  .eq("id", row.id)
74
46
  .maybeSingle();
75
47
  if (error)
@@ -89,48 +61,47 @@ export const getOrderPickupMoment = createServerFn({ method: "GET" })
89
61
  order = null;
90
62
  }
91
63
  if (!order) {
92
- return { selectedKey: null, selected: null, options: [] };
64
+ return {
65
+ view: departureView(null),
66
+ departure: null,
67
+ selectedKey: null,
68
+ options: [],
69
+ hasCarrier: false,
70
+ deliveryTime: null,
71
+ };
93
72
  }
94
73
  const cf = (order.custom_fields ?? {});
95
74
  const carrierId = cf.carrier ? String(cf.carrier) : null;
96
75
  let carrier = null;
97
76
  if (carrierId) {
98
- const { data: c } = await supabase
99
- .from("carriers")
100
- .select("delivery_days, signup_time_1, signup_time_2, signup_days_before_delivery, pickup_weekdays, delivery_weekdays, pickup_moments")
101
- .eq("id", carrierId)
102
- .maybeSingle();
77
+ const { data: c } = await supabase.from("carriers").select("pickup_moments").eq("id", carrierId).maybeSingle();
103
78
  carrier = c ?? null;
104
79
  }
105
- const options = buildPickupOptions(carrier, order.delivery_date);
106
- const storedKey = cf.pickup_moment ? String(cf.pickup_moment) : null;
107
- let selected = (storedKey ? options.find((o) => o.key === storedKey) : undefined) ?? null;
108
- if (!selected && cf.delivery_time) {
109
- const t = normalizeTime(cf.delivery_time);
110
- selected = options.find((o) => o.deliveryTime === t) ?? null;
111
- }
112
- if (!selected)
113
- selected = options[0] ?? null;
80
+ const { loadOrderDepartures } = await import("./order-departure.server.js");
81
+ const departure = (await loadOrderDepartures(supabase, [order.id])).get(order.id) ?? null;
82
+ const options = carrierMomentOptions(carrier);
83
+ const selectedKey = departure?.momentKey ?? (cf.pickup_moment ? String(cf.pickup_moment) : null);
114
84
  return {
115
- selectedKey: selected?.key ?? null,
116
- selected,
85
+ view: departureView(departure),
86
+ departure,
87
+ selectedKey: options.some((o) => o.key === selectedKey) ? selectedKey : null,
117
88
  options,
118
89
  hasCarrier: !!carrier,
90
+ deliveryTime: normalizeTime(cf.delivery_time),
119
91
  };
120
92
  });
121
93
  export const setOrderPickupMoment = createServerFn({ method: "POST" })
122
94
  .middleware([requireSupabaseAuth])
123
- .inputValidator((input) => z.object({ orderId: z.string().min(1), key: z.string().min(1) }).parse(input))
95
+ .inputValidator((input) => z.object({ orderId: z.string().min(1), key: z.string().min(1).max(64) }).parse(input))
124
96
  .handler(async ({ data, context }) => {
125
97
  const supabase = context.supabase;
126
98
  const order = await findOrder(supabase, data.orderId);
127
99
  if (!order)
128
100
  throw new Error("Order niet gevonden");
129
- const deliveryTime = data.key.split("|")[2] || null;
130
101
  const cf = { ...(order.custom_fields ?? {}) };
102
+ if (cf.pickup_moment === data.key)
103
+ return { ok: true };
131
104
  cf.pickup_moment = data.key;
132
- if (deliveryTime)
133
- cf.delivery_time = deliveryTime;
134
105
  const { data: updated, error } = await supabase.from("orders")
135
106
  .update({ custom_fields: cf })
136
107
  .eq("id", order.id)
@@ -138,5 +109,7 @@ export const setOrderPickupMoment = createServerFn({ method: "POST" })
138
109
  if (error)
139
110
  throw new Error(error.message);
140
111
  assertRowsAffected(updated, "Ophaalmoment opslaan");
112
+ const { forgetOrderDeparture } = await import("./order-departure.server.js");
113
+ forgetOrderDeparture(order.id);
141
114
  return { ok: true };
142
115
  });
@@ -1 +1 @@
1
- {"version":3,"file":"salesorders-list.server.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-list.server.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKjF,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC;AAExE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,GAAG,UAAU,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AA8ID,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,UAAU,EAClB,kBAAkB,GAAE,cAAyB,GAC5C,OAAO,CAAC,UAAU,CAAC,CAgnCrB"}
1
+ {"version":3,"file":"salesorders-list.server.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-list.server.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjF,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC;AAExE,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,GAAG,UAAU,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AA8ID,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,UAAU,EAClB,kBAAkB,GAAE,cAAyB,GAC5C,OAAO,CAAC,UAAU,CAAC,CAimCrB"}
@@ -12,7 +12,6 @@ import { combineStages } from "./floriday-status.js";
12
12
  import { combinedCartCount, mergeSummaries, summarize } from "./plan-coverage.js";
13
13
  import { resolveOrderNumbers } from "./order-numbers.server.js";
14
14
  import { mergeStages, resolveOrderStage } from "./order-status.js";
15
- import { buildPickupOptions } from "./order-pickup.functions.js";
16
15
  import { loadBacklogFlags } from "./order-backlog.server.js";
17
16
  const FO_CAP = 20000;
18
17
  const LOCAL_CAP = 5000;
@@ -802,42 +801,7 @@ export async function buildSalesOrdersList(supabase, params, connectionSupabase
802
801
  }
803
802
  // 12a-ter) Ladingdragers en indelingsstatus komen uit de Engine
804
803
  // (logistics_order_plan_coverage), alleen voor de zichtbare rijen — zie stap 14.
805
- // 12a-bis) Ophaalmoment per order (vervoerder + gekozen moment), kort label.
806
- {
807
- const shortPickup = (dateISO, time) => {
808
- const [y, m, d] = dateISO.split("-").map(Number);
809
- const dt = new Date(y ?? 0, (m ?? 1) - 1, d ?? 1);
810
- const date = Number.isNaN(dt.getTime())
811
- ? dateISO
812
- : dt
813
- .toLocaleDateString("nl-NL", { weekday: "short", day: "numeric", month: "short" })
814
- .replace(/\./g, "");
815
- return time ? `${date} ${time}` : date;
816
- };
817
- const localRowForDto = (o) => o.id.startsWith("local:")
818
- ? (localOrderRows.find((r) => String(r.id) === o.id.slice("local:".length)) ?? null)
819
- : (localRowByFoId.get(o.id) ?? null);
820
- for (const o of all) {
821
- const r = localRowForDto(o);
822
- if (!r)
823
- continue;
824
- const cf = r.custom_fields ?? {};
825
- const carrier = cf.carrier ? (carrierById.get(String(cf.carrier)) ?? null) : null;
826
- const deliveryISO = r.delivery_date ? String(r.delivery_date).slice(0, 10) : null;
827
- const options = buildPickupOptions(carrier, deliveryISO);
828
- if (options.length === 0)
829
- continue;
830
- const storedKey = cf.pickup_moment ? String(cf.pickup_moment) : null;
831
- let selected = (storedKey ? options.find((op) => op.key === storedKey) : undefined) ?? null;
832
- if (!selected && cf.delivery_time) {
833
- const t = normalizeTime(cf.delivery_time);
834
- selected = options.find((op) => op.deliveryTime === t) ?? null;
835
- }
836
- if (!selected)
837
- selected = options[0];
838
- o.pickupLabel = shortPickup(selected.pickupDate, selected.pickupTime ?? selected.signupTime);
839
- }
840
- }
804
+ // 12a-bis) Ophaalmoment komt uit de Engine, alleen voor zichtbare rijen (stap 14).
841
805
  // 12a) Weggehaalde beoordelingsmeldingen (vals alarm) — los vastgelegd,
842
806
  // zodat de order zelf niet gewijzigd hoeft te worden.
843
807
  {
@@ -1074,6 +1038,28 @@ export async function buildSalesOrdersList(supabase, params, connectionSupabase
1074
1038
  : (localRowByFoId.get(o.id)?.id ? String(localRowByFoId.get(o.id).id) : null);
1075
1039
  const ids = visible.map(idFor).filter((v) => !!v);
1076
1040
  if (ids.length) {
1041
+ // Ophaalmoment uit de Engine (nooit zelf rekenen).
1042
+ try {
1043
+ const { loadOrderDepartures } = await import("./order-departure.server.js");
1044
+ const { departureView } = await import("./order-departure.js");
1045
+ const deps = await loadOrderDepartures(supabase, ids);
1046
+ for (const o of visible) {
1047
+ const oid = idFor(o);
1048
+ if (!oid)
1049
+ continue;
1050
+ const v = departureView(deps.get(oid));
1051
+ o.pickupLabel = v.short
1052
+ ? `${v.short}${v.lateSignup ? " · te laat aangemeld" : ""}`
1053
+ : deps.has(oid)
1054
+ ? (v.reason ?? "onbekend")
1055
+ : "onbekend";
1056
+ }
1057
+ }
1058
+ catch {
1059
+ for (const o of visible)
1060
+ if (idFor(o))
1061
+ o.pickupLabel = "onbekend";
1062
+ }
1077
1063
  const coverage = await loadPlanCoverage(supabase, ids, "list");
1078
1064
  let failed = 0;
1079
1065
  for (const o of visible) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-verkoop-module",
3
- "version": "1.0.90",
3
+ "version": "1.0.93",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",