@flowselections/floriday-afhandeling-module 1.0.9 → 1.0.11

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 (96) hide show
  1. package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts +7 -0
  2. package/dist-lib/components/afhandeling/ControleLijstPrint.d.ts.map +1 -0
  3. package/dist-lib/components/afhandeling/ControleLijstPrint.js +67 -0
  4. package/dist-lib/components/afhandeling/ControlePage.d.ts +2 -0
  5. package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -0
  6. package/dist-lib/components/afhandeling/ControlePage.js +456 -0
  7. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +9 -0
  8. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -0
  9. package/dist-lib/components/afhandeling/DagDetailDialog.js +149 -0
  10. package/dist-lib/components/afhandeling/LeveringenPage.d.ts.map +1 -1
  11. package/dist-lib/components/afhandeling/LeveringenPage.js +11 -5
  12. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  13. package/dist-lib/components/afhandeling/PickAgendaView.js +13 -4
  14. package/dist-lib/components/afhandeling/PlanningAgendaPage.d.ts.map +1 -1
  15. package/dist-lib/components/afhandeling/PlanningAgendaPage.js +2 -2
  16. package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts +12 -0
  17. package/dist-lib/components/afhandeling/ServiceAreaSelect.d.ts.map +1 -0
  18. package/dist-lib/components/afhandeling/ServiceAreaSelect.js +79 -0
  19. package/dist-lib/components/afhandeling/VervoerderForm.d.ts.map +1 -1
  20. package/dist-lib/components/afhandeling/VervoerderForm.js +62 -6
  21. package/dist-lib/components/afhandeling/VervoerdersPage.d.ts.map +1 -1
  22. package/dist-lib/components/afhandeling/VervoerdersPage.js +25 -6
  23. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts +2 -1
  24. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  25. package/dist-lib/components/afhandeling/WeekAgendaView.js +63 -18
  26. package/dist-lib/components/afhandeling/WeekdayPicker.d.ts +12 -0
  27. package/dist-lib/components/afhandeling/WeekdayPicker.d.ts.map +1 -0
  28. package/dist-lib/components/afhandeling/WeekdayPicker.js +30 -0
  29. package/dist-lib/components/pickstation/PakbonTemplate.d.ts +6 -2
  30. package/dist-lib/components/pickstation/PakbonTemplate.d.ts.map +1 -1
  31. package/dist-lib/components/pickstation/PakbonTemplate.js +37 -12
  32. package/dist-lib/components/settings/AfhandelingSettingsCard.d.ts.map +1 -1
  33. package/dist-lib/components/settings/AfhandelingSettingsCard.js +25 -6
  34. package/dist-lib/components/ui/responsive-dialog.d.ts +20 -0
  35. package/dist-lib/components/ui/responsive-dialog.d.ts.map +1 -0
  36. package/dist-lib/components/ui/responsive-dialog.js +44 -0
  37. package/dist-lib/hooks/useOrderPlanning.d.ts +3 -0
  38. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  39. package/dist-lib/hooks/useOrderPlanning.js +64 -12
  40. package/dist-lib/index.d.ts +1 -0
  41. package/dist-lib/index.d.ts.map +1 -1
  42. package/dist-lib/index.js +4 -2
  43. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2898 -147
  44. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  45. package/dist-lib/integrations/supabase/client.d.ts +2898 -147
  46. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  47. package/dist-lib/integrations/supabase/client.server.d.ts +2898 -147
  48. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  49. package/dist-lib/integrations/supabase/types.d.ts +2986 -155
  50. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  51. package/dist-lib/integrations/supabase/types.js +1 -0
  52. package/dist-lib/lib/afhandelingSettings.functions.d.ts +10666 -5151
  53. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  54. package/dist-lib/lib/afhandelingSettings.functions.js +42 -2
  55. package/dist-lib/lib/carrierMatch.server.d.ts +27 -0
  56. package/dist-lib/lib/carrierMatch.server.d.ts.map +1 -0
  57. package/dist-lib/lib/carrierMatch.server.js +75 -0
  58. package/dist-lib/lib/carrierSignupMail.server.d.ts +56 -0
  59. package/dist-lib/lib/carrierSignupMail.server.d.ts.map +1 -0
  60. package/dist-lib/lib/carrierSignupMail.server.js +229 -0
  61. package/dist-lib/lib/carriers.functions.d.ts +36404 -15470
  62. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  63. package/dist-lib/lib/carriers.functions.js +43 -2
  64. package/dist-lib/lib/cartAssignments.functions.d.ts +49609 -0
  65. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -0
  66. package/dist-lib/lib/cartAssignments.functions.js +130 -0
  67. package/dist-lib/lib/cartControl.functions.d.ts +99259 -0
  68. package/dist-lib/lib/cartControl.functions.d.ts.map +1 -0
  69. package/dist-lib/lib/cartControl.functions.js +489 -0
  70. package/dist-lib/lib/cartMoveImpact.d.ts +8 -0
  71. package/dist-lib/lib/cartMoveImpact.d.ts.map +1 -0
  72. package/dist-lib/lib/cartMoveImpact.js +26 -0
  73. package/dist-lib/lib/cartRescheduling.d.ts +30 -0
  74. package/dist-lib/lib/cartRescheduling.d.ts.map +1 -0
  75. package/dist-lib/lib/cartRescheduling.js +71 -0
  76. package/dist-lib/lib/floriday.functions.d.ts +2902 -147
  77. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  78. package/dist-lib/lib/floriday.functions.js +178 -98
  79. package/dist-lib/lib/geo/euServiceAreas.d.ts +30 -0
  80. package/dist-lib/lib/geo/euServiceAreas.d.ts.map +1 -0
  81. package/dist-lib/lib/geo/euServiceAreas.js +277 -0
  82. package/dist-lib/lib/invalidatePlanning.d.ts +11 -0
  83. package/dist-lib/lib/invalidatePlanning.d.ts.map +1 -0
  84. package/dist-lib/lib/invalidatePlanning.js +25 -0
  85. package/dist-lib/lib/pickstationI18n.d.ts +12 -0
  86. package/dist-lib/lib/pickstationI18n.d.ts.map +1 -1
  87. package/dist-lib/lib/pickstationI18n.js +72 -0
  88. package/dist-lib/lib/pickstationOrders.server.d.ts +2 -1
  89. package/dist-lib/lib/pickstationOrders.server.d.ts.map +1 -1
  90. package/dist-lib/lib/pickstationOrders.server.js +393 -178
  91. package/dist-lib/lib/pickstationTypes.d.ts +23 -2
  92. package/dist-lib/lib/pickstationTypes.d.ts.map +1 -1
  93. package/dist-lib/lib/planning.d.ts +41 -2
  94. package/dist-lib/lib/planning.d.ts.map +1 -1
  95. package/dist-lib/lib/planning.js +159 -36
  96. package/package.json +2 -2
@@ -29,17 +29,46 @@ export interface PlanningRules {
29
29
  /** Tijdstip waarop de order is binnengekomen (voor moment-selectie). */
30
30
  orderCreatedAt?: string | Date | null;
31
31
  holidays?: Date[];
32
+ /** ISO-dagnummers (1=ma … 7=zo) waarop gepickt mag worden. */
33
+ pickWeekdays?: number[];
34
+ /** ISO-dagnummers waarop de vervoerder karren bij ons ophaalt. */
35
+ carrierPickupWeekdays?: number[];
36
+ /** ISO-dagnummers waarop de vervoerder bij de klant bezorgt. */
37
+ carrierDeliveryWeekdays?: number[];
38
+ }
39
+ export interface PickAssignment {
40
+ date: Date;
41
+ cartCount: number;
42
+ /** Concrete kar-indexen op deze datum; nodig voor handmatig verplaatsen. */
43
+ cartIndexes?: number[];
32
44
  }
33
45
  export interface OrderPlanning {
34
46
  deliveryDate: Date;
47
+ /** Oorspronkelijk gevraagde bezorgdatum (vóór eventuele weekdag-snap). */
48
+ requestedDeliveryDate: Date;
35
49
  pickupDate: Date;
36
50
  transportRegisterDate: Date;
37
51
  pickStartDate: Date;
38
52
  scheduledPickDate: Date;
53
+ /** Verdeling van karren over één of meerdere pickdagen. */
54
+ pickAssignments: PickAssignment[];
39
55
  movedEarlier: boolean;
40
56
  /** Welk moment is gekozen (handig voor debug / UI). */
41
57
  selectedMoment: PickupMoment;
58
+ /** Korte uitleg waarom deze planning zo is gekozen. */
59
+ reason?: string;
60
+ /** Naam van de gekozen vervoerder (indien bekend). */
61
+ carrierName?: string;
42
62
  }
63
+ /** ISO-dagnummer 1=ma … 7=zo van een Date. */
64
+ export declare function isoWeekday(d: Date): number;
65
+ export declare function normalizeWeekdays(days: number[] | undefined | null): number[];
66
+ /**
67
+ * Schuif een datum naar de dichtstbijzijnde toegestane weekdag
68
+ * (feestdagen uitgesloten). `direction` bepaalt of we naar het verleden
69
+ * of naar de toekomst opschuiven.
70
+ */
71
+ export declare function snapToAllowedDay(date: Date, allowedWeekdays: number[] | undefined, direction: "backward" | "forward", holidays?: Date[]): Date;
43
72
  export declare function isWorkday(d: Date, holidays?: Date[]): boolean;
44
73
  export declare function subtractWorkdays(date: Date, n: number, holidays?: Date[]): Date;
45
74
  export declare function addWorkdays(date: Date, n: number, holidays?: Date[]): Date;
@@ -55,12 +84,22 @@ export declare function getSignupDeadline(deliveryDate: Date, moment: PickupMome
55
84
  */
56
85
  export declare function selectPickupMoment(deliveryDate: Date, orderCreatedAt: Date | null | undefined, moments: PickupMoment[], holidays?: Date[]): PickupMoment;
57
86
  export declare function calculateOrderPlanning(deliveryDate: string | Date | null | undefined, rules: PlanningRules): OrderPlanning | null;
58
- export interface PickDistributionOptions {
87
+ export interface PickDistributionOptions<T = unknown> {
59
88
  enabled: boolean;
60
89
  maxWorkdaysEarlier: number;
61
90
  holidays?: Date[];
91
+ /** ISO-dagnummers waarop gepickt mag worden (1=ma … 7=zo). */
92
+ pickWeekdays?: number[];
93
+ /** Aantal karren per item — bepaalt het gewicht in de verdeling. */
94
+ getCartCount?: (item: T) => number;
95
+ /** Manual per-cart overrides — geïndexeerd op (itemKey, cartIndex) → Date. */
96
+ getCartOverride?: (item: T, cartIndex: number) => Date | PickCartOverride | null;
97
+ }
98
+ export interface PickCartOverride {
99
+ date: Date;
100
+ movedFromDate?: Date | null;
62
101
  }
63
102
  export declare function distributePickDates<T extends {
64
103
  planning: OrderPlanning | null;
65
- }>(items: T[], options: PickDistributionOptions): T[];
104
+ }>(items: T[], options: PickDistributionOptions<T>): T[];
66
105
  //# sourceMappingURL=planning.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../../src/lib/planning.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,qBAAqB,EAAE,YAOnC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,wDAAwD;IACxD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,qBAAqB,EAAE,IAAI,CAAC;IAC5B,aAAa,EAAE,IAAI,CAAC;IACpB,iBAAiB,EAAE,IAAI,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,uDAAuD;IACvD,cAAc,EAAE,YAAY,CAAC;CAC9B;AAUD,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,CAK7D;AAQD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAQ/E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAQ1E;AAcD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,IAAI,EAClB,MAAM,EAAE,YAAY,EACpB,QAAQ,CAAC,EAAE,IAAI,EAAE,GAChB,IAAI,CAGN;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,IAAI,EAClB,cAAc,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EACvC,OAAO,EAAE,YAAY,EAAE,EACvB,QAAQ,CAAC,EAAE,IAAI,EAAE,GAChB,YAAY,CAgBd;AAED,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,EAC9C,KAAK,EAAE,aAAa,GACnB,aAAa,GAAG,IAAI,CA6BtB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAA;CAAE,EAC9E,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,uBAAuB,GAC/B,CAAC,EAAE,CA2DL"}
1
+ {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../../src/lib/planning.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,38 @@ export const DEFAULT_PICKUP_MOMENT = {
14
14
  delivery_days: 0,
15
15
  delivery_time: "08:00",
16
16
  };
17
+ /** ISO-dagnummer 1=ma … 7=zo van een Date. */
18
+ export function isoWeekday(d) {
19
+ const g = d.getDay();
20
+ return g === 0 ? 7 : g;
21
+ }
22
+ const DEFAULT_WEEKDAYS = [1, 2, 3, 4, 5];
23
+ export function normalizeWeekdays(days) {
24
+ if (!Array.isArray(days) || days.length === 0)
25
+ return DEFAULT_WEEKDAYS;
26
+ const clean = days.map((n) => Number(n)).filter((n) => n >= 1 && n <= 7);
27
+ return clean.length > 0 ? Array.from(new Set(clean)) : DEFAULT_WEEKDAYS;
28
+ }
29
+ function isHoliday(d, holidays) {
30
+ return !!holidays && holidays.some((h) => sameDay(h, d));
31
+ }
32
+ /**
33
+ * Schuif een datum naar de dichtstbijzijnde toegestane weekdag
34
+ * (feestdagen uitgesloten). `direction` bepaalt of we naar het verleden
35
+ * of naar de toekomst opschuiven.
36
+ */
37
+ export function snapToAllowedDay(date, allowedWeekdays, direction, holidays) {
38
+ const allowed = new Set(normalizeWeekdays(allowedWeekdays));
39
+ const out = normalize(date);
40
+ const step = direction === "forward" ? 1 : -1;
41
+ // Vangnet: max 62 iteraties om oneindige loops te voorkomen.
42
+ for (let i = 0; i < 62; i++) {
43
+ if (allowed.has(isoWeekday(out)) && !isHoliday(out, holidays))
44
+ return out;
45
+ out.setDate(out.getDate() + step);
46
+ }
47
+ return out;
48
+ }
17
49
  function sameDay(a, b) {
18
50
  return (a.getFullYear() === b.getFullYear() &&
19
51
  a.getMonth() === b.getMonth() &&
@@ -99,79 +131,170 @@ export function calculateOrderPlanning(deliveryDate, rules) {
99
131
  const d = typeof deliveryDate === "string" ? new Date(deliveryDate) : deliveryDate;
100
132
  if (Number.isNaN(d.getTime()))
101
133
  return null;
102
- const delivery = normalize(d);
134
+ const requested = normalize(d);
103
135
  const moments = rules.pickupMoments.length > 0 ? rules.pickupMoments : [DEFAULT_PICKUP_MOMENT];
104
136
  const orderCreated = rules.orderCreatedAt == null
105
137
  ? null
106
138
  : rules.orderCreatedAt instanceof Date
107
139
  ? rules.orderCreatedAt
108
140
  : new Date(rules.orderCreatedAt);
141
+ const reasons = [];
142
+ // 1) Snap bezorgdatum vooruit naar een dag waarop de vervoerder bezorgt.
143
+ const delivery = snapToAllowedDay(requested, rules.carrierDeliveryWeekdays, "forward", rules.holidays);
144
+ if (!sameDay(delivery, requested)) {
145
+ reasons.push(`Bezorgdatum verschoven naar ${delivery.toLocaleDateString("nl-NL")} omdat de vervoerder niet bezorgt op de gevraagde dag.`);
146
+ }
109
147
  const moment = selectPickupMoment(delivery, orderCreated, moments, rules.holidays);
110
- const pickup = subtractWorkdays(delivery, moment.pickup_days, rules.holidays);
148
+ // 2) Ophaaldatum: terugtellen op werkdagen, daarna snappen op ophaaldagen vervoerder.
149
+ const pickupRaw = subtractWorkdays(delivery, moment.pickup_days, rules.holidays);
150
+ const pickup = snapToAllowedDay(pickupRaw, rules.carrierPickupWeekdays, "backward", rules.holidays);
151
+ if (!sameDay(pickup, pickupRaw)) {
152
+ reasons.push(`Ophaaldatum verschoven naar ${pickup.toLocaleDateString("nl-NL")} omdat de vervoerder alleen op bepaalde dagen ophaalt.`);
153
+ }
111
154
  const transportRegister = subtractWorkdays(delivery, moment.signup_days, rules.holidays);
112
- const pickStart = subtractWorkdays(pickup, rules.pickDurationWorkdays, rules.holidays);
155
+ // 3) Pickstart: terugtellen vanaf ophaal, daarna snappen op pickdagen.
156
+ const pickStartRaw = subtractWorkdays(pickup, rules.pickDurationWorkdays, rules.holidays);
157
+ const pickStart = snapToAllowedDay(pickStartRaw, rules.pickWeekdays, "backward", rules.holidays);
158
+ if (!sameDay(pickStart, pickStartRaw)) {
159
+ reasons.push(`Pickdatum verschoven naar ${pickStart.toLocaleDateString("nl-NL")} vanwege de ingestelde pickdagen.`);
160
+ }
161
+ if (reasons.length === 0) {
162
+ reasons.push(`Planning gebaseerd op ophaalmoment ${moment.pickup_time ?? "standaard"}.`);
163
+ }
113
164
  return {
114
165
  deliveryDate: delivery,
166
+ requestedDeliveryDate: requested,
115
167
  pickupDate: pickup,
116
168
  transportRegisterDate: transportRegister,
117
169
  pickStartDate: pickStart,
118
170
  scheduledPickDate: pickStart,
171
+ pickAssignments: [{ date: pickStart, cartCount: 0 }],
119
172
  movedEarlier: false,
120
173
  selectedMoment: moment,
174
+ reason: reasons.join(" "),
121
175
  };
122
176
  }
123
177
  export function distributePickDates(items, options) {
124
- if (!options.enabled || options.maxWorkdaysEarlier <= 0) {
125
- return items.map((it) => {
126
- if (!it.planning)
127
- return it;
128
- return {
129
- ...it,
130
- planning: {
131
- ...it.planning,
132
- scheduledPickDate: it.planning.pickStartDate,
133
- movedEarlier: false,
134
- },
135
- };
136
- });
137
- }
178
+ const getCarts = (it) => {
179
+ const n = options.getCartCount?.(it);
180
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1;
181
+ };
182
+ const getOverride = (it, idx) => {
183
+ const raw = options.getCartOverride?.(it, idx);
184
+ if (!raw)
185
+ return null;
186
+ const date = raw instanceof Date ? raw : raw.date;
187
+ const out = new Date(date);
188
+ out.setHours(0, 0, 0, 0);
189
+ const movedFrom = raw instanceof Date ? null : raw.movedFromDate;
190
+ const movedFromDate = movedFrom ? new Date(movedFrom) : null;
191
+ if (movedFromDate)
192
+ movedFromDate.setHours(0, 0, 0, 0);
193
+ return { date: out, movedFromDate };
194
+ };
195
+ const key = (d) => `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
196
+ const load = new Map();
197
+ const result = new Map();
198
+ const distribute = options.enabled && options.maxWorkdaysEarlier > 0;
138
199
  const sorted = [...items].sort((a, b) => {
139
200
  const ad = a.planning?.pickStartDate.getTime() ?? Infinity;
140
201
  const bd = b.planning?.pickStartDate.getTime() ?? Infinity;
141
202
  return ad - bd;
142
203
  });
143
- const load = new Map();
144
- const key = (d) => `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
145
- const result = new Map();
204
+ const baseTargets = new Map();
205
+ // Stap 1: bepaal eerst de volledige automatische verdeling, zonder
206
+ // handmatige overrides. Daardoor blijft de automatische balans stabiel en
207
+ // kan een latere handmatige verplaatsing niet door volgende orders worden
208
+ // "teruggevuld".
146
209
  for (const item of sorted) {
147
210
  if (!item.planning) {
148
211
  result.set(item, item);
149
212
  continue;
150
213
  }
151
214
  const deadline = item.planning.pickStartDate;
152
- const candidates = [deadline];
153
- let cursor = deadline;
154
- for (let i = 0; i < options.maxWorkdaysEarlier; i++) {
155
- cursor = subtractWorkdays(cursor, 1, options.holidays);
156
- candidates.unshift(cursor);
215
+ const carts = getCarts(item);
216
+ // Kandidaten in chronologische volgorde — alleen toegestane pickdagen.
217
+ const allowedSet = new Set(normalizeWeekdays(options.pickWeekdays));
218
+ const isAllowed = (d) => allowedSet.has(isoWeekday(d)) && !(options.holidays?.some((h) => sameDay(h, d)));
219
+ const candidates = [];
220
+ if (distribute) {
221
+ let cursor = deadline;
222
+ let added = 0;
223
+ // Loop met veiligheidsmarge; sla dagen over die niet zijn toegestaan.
224
+ for (let i = 0; i < options.maxWorkdaysEarlier * 3 && added < options.maxWorkdaysEarlier; i++) {
225
+ cursor = subtractWorkdays(cursor, 1, options.holidays);
226
+ if (isAllowed(cursor)) {
227
+ candidates.unshift(cursor);
228
+ added++;
229
+ }
230
+ }
157
231
  }
158
- let best = candidates[0];
159
- let bestLoad = load.get(key(best)) ?? 0;
160
- for (let i = 1; i < candidates.length; i++) {
161
- const c = candidates[i];
162
- const l = load.get(key(c)) ?? 0;
163
- if (l < bestLoad) {
164
- best = c;
165
- bestLoad = l;
232
+ candidates.push(deadline);
233
+ const targets = [];
234
+ for (let c = 0; c < carts; c++) {
235
+ let target;
236
+ if (!distribute) {
237
+ target = deadline;
238
+ load.set(key(target), (load.get(key(target)) ?? 0) + 1);
239
+ }
240
+ else {
241
+ let best = candidates[candidates.length - 1];
242
+ let bestLoad = load.get(key(best)) ?? 0;
243
+ for (let i = candidates.length - 2; i >= 0; i--) {
244
+ const cand = candidates[i];
245
+ const l = load.get(key(cand)) ?? 0;
246
+ if (l < bestLoad) {
247
+ best = cand;
248
+ bestLoad = l;
249
+ }
250
+ }
251
+ target = best;
252
+ load.set(key(target), bestLoad + 1);
253
+ }
254
+ targets[c] = target;
255
+ }
256
+ baseTargets.set(item, targets);
257
+ }
258
+ // Stap 2: pas overrides pas toe nadat alle automatische doelen vaststaan.
259
+ // Dit maakt een verplaatsing absoluut: 10/10 wordt na één kar verplaatsen
260
+ // echt 9/11, niet opnieuw 10/10 door load-balancing.
261
+ for (const item of sorted) {
262
+ if (!item.planning)
263
+ continue;
264
+ const deadline = item.planning.pickStartDate;
265
+ const carts = getCarts(item);
266
+ const targets = [...(baseTargets.get(item) ?? [])];
267
+ const perDay = new Map();
268
+ for (let c = 0; c < carts; c++) {
269
+ const override = getOverride(item, c);
270
+ if (override) {
271
+ // Handmatige verplaatsingen gelden altijd voor de concrete kar-index
272
+ // die de gebruiker aanklikt. Niet ruilen met een andere kar op dezelfde
273
+ // brondag, want dan blijft de aangeklikte kar visueel staan.
274
+ targets[c] = override.date;
275
+ }
276
+ }
277
+ for (let c = 0; c < carts; c++) {
278
+ const target = targets[c];
279
+ const k = key(target);
280
+ const existing = perDay.get(k);
281
+ if (existing) {
282
+ existing.cartCount += 1;
283
+ existing.cartIndexes.push(c);
284
+ }
285
+ else {
286
+ perDay.set(k, { date: target, cartCount: 1, cartIndexes: [c] });
166
287
  }
167
288
  }
168
- load.set(key(best), bestLoad + 1);
169
- const moved = best.getTime() !== deadline.getTime();
289
+ const assignments = Array.from(perDay.values()).sort((a, b) => a.date.getTime() - b.date.getTime());
290
+ const earliest = assignments[0]?.date ?? deadline;
291
+ const moved = assignments.some((a) => a.date.getTime() !== deadline.getTime());
170
292
  result.set(item, {
171
293
  ...item,
172
294
  planning: {
173
295
  ...item.planning,
174
- scheduledPickDate: best,
296
+ scheduledPickDate: earliest,
297
+ pickAssignments: assignments,
175
298
  movedEarlier: moved,
176
299
  },
177
300
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-afhandeling-module",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -41,7 +41,7 @@
41
41
  "@cloudflare/vite-plugin": "^1.25.5",
42
42
  "@eslint/js": "^9.32.0",
43
43
  "@flowselections/core": "^1.0.11",
44
- "@lovable.dev/vite-tanstack-config": "2.6.2",
44
+ "@lovable.dev/vite-tanstack-config": "2.6.4",
45
45
  "@rsbuild/core": "^1.0.0",
46
46
  "@tailwindcss/vite": "^4.2.1",
47
47
  "@tanstack/react-query": "^5.83.0",