@flowselections/floriday-afhandeling-module 1.0.41 → 1.0.43
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/AutoProcessStatus.d.ts +3 -1
- package/dist-lib/components/afhandeling/AutoProcessStatus.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/AutoProcessStatus.js +36 -3
- package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControlePage.js +53 -13
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +409 -120
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickAgendaView.js +4 -4
- package/dist-lib/components/afhandeling/PickingPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickingPage.js +1 -1
- package/dist-lib/components/afhandeling/ScanControleView.d.ts +2 -0
- package/dist-lib/components/afhandeling/ScanControleView.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/ScanControleView.js +158 -0
- package/dist-lib/components/afhandeling/WeekAgendaView.js +1 -1
- package/dist-lib/hooks/useCartIndex.d.ts.map +1 -1
- package/dist-lib/hooks/useCartIndex.js +6 -1
- package/dist-lib/hooks/useEngineTasks.js +1 -1
- package/dist-lib/hooks/useKnownOrders.d.ts +11 -0
- package/dist-lib/hooks/useKnownOrders.d.ts.map +1 -0
- package/dist-lib/hooks/useKnownOrders.js +54 -0
- package/dist-lib/hooks/useOrderBuffers.d.ts +17 -0
- package/dist-lib/hooks/useOrderBuffers.d.ts.map +1 -0
- package/dist-lib/hooks/useOrderBuffers.js +53 -0
- package/dist-lib/hooks/useOrderPickDates.d.ts +10 -0
- package/dist-lib/hooks/useOrderPickDates.d.ts.map +1 -0
- package/dist-lib/hooks/useOrderPickDates.js +62 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts +1 -3
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderPlanning.js +9 -13
- package/dist-lib/hooks/usePickAgenda.d.ts +1 -6
- package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
- package/dist-lib/hooks/usePickAgenda.js +62 -9
- package/dist-lib/hooks/usePickMode.d.ts +10 -0
- package/dist-lib/hooks/usePickMode.d.ts.map +1 -0
- package/dist-lib/hooks/usePickMode.js +31 -0
- package/dist-lib/index.d.ts +2 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +19 -6
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +3598 -1225
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +3598 -1225
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +3598 -1225
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +3645 -1246
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +6076 -1330
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/carriers.functions.d.ts +15052 -3187
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.d.ts +15052 -3187
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.d.ts +55913 -6313
- package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.js +134 -10
- package/dist-lib/lib/deliveryNote.functions.d.ts +25891 -0
- package/dist-lib/lib/deliveryNote.functions.d.ts.map +1 -0
- package/dist-lib/lib/deliveryNote.functions.js +32 -0
- package/dist-lib/lib/engineLayouts.functions.d.ts +25889 -0
- package/dist-lib/lib/engineLayouts.functions.d.ts.map +1 -0
- package/dist-lib/lib/engineLayouts.functions.js +42 -0
- package/dist-lib/lib/floriday.functions.d.ts +28979 -712
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +118 -343
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts +17 -0
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts.map +1 -1
- package/dist-lib/lib/logisticsEngine/reschedule.js +63 -0
- package/dist-lib/lib/orderFulfilment.functions.d.ts +25891 -0
- package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -0
- package/dist-lib/lib/orderFulfilment.functions.js +35 -0
- package/dist-lib/lib/orderStatus.d.ts +10 -0
- package/dist-lib/lib/orderStatus.d.ts.map +1 -0
- package/dist-lib/lib/orderStatus.js +81 -0
- package/dist-lib/lib/pickDates.functions.d.ts +25896 -0
- package/dist-lib/lib/pickDates.functions.d.ts.map +1 -0
- package/dist-lib/lib/pickDates.functions.js +56 -0
- package/dist-lib/lib/pickMode.d.ts +14 -0
- package/dist-lib/lib/pickMode.d.ts.map +1 -0
- package/dist-lib/lib/pickMode.js +103 -0
- package/dist-lib/lib/printing.d.ts +3 -1
- package/dist-lib/lib/printing.d.ts.map +1 -1
- package/dist-lib/lib/printing.js +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AA2BD,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+
|
|
1
|
+
{"version":3,"file":"PickAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickAgendaView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC/C;AA2BD,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+BAwFnE"}
|
|
@@ -20,9 +20,9 @@ function CategoryLine({ items, label, }) {
|
|
|
20
20
|
export function PickAgendaView({ onOrderClick }) {
|
|
21
21
|
const [weekStart, setWeekStart] = useState(() => currentWeekStart());
|
|
22
22
|
const range = useMemo(() => agendaRange(weekStart), [weekStart]);
|
|
23
|
-
const { planned, awaiting,
|
|
23
|
+
const { planned, awaiting, tasksWithoutStart, nearestDeliveryDate, signalError, lastEngineRun, isLoading, error, warning, refetch, } = useOrderPlanning(range);
|
|
24
24
|
const awaitingCount = awaiting.length;
|
|
25
|
-
const anyIssue = awaitingCount +
|
|
25
|
+
const anyIssue = awaitingCount + tasksWithoutStart.length > 0;
|
|
26
26
|
const lastRunLabel = lastEngineRun
|
|
27
27
|
? new Date(lastEngineRun).toLocaleString("nl-NL", {
|
|
28
28
|
timeZone: "Europe/Amsterdam",
|
|
@@ -31,11 +31,11 @@ export function PickAgendaView({ onOrderClick }) {
|
|
|
31
31
|
})
|
|
32
32
|
: null;
|
|
33
33
|
const [dayDetailDate, setDayDetailDate] = useState(null);
|
|
34
|
-
return (_jsxs("div", { className: "
|
|
34
|
+
return (_jsxs("div", { className: "flex h-full flex-col gap-6 overflow-hidden", children: [anyIssue && (_jsxs("div", { className: "shrink-0 space-y-1 rounded-md border border-sky-300 bg-sky-50 p-4 text-sm text-sky-900", children: [_jsx(CategoryLine, { items: awaiting, label: (n) => `${n} order${n === 1 ? "" : "s"} zonder indeling van de Logistics Engine, daarom nog niet in de agenda` }), _jsx(CategoryLine, { items: tasksWithoutStart, label: (n) => `${n} order${n === 1 ? "" : "s"} met een Engine-taak zonder geldige starttijd` }), _jsxs("div", { className: "text-xs", children: [lastRunLabel
|
|
35
35
|
? `Laatste indeling door de Engine: ${lastRunLabel}.`
|
|
36
36
|
: "Er is nog geen indeling van de Engine bekend voor deze periode.", " ", awaitingCount === 0
|
|
37
37
|
? "Orders die buiten deze week zijn ingedeeld staan hier niet bij."
|
|
38
38
|
: signalError
|
|
39
39
|
? `Melding vanuit deze module is mislukt: ${signalError}`
|
|
40
|
-
: "Melding vanuit deze module is verstuurd; het inplannen gebeurt door de Logistics Engine."] })] })), warning && !error && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 p-4 text-sm text-amber-900", children: [warning, " De beschikbare orders blijven zichtbaar."] })), error ? (_jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive", children: [error, _jsx(Button, { size: "sm", variant: "outline", className: "ml-3", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : (_jsx(WeekAgendaView, { planned: planned, mode: "pick", isLoading: isLoading, onOrderClick: onOrderClick, onDayClick: setDayDetailDate, onWeekChange: setWeekStart, jumpToDate: nearestDeliveryDate })), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: planned, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
|
|
40
|
+
: "Melding vanuit deze module is verstuurd; het inplannen gebeurt door de Logistics Engine."] })] })), warning && !error && (_jsxs("div", { className: "shrink-0 rounded-md border border-amber-300 bg-amber-50 p-4 text-sm text-amber-900", children: [warning, " De beschikbare orders blijven zichtbaar."] })), error ? (_jsxs("div", { className: "shrink-0 rounded-md border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive", children: [error, _jsx(Button, { size: "sm", variant: "outline", className: "ml-3", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : (_jsx("div", { className: "min-h-0 flex-1", children: _jsx(WeekAgendaView, { planned: planned, mode: "pick", isLoading: isLoading, onOrderClick: onOrderClick, onDayClick: setDayDetailDate, onWeekChange: setWeekStart, jumpToDate: nearestDeliveryDate }) })), _jsx(DagDetailDialog, { open: !!dayDetailDate, date: dayDetailDate, planned: planned, onOpenChange: (o) => !o && setDayDetailDate(null) })] }));
|
|
41
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickingPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickingPage.tsx"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,
|
|
1
|
+
{"version":3,"file":"PickingPage.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/PickingPage.tsx"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,gCAc1B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { PickAgendaView } from "./PickAgendaView";
|
|
3
3
|
export function PickingPage() {
|
|
4
|
-
return (_jsxs("main", { className: "
|
|
4
|
+
return (_jsxs("main", { className: "flex h-full flex-col gap-6 overflow-hidden p-6", children: [_jsxs("div", { className: "shrink-0", children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Pick Agenda" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Overzicht van alle orders die gepickt moeten worden." })] }), _jsx("div", { className: "min-h-0 flex-1", children: _jsx(PickAgendaView, {}) })] }));
|
|
5
5
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScanControleView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/ScanControleView.tsx"],"names":[],"mappings":"AA4FA,wBAAgB,gBAAgB,gCAqV/B"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { supabase } from "../../integrations/supabase/moduleClient";
|
|
4
|
+
import { Button, Badge, Card, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
5
|
+
import { AlertTriangle, CheckCircle2, RefreshCw, ScanLine } from "lucide-react";
|
|
6
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
7
|
+
const AFGEROND_STATUS = "geprint";
|
|
8
|
+
function statusVariant(status) {
|
|
9
|
+
if (status === AFGEROND_STATUS)
|
|
10
|
+
return "default";
|
|
11
|
+
if (status === "mislukt")
|
|
12
|
+
return "destructive";
|
|
13
|
+
return "secondary";
|
|
14
|
+
}
|
|
15
|
+
function tijd(iso) {
|
|
16
|
+
if (!iso)
|
|
17
|
+
return "—";
|
|
18
|
+
const d = new Date(iso);
|
|
19
|
+
return Number.isNaN(d.getTime())
|
|
20
|
+
? "—"
|
|
21
|
+
: d.toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" });
|
|
22
|
+
}
|
|
23
|
+
// --------------------------------------------------------------------------
|
|
24
|
+
export function ScanControleView() {
|
|
25
|
+
const qc = useQueryClient();
|
|
26
|
+
const [barcode, setBarcode] = useState("");
|
|
27
|
+
const [lookup, setLookup] = useState(null);
|
|
28
|
+
const inputRef = useRef(null);
|
|
29
|
+
const settingsQ = useQuery({
|
|
30
|
+
queryKey: ["logistics-control-settings"],
|
|
31
|
+
queryFn: async () => {
|
|
32
|
+
const { data, error } = await supabase.rpc("logistics_control_settings");
|
|
33
|
+
if (error)
|
|
34
|
+
throw new Error(error.message);
|
|
35
|
+
return (data ?? {});
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const settings = settingsQ.data;
|
|
39
|
+
const scannerId = settings?.scannerId ?? null;
|
|
40
|
+
const printerId = settings?.printerId ?? null;
|
|
41
|
+
const postKlaar = Boolean(scannerId && printerId);
|
|
42
|
+
const overviewQ = useQuery({
|
|
43
|
+
queryKey: ["logistics-control-scan-overview", scannerId],
|
|
44
|
+
enabled: Boolean(scannerId),
|
|
45
|
+
refetchInterval: 15000,
|
|
46
|
+
queryFn: async () => {
|
|
47
|
+
const { data, error } = await supabase.rpc("logistics_control_scan_overview", {
|
|
48
|
+
_scanner_id: scannerId ?? undefined,
|
|
49
|
+
_date: new Date().toISOString().slice(0, 10),
|
|
50
|
+
});
|
|
51
|
+
if (error)
|
|
52
|
+
throw new Error(error.message);
|
|
53
|
+
return (data ?? []);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
// Ververs zodra de Engine een scan bijwerkt.
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const channel = supabase
|
|
59
|
+
.channel("logistics-control-scan")
|
|
60
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "logistics_control_scan" }, () => {
|
|
61
|
+
void qc.invalidateQueries({ queryKey: ["logistics-control-scan-overview"] });
|
|
62
|
+
})
|
|
63
|
+
.subscribe();
|
|
64
|
+
return () => {
|
|
65
|
+
void supabase.removeChannel(channel);
|
|
66
|
+
};
|
|
67
|
+
}, [qc]);
|
|
68
|
+
const lookupM = useMutation({
|
|
69
|
+
mutationFn: async (code) => {
|
|
70
|
+
const { data, error } = await supabase.rpc("logistics_control_scan_lookup", {
|
|
71
|
+
p_barcode: code,
|
|
72
|
+
});
|
|
73
|
+
if (error)
|
|
74
|
+
throw new Error(error.message);
|
|
75
|
+
return (data ?? { ok: false, error: "Geen antwoord van de controlepost." });
|
|
76
|
+
},
|
|
77
|
+
onSuccess: (res) => {
|
|
78
|
+
setLookup(res);
|
|
79
|
+
if (!res.ok)
|
|
80
|
+
toast.error(res.error ?? "Deze barcode is niet herkend.");
|
|
81
|
+
},
|
|
82
|
+
onError: (err) => toast.error(err.message),
|
|
83
|
+
});
|
|
84
|
+
const approveM = useMutation({
|
|
85
|
+
mutationFn: async (vars) => {
|
|
86
|
+
const { data, error } = await supabase.rpc("logistics_control_scan_approve", {
|
|
87
|
+
p_barcode: vars.code,
|
|
88
|
+
p_scanner_id: scannerId ?? undefined,
|
|
89
|
+
p_printer_id: printerId ?? undefined,
|
|
90
|
+
p_allow_reprint: vars.allowReprint,
|
|
91
|
+
});
|
|
92
|
+
if (error)
|
|
93
|
+
throw new Error(error.message);
|
|
94
|
+
return (data ?? {});
|
|
95
|
+
},
|
|
96
|
+
onSuccess: (res) => {
|
|
97
|
+
void qc.invalidateQueries({ queryKey: ["logistics-control-scan-overview"] });
|
|
98
|
+
if (!res.ok) {
|
|
99
|
+
setLookup((prev) => (prev ? { ...prev, ...res } : res));
|
|
100
|
+
toast.error(res.error ?? "Goedkeuren is niet gelukt.");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
toast.success(res.isFloriday
|
|
104
|
+
? "Goedgekeurd — de Floriday-afleverbrief wordt opgehaald en geprint."
|
|
105
|
+
: "Goedgekeurd — de afleverbrief staat klaar op de printer.");
|
|
106
|
+
setLookup(null);
|
|
107
|
+
setBarcode("");
|
|
108
|
+
inputRef.current?.focus();
|
|
109
|
+
},
|
|
110
|
+
onError: (err) => toast.error(err.message),
|
|
111
|
+
});
|
|
112
|
+
const retryM = useMutation({
|
|
113
|
+
mutationFn: async (id) => {
|
|
114
|
+
const { data, error } = await supabase.rpc("logistics_control_scan_retry", { _id: id });
|
|
115
|
+
if (error)
|
|
116
|
+
throw new Error(error.message);
|
|
117
|
+
return data;
|
|
118
|
+
},
|
|
119
|
+
onSuccess: (res) => {
|
|
120
|
+
void qc.invalidateQueries({ queryKey: ["logistics-control-scan-overview"] });
|
|
121
|
+
if (res && res.ok === false)
|
|
122
|
+
toast.error(res.error ?? "Opnieuw proberen is niet gelukt.");
|
|
123
|
+
else
|
|
124
|
+
toast.success("Opnieuw aangeboden aan de controlepost.");
|
|
125
|
+
},
|
|
126
|
+
onError: (err) => toast.error(err.message),
|
|
127
|
+
});
|
|
128
|
+
const bezig = lookupM.isPending || approveM.isPending;
|
|
129
|
+
const rows = overviewQ.data ?? [];
|
|
130
|
+
const stats = useMemo(() => ({
|
|
131
|
+
totaal: rows.length,
|
|
132
|
+
geprint: rows.filter((r) => r.status === AFGEROND_STATUS).length,
|
|
133
|
+
wacht: rows.filter((r) => r.status === "afleverbrief_aangevraagd" || r.status === "goedgekeurd").length,
|
|
134
|
+
mislukt: rows.filter((r) => r.status === "mislukt").length,
|
|
135
|
+
}), [rows]);
|
|
136
|
+
function scan(code) {
|
|
137
|
+
const c = code.trim();
|
|
138
|
+
if (!c || !postKlaar)
|
|
139
|
+
return;
|
|
140
|
+
setLookup(null);
|
|
141
|
+
lookupM.mutate(c);
|
|
142
|
+
}
|
|
143
|
+
if (settingsQ.isLoading) {
|
|
144
|
+
return _jsx("div", { className: "text-sm text-muted-foreground", children: "Controlepost laden\u2026" });
|
|
145
|
+
}
|
|
146
|
+
if (!postKlaar) {
|
|
147
|
+
return (_jsx(Card, { className: "p-4 border-destructive/40 bg-destructive/10", children: _jsxs("div", { className: "flex items-start gap-2 text-destructive", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0" }), _jsxs("div", { className: "text-sm", children: [_jsx("div", { className: "font-medium", children: "Controlepost nog niet ingesteld" }), _jsxs("p", { children: ["Er is nog geen ", scannerId ? "printer" : printerId ? "scanner" : "scanner en printer", " ", "gekozen bij Logistiek Engine \u2192 stap 6 \u201CControle\u201D \u2192 Scancontrole. Zolang dat ontbreekt kan er niet gescand worden."] })] })] }) }));
|
|
148
|
+
}
|
|
149
|
+
const cart = lookup?.cart ?? null;
|
|
150
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { className: "p-4 space-y-3", children: [_jsxs("form", { className: "flex gap-2", onSubmit: (e) => {
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
scan(barcode);
|
|
153
|
+
}, children: [_jsx(Input, { ref: inputRef, autoFocus: true, value: barcode, onChange: (e) => setBarcode(e.target.value), placeholder: "Scan de barcode op de pakbon\u2026", disabled: bezig }), _jsxs(Button, { type: "submit", disabled: bezig || !barcode.trim(), children: [_jsx(ScanLine, { className: "h-4 w-4 mr-2" }), lookupM.isPending ? "Zoeken…" : "Opzoeken"] })] }), lookup && !lookup.ok && (_jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: lookup.error ?? "Deze barcode is niet herkend." })), lookup?.ok && (_jsxs("div", { className: "rounded-md border p-3 space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-semibold", children: lookup.order?.orderNumber ?? "—" }), _jsx(Badge, { variant: lookup.isFloriday ? "default" : "outline", children: lookup.isFloriday ? "Floriday-order (FO)" : "Eigen order" }), _jsx(Badge, { variant: "secondary", children: cart?.cartLabel ?? "Kar onbekend" }), lookup.alreadyControlled && (_jsxs(Badge, { variant: "destructive", children: ["Al gecontroleerd \u2014 ", lookup.scan?.status] }))] }), _jsxs("div", { className: "text-sm text-muted-foreground", children: [lookup.order?.customerName ?? "—", cart?.cartNumber ? ` · karnummer ${cart.cartNumber}` : "", typeof cart?.occupancyPct === "number" ? ` · ${Math.round(cart.occupancyPct)}% vol` : ""] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Product" }), _jsx(TableHead, { className: "text-right", children: "Fusten" }), _jsx(TableHead, { className: "text-right", children: "Aantal" })] }) }), _jsx(TableBody, { children: (cart?.items ?? []).length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, className: "text-sm text-muted-foreground", children: "Geen belading gevonden op deze kar." }) })) : ((cart?.items ?? []).map((it, i) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: it.productName ?? "—" }), _jsx(TableCell, { className: "text-right", children: it.crates ?? "—" }), _jsx(TableCell, { className: "text-right", children: it.quantity ?? "—" })] }, it.itemId ?? i)))) })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { disabled: approveM.isPending, onClick: () => approveM.mutate({ code: lookup.barcode ?? barcode, allowReprint: false }), children: [_jsx(CheckCircle2, { className: "h-4 w-4 mr-2" }), approveM.isPending ? "Bezig…" : "Goedkeuren"] }), lookup.alreadyControlled && (_jsx(Button, { variant: "outline", disabled: approveM.isPending, onClick: () => approveM.mutate({ code: lookup.barcode ?? barcode, allowReprint: true }), children: "Toch opnieuw printen" })), _jsx(Button, { variant: "ghost", disabled: approveM.isPending, onClick: () => {
|
|
154
|
+
setLookup(null);
|
|
155
|
+
setBarcode("");
|
|
156
|
+
inputRef.current?.focus();
|
|
157
|
+
}, children: "Annuleren" })] })] }))] }), _jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Gescand vandaag" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.totaal })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Geprint" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.geprint })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "In behandeling" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.wacht })] }), _jsxs(Card, { className: "p-3", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Mislukt" }), _jsx("div", { className: "text-2xl font-semibold", children: stats.mislukt })] })] }), _jsx(Card, { className: "p-0 overflow-hidden", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Ordernummer" }), _jsx(TableHead, { children: "Soort" }), _jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Melding" }), _jsx(TableHead, { children: "Bijgewerkt" }), _jsx(TableHead, {})] }) }), _jsx(TableBody, { children: overviewQ.isLoading ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 6, className: "text-sm text-muted-foreground", children: "Laden\u2026" }) })) : rows.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 6, className: "text-sm text-muted-foreground", children: "Vandaag nog niets gescand." }) })) : (rows.map((r) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: r.orderNumber ?? "—" }), _jsx(TableCell, { children: r.isFloriday ? "FO" : "Eigen" }), _jsx(TableCell, { children: _jsx(Badge, { variant: statusVariant(r.status), children: r.status }) }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: r.error ?? "—" }), _jsx(TableCell, { children: tijd(r.updatedAt) }), _jsx(TableCell, { className: "text-right", children: r.status === "mislukt" && (_jsxs(Button, { size: "sm", variant: "outline", disabled: retryM.isPending, onClick: () => retryM.mutate(r.id), children: [_jsx(RefreshCw, { className: "h-4 w-4 mr-2" }), "Opnieuw proberen"] })) })] }, r.id)))) })] }) })] }));
|
|
158
|
+
}
|
|
@@ -136,7 +136,7 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
|
|
|
136
136
|
d.setHours(0, 0, 0, 0);
|
|
137
137
|
setCursor(startOfWeek(d));
|
|
138
138
|
};
|
|
139
|
-
return (_jsxs("div", { className: "
|
|
139
|
+
return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-hidden", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("h2", { className: "text-xl font-semibold tracking-tight", children: title ?? headerLabel }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: goPrev, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), " Vorige week"] }), _jsx(Button, { variant: "outline", size: "sm", onClick: goToday, children: "Deze week" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: goNext, children: ["Volgende week ", _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }), misaligned && (_jsx("div", { className: "rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground", children: "Aantallen worden opnieuw uitgelijnd." })), _jsxs("div", { className: "grid shrink-0 grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4", children: [_jsx(SummaryCard, { label: MODE_LABELS[mode].summary, value: totals.leveringen, tone: "default" }), _jsx(SummaryCard, { label: "Verzendklaar", value: totals.verzendklaar, tone: "success" }), _jsx(SummaryCard, { label: "In voorbereiding", value: totals.voorbereiding, tone: "muted" }), _jsx(SummaryCard, { label: "Totaal stuks", value: totals.totaalStuks, tone: "default" })] }), _jsx("div", { className: "min-h-0 flex-1 space-y-2 overflow-y-auto rounded-lg border bg-muted/20 p-2", children: isLoading || (mode === "pick" && pickAgenda.isLoading) ? (_jsx("div", { className: "rounded-lg border bg-card p-10 text-center text-sm text-muted-foreground", children: "Laden\u2026" })) : (days.map((day) => (_jsx(DayRow, { day: day, onOrderClick: onOrderClick, onDayClick: onDayClick, capacity: mode === "delivery" ? undefined : capacity.byDate.get(toDayKey(day.date)) }, day.date.toISOString())))) })] }));
|
|
140
140
|
}
|
|
141
141
|
function SummaryCard({ label, value, tone, }) {
|
|
142
142
|
const valueClass = tone === "success" ? "text-green-600" : "text-foreground";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCartIndex.d.ts","sourceRoot":"","sources":["../../src/hooks/useCartIndex.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useCartIndex.d.ts","sourceRoot":"","sources":["../../src/hooks/useCartIndex.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;6BAqBrB,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,MAAM,GAAG,IAAI;;;EA4B1F"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { supabase } from "../integrations/supabase/moduleClient";
|
|
4
|
+
import { useKnownOrders } from "./useKnownOrders";
|
|
4
5
|
/**
|
|
5
6
|
* Gedeelde karindex van de Engine. Dit is de enige bron voor karrentellingen:
|
|
6
7
|
* taak-payloads (cartCount) en eigen tellingen op order_carts zijn niet meer
|
|
@@ -23,6 +24,7 @@ export function useCartIndex(from, to) {
|
|
|
23
24
|
return Array.isArray(data) ? data : [];
|
|
24
25
|
},
|
|
25
26
|
});
|
|
27
|
+
const { isKnownRef } = useKnownOrders();
|
|
26
28
|
const rows = query.data;
|
|
27
29
|
// Per orderreferentie de unieke karren tellen.
|
|
28
30
|
const cartsForOrder = useCallback((...refs) => {
|
|
@@ -31,6 +33,9 @@ export function useCartIndex(from, to) {
|
|
|
31
33
|
const cart = r.cart_id ?? "";
|
|
32
34
|
if (!cart)
|
|
33
35
|
continue;
|
|
36
|
+
// Karren van orders die de Verkoop-module niet kent tellen niet mee.
|
|
37
|
+
if (!isKnownRef(r.order_number, r.order_uuid))
|
|
38
|
+
continue;
|
|
34
39
|
for (const ref of [r.order_number, r.order_uuid]) {
|
|
35
40
|
if (!ref)
|
|
36
41
|
continue;
|
|
@@ -47,7 +52,7 @@ export function useCartIndex(from, to) {
|
|
|
47
52
|
return set.size;
|
|
48
53
|
}
|
|
49
54
|
return null;
|
|
50
|
-
}, [rows]);
|
|
55
|
+
}, [rows, isKnownRef]);
|
|
51
56
|
return {
|
|
52
57
|
cartsForOrder,
|
|
53
58
|
isLoading: query.isLoading,
|
|
@@ -25,7 +25,7 @@ export function useEngineTasks(options) {
|
|
|
25
25
|
const timerRef = useRef(null);
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
const channel = supabase
|
|
28
|
-
.channel(
|
|
28
|
+
.channel(`engine-tasks-${Math.random().toString(36).slice(2)}`)
|
|
29
29
|
.on("postgres_changes", { event: "*", schema: "public", table: "logistics_tasks" }, () => {
|
|
30
30
|
if (timerRef.current)
|
|
31
31
|
return;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alle orderreferenties die de Verkoop-module kent. Alles wat hier niet in
|
|
3
|
+
* staat, hoort in deze module niet zichtbaar te zijn.
|
|
4
|
+
*/
|
|
5
|
+
export declare function useKnownOrders(): {
|
|
6
|
+
refs: Set<string>;
|
|
7
|
+
isKnownRef: (...candidates: (string | null | undefined)[]) => boolean;
|
|
8
|
+
isReady: boolean;
|
|
9
|
+
isLoading: boolean;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=useKnownOrders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useKnownOrders.d.ts","sourceRoot":"","sources":["../../src/hooks/useKnownOrders.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAgB,cAAc;;gCA4BV,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,OAAO;;;EAY1D"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { listKnownOrders } from "../lib/floriday.functions";
|
|
5
|
+
import { normalizeOrderNumber } from "../lib/orderRefs";
|
|
6
|
+
function keyOf(value) {
|
|
7
|
+
if (!value)
|
|
8
|
+
return null;
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (!trimmed || trimmed === "—" || trimmed === "-")
|
|
11
|
+
return null;
|
|
12
|
+
return normalizeOrderNumber(trimmed) ?? trimmed.toUpperCase();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Alle orderreferenties die de Verkoop-module kent. Alles wat hier niet in
|
|
16
|
+
* staat, hoort in deze module niet zichtbaar te zijn.
|
|
17
|
+
*/
|
|
18
|
+
export function useKnownOrders() {
|
|
19
|
+
const fetchKnown = useServerFn(listKnownOrders);
|
|
20
|
+
const query = useQuery({
|
|
21
|
+
queryKey: ["known-orders"],
|
|
22
|
+
queryFn: () => fetchKnown(),
|
|
23
|
+
retry: 1,
|
|
24
|
+
staleTime: 30000,
|
|
25
|
+
refetchInterval: 60000,
|
|
26
|
+
refetchOnWindowFocus: true,
|
|
27
|
+
});
|
|
28
|
+
const refs = useMemo(() => {
|
|
29
|
+
const set = new Set();
|
|
30
|
+
for (const o of query.data?.orders ?? []) {
|
|
31
|
+
const id = keyOf(o.id);
|
|
32
|
+
const num = keyOf(o.orderNumber);
|
|
33
|
+
if (id)
|
|
34
|
+
set.add(id);
|
|
35
|
+
if (num)
|
|
36
|
+
set.add(num);
|
|
37
|
+
}
|
|
38
|
+
return set;
|
|
39
|
+
}, [query.data]);
|
|
40
|
+
// Zodra de lijst geladen is filteren we, ook als er niets open staat.
|
|
41
|
+
const isReady = !query.isLoading && !!query.data && !query.data.error;
|
|
42
|
+
/** Onbekende referentie? Dan niet tonen. Zolang de lijst laadt: niets filteren. */
|
|
43
|
+
const isKnownRef = useCallback((...candidates) => {
|
|
44
|
+
if (!isReady)
|
|
45
|
+
return true;
|
|
46
|
+
for (const c of candidates) {
|
|
47
|
+
const k = keyOf(c);
|
|
48
|
+
if (k && refs.has(k))
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}, [refs, isReady]);
|
|
53
|
+
return { refs, isKnownRef, isReady, isLoading: query.isLoading };
|
|
54
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Bufferkoppeling van een order zoals de Engine die heeft vastgelegd. */
|
|
2
|
+
export interface OrderBufferInfo {
|
|
3
|
+
bufferId: string;
|
|
4
|
+
bufferKey: string | null;
|
|
5
|
+
pickupDate: string | null;
|
|
6
|
+
closed: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Haalt per ordernummer de buffer op waar de order in zit. De buffer wordt
|
|
10
|
+
* door de Logistics Engine bepaald; deze module groepeert er alleen op.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useOrderBuffers(orderRefs: string[]): {
|
|
13
|
+
byRef: Map<string, OrderBufferInfo>;
|
|
14
|
+
bufferFor: (ref: string | null | undefined) => OrderBufferInfo | null;
|
|
15
|
+
isLoading: boolean;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=useOrderBuffers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrderBuffers.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderBuffers.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE;;qBA2C9B,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,GAAG,IAAI;;EAItE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { supabase } from "../integrations/supabase/moduleClient";
|
|
4
|
+
function normalize(ref) {
|
|
5
|
+
return (ref ?? "").trim().toUpperCase();
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Haalt per ordernummer de buffer op waar de order in zit. De buffer wordt
|
|
9
|
+
* door de Logistics Engine bepaald; deze module groepeert er alleen op.
|
|
10
|
+
*/
|
|
11
|
+
export function useOrderBuffers(orderRefs) {
|
|
12
|
+
const refs = useMemo(() => {
|
|
13
|
+
const set = new Set();
|
|
14
|
+
for (const r of orderRefs) {
|
|
15
|
+
const v = (r ?? "").trim();
|
|
16
|
+
if (v)
|
|
17
|
+
set.add(v);
|
|
18
|
+
}
|
|
19
|
+
return [...set].sort();
|
|
20
|
+
}, [orderRefs]);
|
|
21
|
+
const query = useQuery({
|
|
22
|
+
queryKey: ["order-buffers", refs],
|
|
23
|
+
enabled: refs.length > 0,
|
|
24
|
+
staleTime: 60000,
|
|
25
|
+
retry: false,
|
|
26
|
+
queryFn: async () => {
|
|
27
|
+
const out = new Map();
|
|
28
|
+
const { data, error } = await supabase
|
|
29
|
+
.from("orders")
|
|
30
|
+
.select("order_number, buffer_id, order_buffers(buffer_key, pickup_date, closed_at)")
|
|
31
|
+
.in("order_number", refs);
|
|
32
|
+
if (error)
|
|
33
|
+
throw new Error(error.message);
|
|
34
|
+
for (const row of (data ?? [])) {
|
|
35
|
+
if (!row.order_number || !row.buffer_id)
|
|
36
|
+
continue;
|
|
37
|
+
out.set(normalize(row.order_number), {
|
|
38
|
+
bufferId: row.buffer_id,
|
|
39
|
+
bufferKey: row.order_buffers?.buffer_key ?? null,
|
|
40
|
+
pickupDate: row.order_buffers?.pickup_date ?? null,
|
|
41
|
+
closed: !!row.order_buffers?.closed_at,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const byRef = query.data ?? new Map();
|
|
48
|
+
return {
|
|
49
|
+
byRef,
|
|
50
|
+
bufferFor: (ref) => byRef.get(normalize(ref)) ?? null,
|
|
51
|
+
isLoading: query.isLoading,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Corrigeert de pickdag die de Engine teruggeeft. De Engine schuift oude taken
|
|
3
|
+
* door naar vandaag; een order hoort te staan op de dag waarop hij ooit
|
|
4
|
+
* gepland is en nooit ná zijn leverdatum.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useOrderPickDates(): {
|
|
7
|
+
correctPickDate: (orderRef: string, enginePickDate: string) => string;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=useOrderPickDates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrderPickDates.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPickDates.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,wBAAgB,iBAAiB;gCA+BlB,MAAM,kBAAkB,MAAM,KAAG,MAAM;;EAerD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { listOrderPickDates } from "../lib/pickDates.functions";
|
|
5
|
+
import { normalizeOrderNumber } from "../lib/orderRefs";
|
|
6
|
+
function keyOf(value) {
|
|
7
|
+
if (!value)
|
|
8
|
+
return null;
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
if (!trimmed || trimmed === "—" || trimmed === "-")
|
|
11
|
+
return null;
|
|
12
|
+
return normalizeOrderNumber(trimmed) ?? trimmed.toUpperCase();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Corrigeert de pickdag die de Engine teruggeeft. De Engine schuift oude taken
|
|
16
|
+
* door naar vandaag; een order hoort te staan op de dag waarop hij ooit
|
|
17
|
+
* gepland is en nooit ná zijn leverdatum.
|
|
18
|
+
*/
|
|
19
|
+
export function useOrderPickDates() {
|
|
20
|
+
const fetchDates = useServerFn(listOrderPickDates);
|
|
21
|
+
const query = useQuery({
|
|
22
|
+
queryKey: ["order-pick-dates"],
|
|
23
|
+
queryFn: () => fetchDates(),
|
|
24
|
+
staleTime: 60000,
|
|
25
|
+
retry: 1,
|
|
26
|
+
});
|
|
27
|
+
const map = useMemo(() => {
|
|
28
|
+
const m = new Map();
|
|
29
|
+
for (const it of query.data?.items ?? []) {
|
|
30
|
+
const existing = m.get(it.ref);
|
|
31
|
+
if (existing) {
|
|
32
|
+
// Zelfde order, meerdere refs: pak de vroegste bekende plandag.
|
|
33
|
+
if (it.firstPlannedDate &&
|
|
34
|
+
(!existing.firstPlannedDate || it.firstPlannedDate < existing.firstPlannedDate)) {
|
|
35
|
+
existing.firstPlannedDate = it.firstPlannedDate;
|
|
36
|
+
}
|
|
37
|
+
if (!existing.deliveryDate)
|
|
38
|
+
existing.deliveryDate = it.deliveryDate;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
m.set(it.ref, { deliveryDate: it.deliveryDate, firstPlannedDate: it.firstPlannedDate });
|
|
42
|
+
}
|
|
43
|
+
return m;
|
|
44
|
+
}, [query.data]);
|
|
45
|
+
/** Dag waarop deze order getoond moet worden, gegeven de dag van de Engine. */
|
|
46
|
+
const correctPickDate = useCallback((orderRef, enginePickDate) => {
|
|
47
|
+
const k = keyOf(orderRef);
|
|
48
|
+
if (!k)
|
|
49
|
+
return enginePickDate;
|
|
50
|
+
const info = map.get(k);
|
|
51
|
+
if (!info?.deliveryDate)
|
|
52
|
+
return enginePickDate;
|
|
53
|
+
// Pas ingrijpen als de Engine de order ná de leverdatum plant.
|
|
54
|
+
if (enginePickDate <= info.deliveryDate)
|
|
55
|
+
return enginePickDate;
|
|
56
|
+
const first = info.firstPlannedDate;
|
|
57
|
+
if (first && first <= info.deliveryDate)
|
|
58
|
+
return first;
|
|
59
|
+
return info.deliveryDate;
|
|
60
|
+
}, [map]);
|
|
61
|
+
return { correctPickDate, isLoading: query.isLoading };
|
|
62
|
+
}
|
|
@@ -7,7 +7,7 @@ import { type EngineTask } from "../lib/logisticsEngine/engine";
|
|
|
7
7
|
* "niet gemirrord" om actie in de Verkoop-module en "taak zonder starttijd" om
|
|
8
8
|
* onderzoek aan de Engine-kant.
|
|
9
9
|
*/
|
|
10
|
-
export type PlanningState = "planned" | "awaiting_engine" | "outside_window" | "
|
|
10
|
+
export type PlanningState = "planned" | "awaiting_engine" | "outside_window" | "task_without_start";
|
|
11
11
|
export interface PlannedOrder {
|
|
12
12
|
order: FloridayOrder;
|
|
13
13
|
/** Planning volgens de Logistics Engine. Null zolang er geen planning is. */
|
|
@@ -29,8 +29,6 @@ export interface UseOrderPlanningResult {
|
|
|
29
29
|
awaiting: PlannedOrder[];
|
|
30
30
|
/** Orders met een taak die buiten de zichtbare periode is ingedeeld. */
|
|
31
31
|
outsideWindow: PlannedOrder[];
|
|
32
|
-
/** Cache-orders die de Verkoop-module nog niet naar public.orders schreef. */
|
|
33
|
-
notMirrored: PlannedOrder[];
|
|
34
32
|
/** Orders met een taak zonder geldige planned_start. */
|
|
35
33
|
tasksWithoutStart: PlannedOrder[];
|
|
36
34
|
/** Foutmelding als het signaal naar de Engine niet verstuurd kon worden. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAYtG;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,iBAAiB,GACjB,gBAAgB,GAChB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,gFAAgF;IAChF,cAAc,EAAE,OAAO,CAAC;IACxB,iDAAiD;IACjD,KAAK,EAAE,aAAa,CAAC;IACrB,uEAAuE;IACvE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,wEAAwE;IACxE,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,wDAAwD;IACxD,iBAAiB,EAAE,YAAY,EAAE,CAAC;IAClC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAGD,MAAM,WAAW,uBAAuB;IACtC,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,uBAAuB,GAAG,sBAAsB,CAkTxF"}
|
|
@@ -10,6 +10,7 @@ import { useEngineTasks } from "./useEngineTasks";
|
|
|
10
10
|
import { useCartIndex } from "./useCartIndex";
|
|
11
11
|
import { emitEngineEvent, listComposeTaskRefs } from "../lib/logisticsEngine/engine";
|
|
12
12
|
import { orderRefsFor } from "../lib/orderRefs";
|
|
13
|
+
import { isOpenOrderStatus } from "../lib/orderStatus";
|
|
13
14
|
import { MAX_ATTEMPTS, emitQueueStatus, markInFlight, markResult, resetEmitQueue, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
|
|
14
15
|
export function useOrderPlanning(range) {
|
|
15
16
|
const from = range?.from ?? null;
|
|
@@ -152,11 +153,6 @@ export function useOrderPlanning(range) {
|
|
|
152
153
|
hasComposeTask,
|
|
153
154
|
});
|
|
154
155
|
if (!hasComposeTask) {
|
|
155
|
-
// Cache-order zonder rij in public.orders: de Engine kán er geen taak
|
|
156
|
-
// voor hebben. Dat is geen wachten op indeling.
|
|
157
|
-
if (order.source === "floriday" && order.mirrored === false) {
|
|
158
|
-
return fallback("not_mirrored");
|
|
159
|
-
}
|
|
160
156
|
return fallback("awaiting_engine");
|
|
161
157
|
}
|
|
162
158
|
if (!task) {
|
|
@@ -192,6 +188,9 @@ export function useOrderPlanning(range) {
|
|
|
192
188
|
return { order, planning, engineTask: task, awaitingEngine: false, state: "planned", hasComposeTask: true };
|
|
193
189
|
});
|
|
194
190
|
}, [orderList, carriersQuery.data, composeByRef, pickByRef, composeExists, cartsForOrder]);
|
|
191
|
+
// Alleen echt open orders mogen bij de Logistics Engine worden aangemeld;
|
|
192
|
+
// afgehandelde orders blijven wel zichtbaar maar krijgen geen melding meer.
|
|
193
|
+
const openPlanned = useMemo(() => planned.filter((p) => isOpenOrderStatus(p.order.status)), [planned]);
|
|
195
194
|
// Orders zonder taak melden we bij de Engine; zelf indelen mag niet.
|
|
196
195
|
// De meldlus is begrensd: max MAX_PER_ROUND orders per ronde, pogingenteller
|
|
197
196
|
// met backoff per order, en na MAX_ATTEMPTS definitief opgeven met melding.
|
|
@@ -200,7 +199,7 @@ export function useOrderPlanning(range) {
|
|
|
200
199
|
let cancelled = false;
|
|
201
200
|
const byRef = new Map();
|
|
202
201
|
const withTask = [];
|
|
203
|
-
for (const item of
|
|
202
|
+
for (const item of openPlanned) {
|
|
204
203
|
const ref = item.order.orderNumber || item.order.id;
|
|
205
204
|
if (!ref)
|
|
206
205
|
continue;
|
|
@@ -210,8 +209,6 @@ export function useOrderPlanning(range) {
|
|
|
210
209
|
withTask.push(ref);
|
|
211
210
|
continue;
|
|
212
211
|
}
|
|
213
|
-
// Cache-orders zonder rij in public.orders melden we niet: de Engine kan
|
|
214
|
-
// er niets mee, dat is een mirror-probleem in de Verkoop-module.
|
|
215
212
|
if (item.state !== "awaiting_engine")
|
|
216
213
|
continue;
|
|
217
214
|
if (!byRef.has(ref))
|
|
@@ -268,7 +265,7 @@ export function useOrderPlanning(range) {
|
|
|
268
265
|
return () => {
|
|
269
266
|
cancelled = true;
|
|
270
267
|
};
|
|
271
|
-
}, [
|
|
268
|
+
}, [openPlanned]);
|
|
272
269
|
// Tijdstip van de laatste Engine-ronde: nieuwste taak die we kennen.
|
|
273
270
|
const lastEngineRun = useMemo(() => {
|
|
274
271
|
let latest = null;
|
|
@@ -282,10 +279,9 @@ export function useOrderPlanning(range) {
|
|
|
282
279
|
return {
|
|
283
280
|
planned,
|
|
284
281
|
nearestDeliveryDate: ordersQuery.data?.nearestDeliveryDate ?? null,
|
|
285
|
-
awaiting:
|
|
286
|
-
outsideWindow:
|
|
287
|
-
|
|
288
|
-
tasksWithoutStart: planned.filter((p) => p.state === "task_without_start"),
|
|
282
|
+
awaiting: openPlanned.filter((p) => p.state === "awaiting_engine"),
|
|
283
|
+
outsideWindow: openPlanned.filter((p) => p.state === "outside_window"),
|
|
284
|
+
tasksWithoutStart: openPlanned.filter((p) => p.state === "task_without_start"),
|
|
289
285
|
signalError,
|
|
290
286
|
lastEngineRun,
|
|
291
287
|
isLoading: ordersQuery.isLoading || carriersQuery.isLoading || tasksLoading,
|
|
@@ -18,13 +18,8 @@ export interface PickAgendaDay {
|
|
|
18
18
|
shiftedCount: number;
|
|
19
19
|
orders: PickAgendaOrder[];
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Pickagenda van de Logistics Engine. De RPC is de enige bron: deze module
|
|
23
|
-
* berekent zelf geen pickdagen meer en doet geen datum- of tijdzonelogica.
|
|
24
|
-
* Dagen zonder rij tonen simpelweg nullen.
|
|
25
|
-
*/
|
|
26
21
|
export declare function usePickAgenda(from: string | null, to: string | null): {
|
|
27
|
-
days:
|
|
22
|
+
days: PickAgendaDay[];
|
|
28
23
|
byDate: Map<string, PickAgendaDay>;
|
|
29
24
|
isLoading: boolean;
|
|
30
25
|
error: string | null;
|