@flowselections/floriday-afhandeling-module 1.0.35 → 1.0.37

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 (32) hide show
  1. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  2. package/dist-lib/components/afhandeling/PickAgendaView.js +20 -9
  3. package/dist-lib/components/afhandeling/VervoerdersPage.js +1 -1
  4. package/dist-lib/hooks/useOrderPlanning.d.ts +20 -3
  5. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  6. package/dist-lib/hooks/useOrderPlanning.js +80 -15
  7. package/dist-lib/integrations/supabase/auth-middleware.d.ts +214 -1
  8. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  9. package/dist-lib/integrations/supabase/client.server.d.ts +214 -1
  10. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  11. package/dist-lib/integrations/supabase/moduleClient.d.ts +214 -1
  12. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/types.d.ts +218 -1
  14. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  15. package/dist-lib/lib/afhandelingSettings.functions.d.ts +428 -2
  16. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  17. package/dist-lib/lib/carriers.functions.d.ts +1070 -5
  18. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  19. package/dist-lib/lib/cartAssignments.functions.d.ts +1070 -5
  20. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  21. package/dist-lib/lib/cartControl.functions.d.ts +2140 -10
  22. package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
  23. package/dist-lib/lib/floriday.functions.d.ts +223 -2
  24. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/floriday.functions.js +41 -9
  26. package/dist-lib/lib/logisticsEngine/engine.d.ts +21 -1
  27. package/dist-lib/lib/logisticsEngine/engine.d.ts.map +1 -1
  28. package/dist-lib/lib/logisticsEngine/engine.js +81 -19
  29. package/dist-lib/lib/orderRefs.d.ts +18 -0
  30. package/dist-lib/lib/orderRefs.d.ts.map +1 -0
  31. package/dist-lib/lib/orderRefs.js +30 -0
  32. package/package.json +120 -120
@@ -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;AAED,wBAAgB,cAAc,CAAC,EAAE,YAAY,EAAE,EAAE,mBAAmB,+BAwEnE"}
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,+BA6FnE"}
@@ -5,15 +5,24 @@ import { WeekAgendaView } from "./WeekAgendaView";
5
5
  import { DagDetailDialog } from "./DagDetailDialog";
6
6
  import { useOrderPlanning } from "../../hooks/useOrderPlanning";
7
7
  import { agendaRange, currentWeekStart } from "../../lib/agendaRange";
8
+ /** Toon hooguit dit aantal ordernummers per categorie; de rest telt als "meer". */
9
+ const MAX_REFS = 8;
10
+ function refsOf(items) {
11
+ const all = items.map((p) => p.order.orderNumber || p.order.id).filter(Boolean);
12
+ return { shown: all.slice(0, MAX_REFS), total: all.length };
13
+ }
14
+ function CategoryLine({ items, label, }) {
15
+ const { shown, total } = refsOf(items);
16
+ if (!total)
17
+ return null;
18
+ return (_jsxs("div", { children: [label(total), ": ", shown.join(", "), total > shown.length ? ` en ${total - shown.length} meer` : "", "."] }));
19
+ }
8
20
  export function PickAgendaView({ onOrderClick }) {
9
21
  const [weekStart, setWeekStart] = useState(() => currentWeekStart());
10
22
  const range = useMemo(() => agendaRange(weekStart), [weekStart]);
11
- const { planned, awaiting, nearestDeliveryDate, signalError, lastEngineRun, isLoading, error, warning, refetch } = useOrderPlanning(range);
23
+ const { planned, awaiting, notMirrored, tasksWithoutStart, nearestDeliveryDate, signalError, lastEngineRun, isLoading, error, warning, refetch, } = useOrderPlanning(range);
12
24
  const awaitingCount = awaiting.length;
13
- const awaitingRefs = awaiting
14
- .map((p) => p.order.orderNumber || p.order.id)
15
- .filter(Boolean)
16
- .slice(0, 8);
25
+ const anyIssue = awaitingCount + notMirrored.length + tasksWithoutStart.length > 0;
17
26
  const lastRunLabel = lastEngineRun
18
27
  ? new Date(lastEngineRun).toLocaleString("nl-NL", {
19
28
  timeZone: "Europe/Amsterdam",
@@ -22,9 +31,11 @@ export function PickAgendaView({ onOrderClick }) {
22
31
  })
23
32
  : null;
24
33
  const [dayDetailDate, setDayDetailDate] = useState(null);
25
- return (_jsxs("div", { className: "space-y-6", children: [awaitingCount > 0 && (_jsxs("div", { className: "space-y-1 rounded-md border border-sky-300 bg-sky-50 p-4 text-sm text-sky-900", children: [_jsxs("div", { children: [awaitingCount, " order", awaitingCount === 1 ? "" : "s", " wacht", awaitingCount === 1 ? "" : "en", " op een indeling van de Logistics Engine en ", awaitingCount === 1 ? "wordt" : "worden", " daarom nog niet in de agenda gepland: ", awaitingRefs.join(", "), awaitingCount > awaitingRefs.length ? ` en ${awaitingCount - awaitingRefs.length} meer` : "", "."] }), _jsxs("div", { className: "text-xs", children: [lastRunLabel
34
+ return (_jsxs("div", { className: "space-y-6", children: [anyIssue && (_jsxs("div", { className: "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: notMirrored, label: (n) => `${n} Floriday-order${n === 1 ? "" : "s"} nog niet overgezet naar de orderadministratie door de Verkoop Module; de Engine kan ${n === 1 ? "die" : "die"} nog niet indelen` }), _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
26
35
  ? `Laatste indeling door de Engine: ${lastRunLabel}.`
27
- : "Er is nog geen indeling van de Engine bekend voor deze periode.", " ", signalError
28
- ? `Melding vanuit deze module is mislukt: ${signalError}`
29
- : "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) })] }));
36
+ : "Er is nog geen indeling van de Engine bekend voor deze periode.", " ", awaitingCount === 0
37
+ ? "Orders die buiten deze week zijn ingedeeld staan hier niet bij."
38
+ : signalError
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) })] }));
30
41
  }
@@ -67,5 +67,5 @@ function VervoerdersLijst() {
67
67
  [ids[index], ids[target]] = [ids[target], ids[index]];
68
68
  reorderMutation.mutate(ids);
69
69
  };
70
- return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Vervoerders" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer vervoerders en hun verzendtarieven per land." })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] }), _jsx("div", { className: "border rounded-md bg-card", children: isLoading ? (_jsx("div", { className: "p-12 text-center text-muted-foreground", children: "Laden..." })) : error ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsxs("div", { className: "text-destructive", children: ["Fout bij laden: ", error.message] }), _jsx(Button, { variant: "outline", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : carriers.length === 0 ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsx(Truck, { className: "w-10 h-10 mx-auto text-muted-foreground" }), _jsx("div", { className: "font-medium", children: "Nog geen vervoerders" }), _jsx("div", { className: "text-sm text-muted-foreground", children: "Voeg een vervoerder toe om verzendtarieven in te stellen." }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-32", children: "Prioriteit" }), _jsx(TableHead, { children: "Bedrijfsnaam" }), _jsx(TableHead, { children: "Contactpersoon" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Telefoon" }), _jsx(TableHead, { children: "Levertijd" }), _jsx(TableHead, { children: "Landen" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: carriers.map((c, index) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "w-6 text-sm font-medium tabular-nums text-muted-foreground", children: index + 1 }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(index, -1), disabled: index === 0 || reorderMutation.isPending, "aria-label": "Hoger", children: _jsx(ArrowUp, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(index, 1), disabled: index === carriers.length - 1 || reorderMutation.isPending, "aria-label": "Lager", children: _jsx(ArrowDown, { className: "w-4 h-4" }) })] }) }), _jsx(TableCell, { className: "font-medium", children: c.name }), _jsx(TableCell, { children: c.contact_person ?? "—" }), _jsx(TableCell, { children: c.email ?? "—" }), _jsx(TableCell, { children: c.phone ?? "—" }), _jsxs(TableCell, { children: [carrierLeadDaysFromMoments(c.pickup_moments), " werkdagen"] }), _jsx(TableCell, { children: c.rate_count }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { to: "/afhandeling/vervoerders", search: { id: c.id }, children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setDeletingId(c.id), className: "text-destructive hover:text-destructive", children: _jsx(Trash2, { className: "w-4 h-4" }) })] }) })] }, c.id))) })] })) }), _jsx(AlertDialog, { open: !!deletingId, onOpenChange: (o) => !o && setDeletingId(null), children: _jsxs(ResponsiveAlertDialogContent, { size: "sm", children: [_jsx(ResponsiveAlertDialogHeader, { children: _jsx(AlertDialogTitle, { children: "Vervoerder verwijderen?" }) }), _jsx(ResponsiveAlertDialogBody, { children: _jsx(AlertDialogDescription, { children: "Deze actie kan niet ongedaan gemaakt worden. Alle verzendtarieven worden ook verwijderd." }) }), _jsxs(ResponsiveAlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: () => deletingId && deleteMutation.mutate(deletingId), className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Verwijderen" })] })] }) })] }));
70
+ return (_jsxs("div", { className: "p-6 space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-2xl font-semibold", children: "Vervoerders" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Beheer vervoerders en hun verzendtarieven per land." })] }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] }), _jsx("div", { className: "border rounded-md bg-card", children: isLoading ? (_jsx("div", { className: "p-12 text-center text-muted-foreground", children: "Laden..." })) : error ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsxs("div", { className: "text-destructive", children: ["Fout bij laden: ", error.message] }), _jsx(Button, { variant: "outline", onClick: () => refetch(), children: "Opnieuw proberen" })] })) : carriers.length === 0 ? (_jsxs("div", { className: "p-12 text-center space-y-3", children: [_jsx(Truck, { className: "w-10 h-10 mx-auto text-muted-foreground" }), _jsx("div", { className: "font-medium", children: "Nog geen vervoerders" }), _jsx("div", { className: "text-sm text-muted-foreground", children: "Voeg een vervoerder toe om verzendtarieven in te stellen." }), _jsx(Button, { asChild: true, children: _jsxs(Link, { to: "/afhandeling/vervoerders/nieuw", children: [_jsx(Plus, { className: "w-4 h-4 mr-1" }), "Vervoerder toevoegen"] }) })] })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-32", children: "Prioriteit" }), _jsx(TableHead, { children: "Bedrijfsnaam" }), _jsx(TableHead, { children: "Contactpersoon" }), _jsx(TableHead, { children: "E-mail" }), _jsx(TableHead, { children: "Telefoon" }), _jsx(TableHead, { children: "Levertijd" }), _jsx(TableHead, { children: "Landen" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: carriers.map((c, index) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "w-6 text-sm font-medium tabular-nums text-muted-foreground", children: index + 1 }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(index, -1), disabled: index === 0 || reorderMutation.isPending, "aria-label": "Hoger", children: _jsx(ArrowUp, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => move(index, 1), disabled: index === carriers.length - 1 || reorderMutation.isPending, "aria-label": "Lager", children: _jsx(ArrowDown, { className: "w-4 h-4" }) })] }) }), _jsx(TableCell, { className: "font-medium", children: c.name }), _jsx(TableCell, { children: c.contact_person ?? "—" }), _jsx(TableCell, { children: c.email ?? "—" }), _jsx(TableCell, { children: c.phone ?? "—" }), _jsxs(TableCell, { children: [carrierLeadDaysFromMoments(c.pickup_moments), " werkdagen"] }), _jsx(TableCell, { children: c.rate_count }), _jsx(TableCell, { className: "text-right", children: _jsxs("div", { className: "flex justify-end gap-1", children: [_jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { to: "/afhandeling/vervoerders/$carrierId", params: { carrierId: c.id }, children: _jsx(Pencil, { className: "w-4 h-4" }) }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setDeletingId(c.id), className: "text-destructive hover:text-destructive", children: _jsx(Trash2, { className: "w-4 h-4" }) })] }) })] }, c.id))) })] })) }), _jsx(AlertDialog, { open: !!deletingId, onOpenChange: (o) => !o && setDeletingId(null), children: _jsxs(ResponsiveAlertDialogContent, { size: "sm", children: [_jsx(ResponsiveAlertDialogHeader, { children: _jsx(AlertDialogTitle, { children: "Vervoerder verwijderen?" }) }), _jsx(ResponsiveAlertDialogBody, { children: _jsx(AlertDialogDescription, { children: "Deze actie kan niet ongedaan gemaakt worden. Alle verzendtarieven worden ook verwijderd." }) }), _jsxs(ResponsiveAlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Annuleren" }), _jsx(AlertDialogAction, { onClick: () => deletingId && deleteMutation.mutate(deletingId), className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: "Verwijderen" })] })] }) })] }));
71
71
  }
@@ -1,21 +1,38 @@
1
1
  import type { FloridayOrder } from "../lib/floriday.functions";
2
2
  import { type OrderPlanning } from "../lib/planning";
3
3
  import { type EngineTask } from "../lib/logisticsEngine/engine";
4
+ /**
5
+ * Waarom een order (nog) geen planning in deze week heeft. Bewust gesplitst:
6
+ * "geen indeling" vraagt om melden bij de Engine, "buiten deze week" om niets,
7
+ * "niet gemirrord" om actie in de Verkoop-module en "taak zonder starttijd" om
8
+ * onderzoek aan de Engine-kant.
9
+ */
10
+ export type PlanningState = "planned" | "awaiting_engine" | "outside_window" | "not_mirrored" | "task_without_start";
4
11
  export interface PlannedOrder {
5
12
  order: FloridayOrder;
6
- /** Planning volgens de Logistics Engine. Null zolang er geen taak is. */
13
+ /** Planning volgens de Logistics Engine. Null zolang er geen planning is. */
7
14
  planning: OrderPlanning | null;
8
15
  /** De leidende load.compose-taak uit de Engine (indien aanwezig). */
9
16
  engineTask: EngineTask | null;
10
- /** True zolang de Engine nog geen load.compose-taak heeft geleverd. */
17
+ /** True alléén als er nergens een load.compose-taak voor deze order bestaat. */
11
18
  awaitingEngine: boolean;
19
+ /** Reden waarom deze order (niet) gepland is. */
20
+ state: PlanningState;
21
+ /** Bestaat er ergens een load.compose-taak, ongeacht planned_start? */
22
+ hasComposeTask: boolean;
12
23
  }
13
24
  export interface UseOrderPlanningResult {
14
25
  planned: PlannedOrder[];
15
26
  /** Dichtstbijzijnde leverdatum buiten het venster (leeg venster → auto-jump). */
16
27
  nearestDeliveryDate?: string | null;
17
- /** Orders die op een Engine-taak wachten en dus niet zijn ingedeeld. */
28
+ /** Orders die echt geen load.compose-taak hebben (nergens, geen venster). */
18
29
  awaiting: PlannedOrder[];
30
+ /** Orders met een taak die buiten de zichtbare periode is ingedeeld. */
31
+ outsideWindow: PlannedOrder[];
32
+ /** Cache-orders die de Verkoop-module nog niet naar public.orders schreef. */
33
+ notMirrored: PlannedOrder[];
34
+ /** Orders met een taak zonder geldige planned_start. */
35
+ tasksWithoutStart: PlannedOrder[];
19
36
  /** Foutmelding als het signaal naar de Engine niet verstuurd kon worden. */
20
37
  signalError?: string | null;
21
38
  /** Tijdstip van de laatst bekende Engine-ronde (nieuwste taak). */
@@ -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;AAM/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AASjF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,yEAAyE;IACzE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,qEAAqE;IACrE,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,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,wEAAwE;IACxE,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,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;AAMD,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,CA6PxF"}
1
+ {"version":3,"file":"useOrderPlanning.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderPlanning.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAWtG;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,iBAAiB,GACjB,gBAAgB,GAChB,cAAc,GACd,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,8EAA8E;IAC9E,WAAW,EAAE,YAAY,EAAE,CAAC;IAC5B,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,CAkUxF"}
@@ -8,11 +8,9 @@ import { safeServerCall } from "../lib/serverFnError";
8
8
  import { normalizePickupMoments, sanitizeWeekdays, selectCarrierForTarget } from "../lib/carrierPlanning";
9
9
  import { calculateOrderPlanning, DEFAULT_PICKUP_MOMENT, } from "../lib/planning";
10
10
  import { useEngineTasks } from "./useEngineTasks";
11
- import { emitEngineEvent } from "../lib/logisticsEngine/engine";
12
- import { MAX_ATTEMPTS, emitQueueStatus, markInFlight, markResult, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
13
- function orderRefsFor(order) {
14
- return [order.orderNumber, order.id].filter((v) => Boolean(v));
15
- }
11
+ import { emitEngineEvent, listComposeTaskRefs } from "../lib/logisticsEngine/engine";
12
+ import { orderRefsFor } from "../lib/orderRefs";
13
+ import { MAX_ATTEMPTS, emitQueueStatus, markInFlight, markResult, resetEmitQueue, selectEmitBatch, } from "../lib/logisticsEngine/emitQueue";
16
14
  export function useOrderPlanning(range) {
17
15
  const from = range?.from ?? null;
18
16
  const to = range?.to ?? null;
@@ -96,8 +94,32 @@ export function useOrderPlanning(range) {
96
94
  }
97
95
  return { composeByRef: compose, pickByRef: pick };
98
96
  }, [engineTasks]);
97
+ // Venstervrije bestaanscheck: heeft deze order ergens een load.compose-taak?
98
+ // De takenquery hierboven is gefilterd op planned_start binnen de zichtbare
99
+ // periode, terwijl orders op leverdatum gefilterd zijn. Een order die net
100
+ // buiten dat venster is ingedeeld leek daardoor "wachtend op de Engine".
101
+ const orderList = useMemo(() => ordersQuery.data?.orders ?? [], [ordersQuery.data]);
102
+ const refsForCheck = useMemo(() => {
103
+ const set = new Set();
104
+ for (const o of orderList)
105
+ for (const ref of orderRefsFor(o))
106
+ set.add(ref);
107
+ return [...set].sort();
108
+ }, [orderList]);
109
+ const composeExistsQuery = useQuery({
110
+ queryKey: ["engine", "compose-exists", refsForCheck.length, refsForCheck[0] ?? null, refsForCheck[refsForCheck.length - 1] ?? null],
111
+ queryFn: () => listComposeTaskRefs(refsForCheck),
112
+ enabled: refsForCheck.length > 0,
113
+ retry: false,
114
+ staleTime: 30000,
115
+ placeholderData: (prev) => prev,
116
+ refetchInterval: 60000,
117
+ refetchIntervalInBackground: false,
118
+ refetchOnWindowFocus: true,
119
+ });
120
+ const composeExists = composeExistsQuery.data;
99
121
  const planned = useMemo(() => {
100
- const orders = ordersQuery.data?.orders ?? [];
122
+ const orders = orderList;
101
123
  const settings = settingsQuery.data ?? {
102
124
  pickDaysBeforeTransport: 1,
103
125
  transportRegisterDaysBefore: 1,
@@ -134,13 +156,38 @@ export function useOrderPlanning(range) {
134
156
  // De pickdag komt uit pick.execute (zelfde bron als de capaciteitscontrole
135
157
  // van de Engine); load.compose blijft de bron voor de karindeling.
136
158
  const pickTask = refs.map((ref) => pickByRef.get(ref)).find(Boolean) ?? null;
137
- if (!task || !base) {
138
- return { order, planning: null, engineTask: task, awaitingEngine: true };
159
+ const existing = composeExists
160
+ ? refs.map((ref) => composeExists.get(ref)).find(Boolean) ?? null
161
+ : null;
162
+ const hasComposeTask = Boolean(task || existing);
163
+ const fallback = (state) => ({
164
+ order,
165
+ planning: null,
166
+ engineTask: task,
167
+ awaitingEngine: state === "awaiting_engine",
168
+ state,
169
+ hasComposeTask,
170
+ });
171
+ if (!hasComposeTask) {
172
+ // Cache-order zonder rij in public.orders: de Engine kán er geen taak
173
+ // voor hebben. Dat is geen wachten op indeling.
174
+ if (order.source === "floriday" && order.mirrored === false) {
175
+ return fallback("not_mirrored");
176
+ }
177
+ return fallback("awaiting_engine");
178
+ }
179
+ if (!task) {
180
+ // Taak bestaat wél, maar valt buiten de zichtbare periode.
181
+ return fallback(existing?.planned_start ? "outside_window" : "task_without_start");
139
182
  }
140
183
  const startSource = pickTask?.planned_start ?? task.planned_start;
141
184
  const start = startSource ? new Date(startSource) : null;
142
185
  if (!start || Number.isNaN(start.getTime())) {
143
- return { order, planning: null, engineTask: task, awaitingEngine: true };
186
+ return fallback("task_without_start");
187
+ }
188
+ if (!base) {
189
+ // Order zonder bruikbare leverdatum: taak bestaat, planning niet.
190
+ return fallback("task_without_start");
144
191
  }
145
192
  const carrierCount = order.cartCount ?? task.payload?.loadCarriers?.length ?? 1;
146
193
  const planning = {
@@ -158,9 +205,9 @@ export function useOrderPlanning(range) {
158
205
  reason: `Ingepland door de Logistics Engine (taak ${(pickTask ?? task).task_type}, regelversie ${(pickTask ?? task).rule_version ?? "-"}).`,
159
206
  carrierName: carrier?.name ?? base.carrierName,
160
207
  };
161
- return { order, planning, engineTask: task, awaitingEngine: false };
208
+ return { order, planning, engineTask: task, awaitingEngine: false, state: "planned", hasComposeTask: true };
162
209
  });
163
- }, [ordersQuery.data, settingsQuery.data, carriersQuery.data, composeByRef, pickByRef]);
210
+ }, [orderList, settingsQuery.data, carriersQuery.data, composeByRef, pickByRef, composeExists]);
164
211
  // Orders zonder taak melden we bij de Engine; zelf indelen mag niet.
165
212
  // De meldlus is begrensd: max MAX_PER_ROUND orders per ronde, pogingenteller
166
213
  // met backoff per order, en na MAX_ATTEMPTS definitief opgeven met melding.
@@ -168,13 +215,28 @@ export function useOrderPlanning(range) {
168
215
  useEffect(() => {
169
216
  let cancelled = false;
170
217
  const byRef = new Map();
218
+ const withTask = [];
171
219
  for (const item of planned) {
172
- if (!item.awaitingEngine || item.engineTask)
173
- continue;
174
220
  const ref = item.order.orderNumber || item.order.id;
175
- if (ref && !byRef.has(ref))
221
+ if (!ref)
222
+ continue;
223
+ // Heeft de order ergens een load.compose-taak? Dan nooit melden — ook niet
224
+ // als de taak buiten de zichtbare periode gepland staat.
225
+ if (item.hasComposeTask) {
226
+ withTask.push(ref);
227
+ continue;
228
+ }
229
+ // Cache-orders zonder rij in public.orders melden we niet: de Engine kan
230
+ // er niets mee, dat is een mirror-probleem in de Verkoop-module.
231
+ if (item.state !== "awaiting_engine")
232
+ continue;
233
+ if (!byRef.has(ref))
176
234
  byRef.set(ref, item);
177
235
  }
236
+ // Refs die inmiddels een taak hebben uit de registry halen, zodat ze niet in
237
+ // de foutbanner blijven hangen als ze eerder waren opgegeven.
238
+ if (withTask.length)
239
+ resetEmitQueue(withTask);
178
240
  const refs = [...byRef.keys()];
179
241
  const batch = selectEmitBatch(refs);
180
242
  if (!batch.length) {
@@ -236,7 +298,10 @@ export function useOrderPlanning(range) {
236
298
  return {
237
299
  planned,
238
300
  nearestDeliveryDate: ordersQuery.data?.nearestDeliveryDate ?? null,
239
- awaiting: planned.filter((p) => p.awaitingEngine),
301
+ awaiting: planned.filter((p) => p.state === "awaiting_engine"),
302
+ outsideWindow: planned.filter((p) => p.state === "outside_window"),
303
+ notMirrored: planned.filter((p) => p.state === "not_mirrored"),
304
+ tasksWithoutStart: planned.filter((p) => p.state === "task_without_start"),
240
305
  signalError,
241
306
  lastEngineRun,
242
307
  isLoading: ordersQuery.isLoading || settingsQuery.isLoading || carriersQuery.isLoading || tasksLoading,