@flowselections/logistiek-engine 1.0.1

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 (175) hide show
  1. package/README.md +24 -0
  2. package/dist-lib/_core-safelist.d.ts +2 -0
  3. package/dist-lib/_core-safelist.d.ts.map +1 -0
  4. package/dist-lib/_core-safelist.js +15 -0
  5. package/dist-lib/components/BeladingIndelingCard.d.ts +2 -0
  6. package/dist-lib/components/BeladingIndelingCard.d.ts.map +1 -0
  7. package/dist-lib/components/BeladingIndelingCard.js +77 -0
  8. package/dist-lib/components/BufferDebugCard.d.ts +19 -0
  9. package/dist-lib/components/BufferDebugCard.d.ts.map +1 -0
  10. package/dist-lib/components/BufferDebugCard.js +69 -0
  11. package/dist-lib/components/CapaciteitMeldingenCard.d.ts +2 -0
  12. package/dist-lib/components/CapaciteitMeldingenCard.d.ts.map +1 -0
  13. package/dist-lib/components/CapaciteitMeldingenCard.js +40 -0
  14. package/dist-lib/components/LadingdragersPage.d.ts +2 -0
  15. package/dist-lib/components/LadingdragersPage.d.ts.map +1 -0
  16. package/dist-lib/components/LadingdragersPage.js +102 -0
  17. package/dist-lib/components/LogistiekEngineLogo.d.ts +6 -0
  18. package/dist-lib/components/LogistiekEngineLogo.d.ts.map +1 -0
  19. package/dist-lib/components/LogistiekEngineLogo.js +4 -0
  20. package/dist-lib/components/LogistiekEnginePage.d.ts +2 -0
  21. package/dist-lib/components/LogistiekEnginePage.d.ts.map +1 -0
  22. package/dist-lib/components/LogistiekEnginePage.js +53 -0
  23. package/dist-lib/components/ModulePromptsPage.d.ts +2 -0
  24. package/dist-lib/components/ModulePromptsPage.d.ts.map +1 -0
  25. package/dist-lib/components/ModulePromptsPage.js +58 -0
  26. package/dist-lib/components/RegiePage.d.ts +2 -0
  27. package/dist-lib/components/RegiePage.d.ts.map +1 -0
  28. package/dist-lib/components/RegiePage.js +82 -0
  29. package/dist-lib/components/RuleEnginePage.d.ts +7 -0
  30. package/dist-lib/components/RuleEnginePage.d.ts.map +1 -0
  31. package/dist-lib/components/RuleEnginePage.js +114 -0
  32. package/dist-lib/components/StrategieDialog.d.ts +11 -0
  33. package/dist-lib/components/StrategieDialog.d.ts.map +1 -0
  34. package/dist-lib/components/StrategieDialog.js +38 -0
  35. package/dist-lib/components/VertragingMeldingenCard.d.ts +2 -0
  36. package/dist-lib/components/VertragingMeldingenCard.d.ts.map +1 -0
  37. package/dist-lib/components/VertragingMeldingenCard.js +32 -0
  38. package/dist-lib/components/VoorraadMeldingenCard.d.ts +2 -0
  39. package/dist-lib/components/VoorraadMeldingenCard.d.ts.map +1 -0
  40. package/dist-lib/components/VoorraadMeldingenCard.js +26 -0
  41. package/dist-lib/components/rule-engine/CollapsibleGroup.d.ts +12 -0
  42. package/dist-lib/components/rule-engine/CollapsibleGroup.d.ts.map +1 -0
  43. package/dist-lib/components/rule-engine/CollapsibleGroup.js +11 -0
  44. package/dist-lib/components/rule-engine/ConditionsEditor.d.ts +8 -0
  45. package/dist-lib/components/rule-engine/ConditionsEditor.d.ts.map +1 -0
  46. package/dist-lib/components/rule-engine/ConditionsEditor.js +31 -0
  47. package/dist-lib/components/rule-engine/FieldHelp.d.ts +14 -0
  48. package/dist-lib/components/rule-engine/FieldHelp.d.ts.map +1 -0
  49. package/dist-lib/components/rule-engine/FieldHelp.js +18 -0
  50. package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts +2 -0
  51. package/dist-lib/components/rule-engine/FloridayConnectionBanner.d.ts.map +1 -0
  52. package/dist-lib/components/rule-engine/FloridayConnectionBanner.js +41 -0
  53. package/dist-lib/components/rule-engine/ImpactPanel.d.ts +5 -0
  54. package/dist-lib/components/rule-engine/ImpactPanel.d.ts.map +1 -0
  55. package/dist-lib/components/rule-engine/ImpactPanel.js +25 -0
  56. package/dist-lib/components/rule-engine/RuleFieldInput.d.ts +9 -0
  57. package/dist-lib/components/rule-engine/RuleFieldInput.d.ts.map +1 -0
  58. package/dist-lib/components/rule-engine/RuleFieldInput.js +80 -0
  59. package/dist-lib/components/rule-engine/VersionsPanel.d.ts +10 -0
  60. package/dist-lib/components/rule-engine/VersionsPanel.d.ts.map +1 -0
  61. package/dist-lib/components/rule-engine/VersionsPanel.js +16 -0
  62. package/dist-lib/components/settings/LogistiekEngineSettingsCard.d.ts +2 -0
  63. package/dist-lib/components/settings/LogistiekEngineSettingsCard.d.ts.map +1 -0
  64. package/dist-lib/components/settings/LogistiekEngineSettingsCard.js +22 -0
  65. package/dist-lib/hooks/useCapaciteitMeldingen.d.ts +20 -0
  66. package/dist-lib/hooks/useCapaciteitMeldingen.d.ts.map +1 -0
  67. package/dist-lib/hooks/useCapaciteitMeldingen.js +46 -0
  68. package/dist-lib/hooks/useDocumentLayouts.d.ts +13 -0
  69. package/dist-lib/hooks/useDocumentLayouts.d.ts.map +1 -0
  70. package/dist-lib/hooks/useDocumentLayouts.js +54 -0
  71. package/dist-lib/hooks/useLadingdragers.d.ts +16 -0
  72. package/dist-lib/hooks/useLadingdragers.d.ts.map +1 -0
  73. package/dist-lib/hooks/useLadingdragers.js +86 -0
  74. package/dist-lib/hooks/useLogistiekStrategieen.d.ts +29 -0
  75. package/dist-lib/hooks/useLogistiekStrategieen.d.ts.map +1 -0
  76. package/dist-lib/hooks/useLogistiekStrategieen.js +111 -0
  77. package/dist-lib/hooks/useLogistiekTaken.d.ts +89 -0
  78. package/dist-lib/hooks/useLogistiekTaken.d.ts.map +1 -0
  79. package/dist-lib/hooks/useLogistiekTaken.js +314 -0
  80. package/dist-lib/hooks/usePickstations.d.ts +10 -0
  81. package/dist-lib/hooks/usePickstations.d.ts.map +1 -0
  82. package/dist-lib/hooks/usePickstations.js +51 -0
  83. package/dist-lib/hooks/useRecalcJob.d.ts +24 -0
  84. package/dist-lib/hooks/useRecalcJob.d.ts.map +1 -0
  85. package/dist-lib/hooks/useRecalcJob.js +83 -0
  86. package/dist-lib/hooks/useRuleEngine.d.ts +48 -0
  87. package/dist-lib/hooks/useRuleEngine.d.ts.map +1 -0
  88. package/dist-lib/hooks/useRuleEngine.js +222 -0
  89. package/dist-lib/hooks/useVertragingMeldingen.d.ts +29 -0
  90. package/dist-lib/hooks/useVertragingMeldingen.d.ts.map +1 -0
  91. package/dist-lib/hooks/useVertragingMeldingen.js +68 -0
  92. package/dist-lib/hooks/useVoorraadMeldingen.d.ts +30 -0
  93. package/dist-lib/hooks/useVoorraadMeldingen.d.ts.map +1 -0
  94. package/dist-lib/hooks/useVoorraadMeldingen.js +70 -0
  95. package/dist-lib/index.d.ts +36 -0
  96. package/dist-lib/index.d.ts.map +1 -0
  97. package/dist-lib/index.js +52 -0
  98. package/dist-lib/integrations/supabase/auth-attacher.d.ts +2 -0
  99. package/dist-lib/integrations/supabase/auth-attacher.d.ts.map +1 -0
  100. package/dist-lib/integrations/supabase/auth-attacher.js +12 -0
  101. package/dist-lib/integrations/supabase/auth-middleware.d.ts +19781 -0
  102. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -0
  103. package/dist-lib/integrations/supabase/auth-middleware.js +52 -0
  104. package/dist-lib/integrations/supabase/client.d.ts +19777 -0
  105. package/dist-lib/integrations/supabase/client.d.ts.map +1 -0
  106. package/dist-lib/integrations/supabase/client.js +13 -0
  107. package/dist-lib/integrations/supabase/client.server.d.ts +19777 -0
  108. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -0
  109. package/dist-lib/integrations/supabase/client.server.js +30 -0
  110. package/dist-lib/integrations/supabase/types.d.ts +20355 -0
  111. package/dist-lib/integrations/supabase/types.d.ts.map +1 -0
  112. package/dist-lib/integrations/supabase/types.js +78 -0
  113. package/dist-lib/lib/bufferMatching.d.ts +26 -0
  114. package/dist-lib/lib/bufferMatching.d.ts.map +1 -0
  115. package/dist-lib/lib/bufferMatching.js +73 -0
  116. package/dist-lib/lib/bulkLoadCarrierPlanning.server.d.ts +23 -0
  117. package/dist-lib/lib/bulkLoadCarrierPlanning.server.d.ts.map +1 -0
  118. package/dist-lib/lib/bulkLoadCarrierPlanning.server.js +126 -0
  119. package/dist-lib/lib/floridayDocuments.functions.d.ts +59415 -0
  120. package/dist-lib/lib/floridayDocuments.functions.d.ts.map +1 -0
  121. package/dist-lib/lib/floridayDocuments.functions.js +68 -0
  122. package/dist-lib/lib/floridayDocuments.server.d.ts +51 -0
  123. package/dist-lib/lib/floridayDocuments.server.d.ts.map +1 -0
  124. package/dist-lib/lib/floridayDocuments.server.js +109 -0
  125. package/dist-lib/lib/loadCarrierPlanner.d.ts +71 -0
  126. package/dist-lib/lib/loadCarrierPlanner.d.ts.map +1 -0
  127. package/dist-lib/lib/loadCarrierPlanner.js +374 -0
  128. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts +98984 -0
  129. package/dist-lib/lib/loadCarrierPlanning.functions.d.ts.map +1 -0
  130. package/dist-lib/lib/loadCarrierPlanning.functions.js +106 -0
  131. package/dist-lib/lib/loadCarrierPlanning.server.d.ts +46 -0
  132. package/dist-lib/lib/loadCarrierPlanning.server.d.ts.map +1 -0
  133. package/dist-lib/lib/loadCarrierPlanning.server.js +515 -0
  134. package/dist-lib/lib/loadCarrierTaskSync.server.d.ts +3 -0
  135. package/dist-lib/lib/loadCarrierTaskSync.server.d.ts.map +1 -0
  136. package/dist-lib/lib/loadCarrierTaskSync.server.js +56 -0
  137. package/dist-lib/lib/loadCarrierTypes.d.ts +19 -0
  138. package/dist-lib/lib/loadCarrierTypes.d.ts.map +1 -0
  139. package/dist-lib/lib/loadCarrierTypes.js +41 -0
  140. package/dist-lib/lib/logisticsPlanner.d.ts +56 -0
  141. package/dist-lib/lib/logisticsPlanner.d.ts.map +1 -0
  142. package/dist-lib/lib/logisticsPlanner.js +273 -0
  143. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts +39575 -0
  144. package/dist-lib/lib/logisticsRecalcJob.functions.d.ts.map +1 -0
  145. package/dist-lib/lib/logisticsRecalcJob.functions.js +30 -0
  146. package/dist-lib/lib/logisticsRecalcJob.server.d.ts +32 -0
  147. package/dist-lib/lib/logisticsRecalcJob.server.d.ts.map +1 -0
  148. package/dist-lib/lib/logisticsRecalcJob.server.js +116 -0
  149. package/dist-lib/lib/logisticsRecalculate.functions.d.ts +19792 -0
  150. package/dist-lib/lib/logisticsRecalculate.functions.d.ts.map +1 -0
  151. package/dist-lib/lib/logisticsRecalculate.functions.js +35 -0
  152. package/dist-lib/lib/logisticsTasks.d.ts +23 -0
  153. package/dist-lib/lib/logisticsTasks.d.ts.map +1 -0
  154. package/dist-lib/lib/logisticsTasks.js +92 -0
  155. package/dist-lib/lib/modulePrompts.d.ts +31 -0
  156. package/dist-lib/lib/modulePrompts.d.ts.map +1 -0
  157. package/dist-lib/lib/modulePrompts.js +1195 -0
  158. package/dist-lib/lib/ruleEngineImpact.d.ts +27 -0
  159. package/dist-lib/lib/ruleEngineImpact.d.ts.map +1 -0
  160. package/dist-lib/lib/ruleEngineImpact.js +257 -0
  161. package/dist-lib/lib/ruleEngineSchema.d.ts +69 -0
  162. package/dist-lib/lib/ruleEngineSchema.d.ts.map +1 -0
  163. package/dist-lib/lib/ruleEngineSchema.js +899 -0
  164. package/dist-lib/lib/ruleEngineWizard.d.ts +17 -0
  165. package/dist-lib/lib/ruleEngineWizard.d.ts.map +1 -0
  166. package/dist-lib/lib/ruleEngineWizard.js +204 -0
  167. package/dist-lib/lib/utils.d.ts +3 -0
  168. package/dist-lib/lib/utils.d.ts.map +1 -0
  169. package/dist-lib/lib/utils.js +5 -0
  170. package/dist-lib/lib/validationSchemas.d.ts +15 -0
  171. package/dist-lib/lib/validationSchemas.d.ts.map +1 -0
  172. package/dist-lib/lib/validationSchemas.js +25 -0
  173. package/dist-lib/styles.css +1 -0
  174. package/package.json +113 -0
  175. package/public/logo.png +0 -0
@@ -0,0 +1,56 @@
1
+ // ============================================================================
2
+ // src/lib/loadCarrierTaskSync.server.ts
3
+ // ============================================================================
4
+ // Zet het indelingsresultaat in de payload van de openstaande load.compose
5
+ // taken, zodat de Afhandeling-module niets meer zelf hoeft te berekenen.
6
+ // ============================================================================
7
+ import { supabaseAdmin } from "../integrations/supabase/client.server";
8
+ const db = () => supabaseAdmin;
9
+ const OPEN_STATUSES = ["planned", "ready", "in_progress"];
10
+ export async function attachIndelingToTasks(result) {
11
+ if (!result || result.skipped === "locked")
12
+ return 0;
13
+ const carts = Array.isArray(result.carts) ? result.carts : [];
14
+ if (result.orderIds.length === 0)
15
+ return 0;
16
+ const { data: orders } = await db()
17
+ .from("orders")
18
+ .select("id, order_number")
19
+ .in("id", result.orderIds);
20
+ const refs = (orders ?? []).map((o) => o.order_number).filter(Boolean);
21
+ if (refs.length === 0)
22
+ return 0;
23
+ const { data: tasks } = await db()
24
+ .from("logistics_tasks")
25
+ .select("id, payload, order_ref")
26
+ .eq("task_type", "load.compose")
27
+ .in("order_ref", refs)
28
+ .in("status", OPEN_STATUSES);
29
+ const list = Array.isArray(tasks) ? tasks : [];
30
+ let updated = 0;
31
+ for (const task of list) {
32
+ const payload = { ...(task.payload ?? {}) };
33
+ payload.indeling = carts.map((c) => ({
34
+ cart_number: c.cart_number,
35
+ carrier_type: c.carrier_type,
36
+ occupancy_pct: c.occupancy_pct,
37
+ total_layers_used: c.total_layers_used,
38
+ max_layers: c.max_layers,
39
+ is_complete: c.is_complete,
40
+ weight_kg: c.weight_kg,
41
+ items: c.items.map((i) => ({
42
+ product_name: i.product_name,
43
+ product_id: i.product_id,
44
+ crates: i.crates,
45
+ plants: i.plants,
46
+ layer_share_pct: i.layer_share_pct,
47
+ })),
48
+ }));
49
+ payload.indelingWarnings = result.warnings;
50
+ payload.indelingSource = "logistics-engine";
51
+ const { error } = await db().from("logistics_tasks").update({ payload }).eq("id", task.id);
52
+ if (!error)
53
+ updated++;
54
+ }
55
+ return updated;
56
+ }
@@ -0,0 +1,19 @@
1
+ import type { CapacityModel, CarrierProfile } from "./loadCarrierPlanner";
2
+ export type LoadCarrierCode = "trolley" | "danish_container" | "euro_rolcontainer" | "pallet" | "euro_pallet" | "loose";
3
+ export interface LoadCarrierTypeRow extends CarrierProfile {
4
+ id: string;
5
+ layer_length_cm: number;
6
+ layer_width_cm: number;
7
+ max_height_cm: number | null;
8
+ max_orders: number | null;
9
+ max_customers: number | null;
10
+ is_active: boolean;
11
+ is_default: boolean;
12
+ sort_order: number;
13
+ }
14
+ export declare const CAPACITY_MODEL_LABELS: Record<CapacityModel, string>;
15
+ export declare const CARRIER_LABELS: Record<string, string>;
16
+ export declare function carrierLabel(code: string, fallback?: string): string;
17
+ /** Fallbackprofiel als een type nog niet in de database staat. */
18
+ export declare function fallbackCarrierProfile(code: string): CarrierProfile;
19
+ //# sourceMappingURL=loadCarrierTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadCarrierTypes.d.ts","sourceRoot":"","sources":["../../src/lib/loadCarrierTypes.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1E,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,kBAAkB,GAClB,mBAAmB,GACnB,QAAQ,GACR,aAAa,GACb,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAK/D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOjD,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAmBnE"}
@@ -0,0 +1,41 @@
1
+ // ============================================================================
2
+ // src/lib/loadCarrierTypes.ts — gedeelde typen en labels voor ladingdragers
3
+ // ============================================================================
4
+ export const CAPACITY_MODEL_LABELS = {
5
+ layered: "Lagen (stapelwagen / container)",
6
+ stacked: "Gestapeld (pallet met dozen)",
7
+ single: "Enkele eenheid (doos)",
8
+ none: "Los — geen ladingdrager",
9
+ };
10
+ export const CARRIER_LABELS = {
11
+ trolley: "Stapelwagen",
12
+ danish_container: "Deense container",
13
+ euro_rolcontainer: "Euro-rolcontainer",
14
+ pallet: "Pallet",
15
+ euro_pallet: "Euro-pallet",
16
+ loose: "Geen ladingdrager",
17
+ };
18
+ export function carrierLabel(code, fallback) {
19
+ return CARRIER_LABELS[code] ?? fallback ?? code;
20
+ }
21
+ /** Fallbackprofiel als een type nog niet in de database staat. */
22
+ export function fallbackCarrierProfile(code) {
23
+ const layered = { capacity_model: "layered", layers_per_carrier: 5 };
24
+ const map = {
25
+ trolley: layered,
26
+ danish_container: layered,
27
+ euro_rolcontainer: { capacity_model: "layered", layers_per_carrier: 6 },
28
+ pallet: { capacity_model: "stacked", layers_per_carrier: 4, max_boxes: 40 },
29
+ euro_pallet: { capacity_model: "stacked", layers_per_carrier: 4, max_boxes: 32 },
30
+ loose: { capacity_model: "none", layers_per_carrier: 1, allow_mixing: false },
31
+ };
32
+ return {
33
+ id: null,
34
+ code,
35
+ name: carrierLabel(code),
36
+ capacity_model: "layered",
37
+ layers_per_carrier: 5,
38
+ allow_mixing: true,
39
+ ...(map[code] ?? {}),
40
+ };
41
+ }
@@ -0,0 +1,56 @@
1
+ import type { RuleEngineSettings } from "./ruleEngineSchema";
2
+ import { type LogisticsModuleKey, type LogisticsTaskType } from "./logisticsTasks";
3
+ export type LoadCarrierType = "trolley" | "danish_container" | "euro_rolcontainer" | "pallet" | "euro_pallet" | "loose";
4
+ export interface PlannerLoadCarrier {
5
+ type: LoadCarrierType;
6
+ count: number;
7
+ }
8
+ export interface PlannerOrder {
9
+ orderRef: string;
10
+ /** Aanmeldmoment (ISO). Alles wordt hier vanaf teruggerekend. */
11
+ checkinAt: string;
12
+ /** Werkelijk ophaalmoment van de vervoerder (ISO). Valt terug op checkinAt. */
13
+ pickupAt?: string | null;
14
+ customerRef?: string | null;
15
+ deliveryLocationRef?: string | null;
16
+ bufferRef?: string | null;
17
+ loadCarriers?: PlannerLoadCarrier[];
18
+ }
19
+ export interface PlannerContext {
20
+ strategyId?: string | null;
21
+ ruleConfigId?: string | null;
22
+ ruleVersion?: number | null;
23
+ shadow?: boolean;
24
+ }
25
+ export interface PlannedTask {
26
+ taskType: LogisticsTaskType;
27
+ ownerModule: LogisticsModuleKey;
28
+ sequence: number;
29
+ plannedStart: string | null;
30
+ plannedEnd: string | null;
31
+ dueAt: string | null;
32
+ payload: Record<string, unknown>;
33
+ }
34
+ export interface PlannerResult {
35
+ tasks: PlannedTask[];
36
+ /** Berekende tussenwaarden — handig voor het regieoverzicht en voor uitleg. */
37
+ timeline: {
38
+ checkinAt: string;
39
+ pickupAt: string;
40
+ bufferCloseAt: string | null;
41
+ pickStartAt: string;
42
+ pickEndAt: string;
43
+ pickMinutes: number;
44
+ reserveMinutes: number;
45
+ staff: number;
46
+ controlNoticeAt: string | null;
47
+ };
48
+ notes: string[];
49
+ }
50
+ /** Bruto picktijd in minuten voor alle ladingdragers samen, gedeeld door de bezetting. */
51
+ export declare function calculatePickMinutes(carriers: PlannerLoadCarrier[], settings: RuleEngineSettings): {
52
+ minutes: number;
53
+ staff: number;
54
+ };
55
+ export declare function planOrder(order: PlannerOrder, settings: RuleEngineSettings, context?: PlannerContext): PlannerResult;
56
+ //# sourceMappingURL=logisticsPlanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logisticsPlanner.d.ts","sourceRoot":"","sources":["../../src/lib/logisticsPlanner.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAc,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAO/F,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,kBAAkB,GAClB,mBAAmB,GACnB,QAAQ,GACR,aAAa,GACb,OAAO,CAAC;AAEZ,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,kBAAkB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,+EAA+E;IAC/E,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;IACF,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAgDD,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,kBAAkB,EAAE,EAC9B,QAAQ,EAAE,kBAAkB,GAC3B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CASpC;AAMD,wBAAgB,SAAS,CACvB,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAsPf"}
@@ -0,0 +1,273 @@
1
+ // ============================================================================
2
+ // src/lib/logisticsPlanner.ts — pure planner van de Logistics Engine
3
+ // ============================================================================
4
+ // Zet een order + rule config om in een complete takenlijst voor alle modules.
5
+ // Volledig deterministisch: zelfde input geeft altijd dezelfde taken.
6
+ // Geen database, geen netwerk — daardoor los testbaar.
7
+ // ============================================================================
8
+ import { TASK_OWNER } from "./logisticsTasks";
9
+ import { normalizeBufferCriteria } from "./bufferMatching";
10
+ // ---------------------------------------------------------------------------
11
+ // Hulp
12
+ // ---------------------------------------------------------------------------
13
+ const MIN = 60000;
14
+ function num(value, fallback) {
15
+ const n = typeof value === "string" ? Number(value) : value;
16
+ return typeof n === "number" && Number.isFinite(n) ? n : fallback;
17
+ }
18
+ function str(value, fallback) {
19
+ return typeof value === "string" && value.length > 0 ? value : fallback;
20
+ }
21
+ function bool(value, fallback) {
22
+ return typeof value === "boolean" ? value : fallback;
23
+ }
24
+ function fields(settings, stepId) {
25
+ const step = settings?.[stepId];
26
+ return (step && typeof step === "object" && step.fields) || {};
27
+ }
28
+ function shift(iso, minutes) {
29
+ return new Date(new Date(iso).getTime() + minutes * MIN).toISOString();
30
+ }
31
+ const PICK_TIME_KEY = {
32
+ trolley: "pick_time_trolley",
33
+ danish_container: "pick_time_danish_container",
34
+ euro_rolcontainer: "pick_time_euro_rolcontainer",
35
+ pallet: "pick_time_pallet",
36
+ euro_pallet: "pick_time_euro_pallet",
37
+ loose: "pick_time_loose",
38
+ };
39
+ const PICK_TIME_DEFAULT = {
40
+ trolley: 15,
41
+ danish_container: 15,
42
+ euro_rolcontainer: 12,
43
+ pallet: 20,
44
+ euro_pallet: 3,
45
+ loose: 5,
46
+ };
47
+ /** Bruto picktijd in minuten voor alle ladingdragers samen, gedeeld door de bezetting. */
48
+ export function calculatePickMinutes(carriers, settings) {
49
+ const station = fields(settings, "pickstations");
50
+ const staff = Math.max(1, Math.round(num(station.avg_staff, 5)));
51
+ let total = 0;
52
+ for (const carrier of carriers) {
53
+ const perCarrier = num(station[PICK_TIME_KEY[carrier.type]], PICK_TIME_DEFAULT[carrier.type] ?? 10);
54
+ total += perCarrier * Math.max(0, num(carrier.count, 0));
55
+ }
56
+ return { minutes: Math.ceil(total / staff), staff };
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Planner
60
+ // ---------------------------------------------------------------------------
61
+ export function planOrder(order, settings, context = {}) {
62
+ const notes = [];
63
+ const carriers = Array.isArray(order.loadCarriers) ? order.loadCarriers : [];
64
+ const checkinAt = new Date(order.checkinAt).toISOString();
65
+ const pickupAt = order.pickupAt ? new Date(order.pickupAt).toISOString() : checkinAt;
66
+ const orderStep = fields(settings, "order_verwerking");
67
+ const pickStep = fields(settings, "order_picken");
68
+ const controleStep = fields(settings, "controle");
69
+ // --- 1. Orderbuffer ------------------------------------------------------
70
+ const bufferEnabled = bool(orderStep.buffer_enabled, true);
71
+ const bufferCloseAt = bufferEnabled
72
+ ? shift(checkinAt, -num(orderStep.buffer_close_before_minutes, 60))
73
+ : null;
74
+ const bufferRelease = str(orderStep.buffer_release, "auto");
75
+ if (bufferRelease === "none") {
76
+ notes.push("De buffer geeft niets automatisch vrij; iemand moet de order handmatig oppakken.");
77
+ }
78
+ // --- 2. Picktijd ---------------------------------------------------------
79
+ const { minutes: pickMinutes, staff } = calculatePickMinutes(carriers, settings);
80
+ if (carriers.length === 0) {
81
+ notes.push("Geen ladingdragers meegegeven; picktijd is 0 minuten.");
82
+ }
83
+ // --- 3. Reservetijd ------------------------------------------------------
84
+ const pickStartMode = str(pickStep.pick_start_mode, "as_late_as_possible");
85
+ let reserveMinutes = 0;
86
+ if (pickStartMode === "as_late_as_possible") {
87
+ reserveMinutes =
88
+ str(pickStep.pick_reserve_type, "fixed") === "percentage"
89
+ ? Math.ceil((pickMinutes * num(pickStep.pick_reserve_percentage, 20)) / 100)
90
+ : num(pickStep.pick_reserve_minutes, 30);
91
+ }
92
+ // --- 4. Pickvenster ------------------------------------------------------
93
+ let pickEndAt;
94
+ let pickStartAt;
95
+ if (pickStartMode === "window_before_checkin") {
96
+ const unit = str(pickStep.pick_window_unit, "hours");
97
+ const lead = unit === "days" ? num(pickStep.pick_window_days, 1) * 24 * 60 : num(pickStep.pick_window_hours, 4) * 60;
98
+ pickStartAt = shift(checkinAt, -lead);
99
+ pickEndAt = checkinAt;
100
+ }
101
+ else {
102
+ pickEndAt = shift(pickupAt, -reserveMinutes);
103
+ pickStartAt = shift(pickEndAt, -pickMinutes);
104
+ }
105
+ if (bufferCloseAt && new Date(bufferCloseAt) > new Date(pickStartAt)) {
106
+ notes.push("De buffer sluit later dan het picken zou moeten beginnen; controleer de buffertijd.");
107
+ }
108
+ // --- 5. Taken opbouwen ---------------------------------------------------
109
+ const tasks = [];
110
+ let seq = 0;
111
+ const base = {
112
+ orderRef: order.orderRef,
113
+ customerRef: order.customerRef ?? null,
114
+ checkinAt,
115
+ pickupAt,
116
+ };
117
+ const add = (taskType, plannedStart, plannedEnd, payload = {}) => {
118
+ tasks.push({
119
+ taskType,
120
+ ownerModule: TASK_OWNER[taskType],
121
+ sequence: seq++,
122
+ plannedStart,
123
+ plannedEnd,
124
+ dueAt: plannedEnd ?? plannedStart,
125
+ payload: { ...base, ...payload },
126
+ });
127
+ };
128
+ if (bufferEnabled && bufferCloseAt) {
129
+ add("buffer.close", bufferCloseAt, bufferCloseAt, {
130
+ bufferRef: order.bufferRef ?? null,
131
+ release: bufferRelease,
132
+ matchCriteria: normalizeBufferCriteria(orderStep.buffer_match),
133
+ });
134
+ }
135
+ add("stock.reserve", bufferCloseAt ?? pickStartAt, bufferCloseAt ?? pickStartAt, {
136
+ loadCarriers: carriers,
137
+ });
138
+ add("load.compose", shift(pickStartAt, -10), pickStartAt, {
139
+ loadCarriers: carriers,
140
+ fillMode: "generated",
141
+ });
142
+ add("pick.execute", pickStartAt, pickEndAt, {
143
+ pickMode: str(pickStep.pick_mode, "cart"),
144
+ pickStartMode,
145
+ pickMinutes,
146
+ staff,
147
+ reserveMinutes,
148
+ scanner: str(pickStep.scanner, "required"),
149
+ loadCarriers: carriers,
150
+ });
151
+ if (str(pickStep.scanner, "required") === "required") {
152
+ add("scan.verify", pickStartAt, pickEndAt, { reason: "pick_scanner_required" });
153
+ }
154
+ // --- 6. Controle ---------------------------------------------------------
155
+ const controlMode = str(controleStep.control_mode, "scanner");
156
+ const controlNoticeAt = controlMode === "skip" ? null : shift(pickupAt, -num(controleStep.control_notice_minutes, 60));
157
+ if (controlNoticeAt) {
158
+ const controlOwner = controlMode === "scanner" ? "scanners" : "afhandeling";
159
+ tasks.push({
160
+ taskType: "control.execute",
161
+ ownerModule: controlOwner,
162
+ sequence: seq++,
163
+ plannedStart: controlNoticeAt,
164
+ plannedEnd: pickupAt,
165
+ dueAt: pickupAt,
166
+ payload: { ...base, controlMode, noticeAt: controlNoticeAt },
167
+ });
168
+ if (controlMode === "paper") {
169
+ const printItems = str(controleStep.control_print_items, "control_note");
170
+ add("print.control_note", controlNoticeAt, controlNoticeAt, { printItems });
171
+ }
172
+ }
173
+ else {
174
+ notes.push("Controle staat uit; er wordt geen controletaak aangemaakt.");
175
+ }
176
+ // --- 7. Verzendbrief -----------------------------------------------------
177
+ const shippingChoice = str(controleStep.control_print_items, "control_note") === "control_note_and_shipping" && controlMode === "paper"
178
+ ? "with_control"
179
+ : "with_packing_slip";
180
+ const shippingAt = shippingChoice === "with_control" ? (controlNoticeAt ?? pickEndAt) : pickStartAt;
181
+ // --- 7b. Documenten (Rule Engine stap "Documenten") ----------------------
182
+ const docStep = fields(settings, "pickstations");
183
+ const HOUR = 60;
184
+ const docRelease = (mode, hours) => {
185
+ if (mode === "hours_before_pick")
186
+ return shift(pickStartAt, -hours * HOUR);
187
+ return pickStartAt;
188
+ };
189
+ const packingRelease = str(docStep.packing_slip_release, "with_pick_release");
190
+ const packingAt = docRelease(packingRelease, num(docStep.packing_slip_hours_before, 2));
191
+ const pickMode = str(pickStep.pick_mode, "cart");
192
+ const groupIdenticalCarts = pickMode === "cart" && bool(docStep.packing_slip_group_identical_carts, true);
193
+ add("print.packing_slip", packingAt, packingAt, {
194
+ documentType: "pakbon",
195
+ release: packingRelease,
196
+ pickStationId: str(docStep.packing_slip_station, "") || null,
197
+ requiresApproval: false,
198
+ pickMode,
199
+ groupIdenticalCarts,
200
+ cartsPerPrint: groupIdenticalCarts ? Math.max(1, num(docStep.packing_slip_carts_per_print, 1)) : null,
201
+ splitIdenticalGroups: false,
202
+ });
203
+ const stickerRelease = str(docStep.sticker_release, "with_pick_release");
204
+ const stickerAt = docRelease(stickerRelease, num(docStep.sticker_hours_before, 2));
205
+ add("print.sticker", stickerAt, stickerAt, {
206
+ documentType: "sticker",
207
+ release: stickerRelease,
208
+ pickStationId: str(docStep.sticker_station, "") || null,
209
+ requiresApproval: false,
210
+ });
211
+ const shippingRelease = str(docStep.shipping_note_release, "after_checkin");
212
+ let shippingReleaseAt;
213
+ switch (shippingRelease) {
214
+ case "with_packing_slip":
215
+ shippingReleaseAt = packingAt;
216
+ break;
217
+ case "after_scan":
218
+ shippingReleaseAt = pickEndAt;
219
+ break;
220
+ case "manual_approval":
221
+ shippingReleaseAt = checkinAt;
222
+ break;
223
+ case "after_checkin":
224
+ default:
225
+ shippingReleaseAt = checkinAt;
226
+ }
227
+ add("print.shipping_note", shippingReleaseAt, shippingReleaseAt, {
228
+ documentType: "verzendbrief",
229
+ moment: shippingChoice,
230
+ release: shippingRelease,
231
+ pickStationId: str(docStep.shipping_note_station, "") || null,
232
+ requiresApproval: shippingRelease === "manual_approval",
233
+ printedAt: shippingAt,
234
+ });
235
+ const productionRelease = str(docStep.production_note_release, "on_stock_upgrade");
236
+ add("print.production_note", null, null, {
237
+ documentType: "productiebon",
238
+ release: productionRelease,
239
+ trigger: "stock.upgraded",
240
+ pickStationId: str(docStep.production_note_station, "") || null,
241
+ requiresApproval: productionRelease === "manual_approval_after_upgrade",
242
+ });
243
+ if (shippingRelease === "manual_approval") {
244
+ notes.push("De verzendbrief wacht op handmatige goedkeuring op een pickstation.");
245
+ }
246
+ if (productionRelease === "manual_approval_after_upgrade") {
247
+ notes.push("De productiebon wacht na opwaardering op handmatige goedkeuring.");
248
+ }
249
+ // --- 8. Transport --------------------------------------------------------
250
+ add("transport.dispatch", pickupAt, pickupAt, {});
251
+ // Shadow-vlag meegeven op elke taak-payload voor de ontvangende module.
252
+ const shadow = context.shadow ?? true;
253
+ for (const task of tasks) {
254
+ task.payload.shadow = shadow;
255
+ task.payload.strategyId = context.strategyId ?? null;
256
+ task.payload.ruleVersion = context.ruleVersion ?? null;
257
+ }
258
+ return {
259
+ tasks,
260
+ timeline: {
261
+ checkinAt,
262
+ pickupAt,
263
+ bufferCloseAt,
264
+ pickStartAt,
265
+ pickEndAt,
266
+ pickMinutes,
267
+ reserveMinutes,
268
+ staff,
269
+ controlNoticeAt,
270
+ },
271
+ notes,
272
+ };
273
+ }