@happychef/algorithm 1.2.5 → 1.2.6

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.
@@ -49,16 +49,16 @@ function getMealType(time) {
49
49
  }
50
50
 
51
51
  /**
52
- * Count number of groups/reservations arriving at exact time
52
+ * Count guests arriving at exact time
53
53
  * @param {Array} reservations - Reservation list
54
54
  * @param {string} date - Date (YYYY-MM-DD)
55
55
  * @param {string} time - Time (HH:MM)
56
- * @returns {number} - Number of groups/arrivals (count of reservations)
56
+ * @returns {number} - Guest count
57
57
  */
58
58
  function countArrivalsAtTime(reservations, date, time) {
59
59
  return reservations
60
60
  .filter(r => r.date === date && r.time === time)
61
- .length; // Count number of reservations, not total guests
61
+ .reduce((sum, r) => sum + (parseInt(r.guests, 10) || 0), 0);
62
62
  }
63
63
 
64
64
  /**
@@ -92,18 +92,17 @@ function getMealType(time) {
92
92
 
93
93
  // Get max arrivals value for this specific time
94
94
  const maxArrivals = extractNumber(maxArrivalsConfig[time]);
95
- if (maxArrivals === null || maxArrivals <= 0) {
96
- // Keep the timeblock if no specific max arrivals for this time, or if set to 0 (unlimited)
95
+ if (maxArrivals === null) {
96
+ // Keep the timeblock if no specific max arrivals for this time
97
97
  filteredBlocks[time] = timeData;
98
98
  continue;
99
99
  }
100
-
100
+
101
101
  // Count current arrivals at this exact time
102
102
  const currentArrivals = countArrivalsAtTime(reservations, date, time);
103
-
103
+
104
104
  // Only include timeblock if adding these guests doesn't exceed max arrivals
105
- // Note: maxArrivals is the number of GROUPS (arrivals), not guests
106
- if (currentArrivals + 1 <= maxArrivals) {
105
+ if (currentArrivals + guests <= maxArrivals) {
107
106
  filteredBlocks[time] = timeData;
108
107
  }
109
108
  }
@@ -166,11 +166,10 @@ function getMealType(time) {
166
166
  // Extract the maximum number of groups allowed for this threshold
167
167
  const maxAllowedCount = extractNumber(maxGroupSettings[limitSizeStr]);
168
168
  logDebug(` Max groups allowed for >=${limitSize} is: ${maxAllowedCount} (raw setting: ${JSON.stringify(maxGroupSettings[limitSizeStr])})`);
169
-
170
- // If the setting for this limit size is invalid (null, zero, or negative), treat it as 'no limit'
171
- // IMPORTANT: A value of 0 means "unlimited" - don't apply this filter
172
- if (maxAllowedCount === null || maxAllowedCount <= 0) {
173
- logDebug(` Skipping check: Invalid or non-positive max count (${maxAllowedCount}) - treating as unlimited for size ${limitSize}.`);
169
+
170
+ // If the setting for this limit size is invalid (null or negative), treat it as 'no limit'
171
+ if (maxAllowedCount === null || maxAllowedCount < 0) {
172
+ logDebug(` Skipping check: Invalid or non-positive max count defined for size ${limitSize}.`);
174
173
  continue; // Skip to the next limitSize
175
174
  }
176
175
 
@@ -1,27 +1,22 @@
1
1
  // getAvailableTimeblocks.js
2
2
 
3
3
  const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
4
- const { filterTimeblocksByMaxArrivals } = require('./filters/maxArrivalsFilter');
5
- const { filterTimeblocksByMaxGroups } = require('./filters/maxGroupsFilter');
4
+ const { parseTime, getMealTypeByTime } = require('./tableHelpers');
6
5
 
7
6
  /**
8
7
  * Parses a time string in "HH:MM" format into a Date object on a specific date.
9
8
  * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
10
9
  * @param {string} timeStr - Time string in "HH:MM" format.
11
- * @param {string} timeZone - The IANA time zone identifier.
12
- * @returns {Date} Date object representing the time on the specified date and time zone.
10
+ * @param {string} timeZone - The IANA time zone identifier (not used, kept for compatibility).
11
+ * @returns {Date} Date object representing the time on the specified date.
13
12
  */
14
13
  function parseDateTimeInTimeZone(dateStr, timeStr, timeZone) {
15
14
  const [year, month, day] = dateStr.split('-').map(Number);
16
15
  const [hours, minutes] = timeStr.split(':').map(Number);
17
16
 
18
- // Create a date object in UTC
19
- const date = new Date(Date.UTC(year, month - 1, day, hours, minutes));
20
- // Convert that UTC date/time to the specified time zone
21
- const dateInTimeZone = new Date(
22
- date.toLocaleString('en-US', { timeZone: timeZone })
23
- );
24
- return dateInTimeZone;
17
+ // Create a simple date object for the given date and time
18
+ // This represents the local time on that date
19
+ return new Date(year, month - 1, day, hours, minutes);
25
20
  }
26
21
 
27
22
  /**
@@ -59,23 +54,19 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
59
54
  // Time zone for CEST/CET (Europe/Amsterdam)
60
55
  const timeZone = 'Europe/Amsterdam';
61
56
 
62
- // Current date/time in CEST
57
+ // Current date/time in local timezone (system time)
58
+ // Note: We assume the server is running in the correct timezone
63
59
  const now = new Date();
64
- const currentTimeInTimeZone = new Date(
65
- now.toLocaleString('en-US', { timeZone: timeZone })
66
- );
60
+ const currentTimeInTimeZone = now;
67
61
 
68
62
  // Calculate the maximum allowed date
69
63
  const maxAllowedDate = new Date(currentTimeInTimeZone.getTime());
70
64
  maxAllowedDate.setDate(maxAllowedDate.getDate() + dagenInToekomst);
71
65
  maxAllowedDate.setHours(23, 59, 59, 999);
72
66
 
73
- // Parse the target date in the specified time zone
67
+ // Parse the target date (just the date part, no time)
74
68
  const [year, month, day] = dateStr.split('-').map(Number);
75
- const targetDate = new Date(Date.UTC(year, month - 1, day));
76
- const targetDateInTimeZone = new Date(
77
- targetDate.toLocaleString('en-US', { timeZone: timeZone })
78
- );
69
+ const targetDateInTimeZone = new Date(year, month - 1, day);
79
70
 
80
71
  // Check if targetDateInTimeZone is within dagenInToekomst (skip for admin)
81
72
  if (!isAdmin && targetDateInTimeZone > maxAllowedDate) {
@@ -88,7 +79,7 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
88
79
  currentTimeInTimeZone.toDateString() === targetDateInTimeZone.toDateString();
89
80
 
90
81
  // Get available time blocks or shifts
91
- let availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin);
82
+ const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin);
92
83
 
93
84
  // If the date is today and uurOpVoorhand is greater than zero, prune time blocks (skip for admin)
94
85
  if (!isAdmin && isToday && uurOpVoorhand >= 0) {
@@ -106,14 +97,44 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
106
97
  }
107
98
  }
108
99
 
109
- // Apply max arrivals filter (skip for admin)
100
+ // Apply last booking time filter (lunchStop, dinerStop, ontbijtStop) - skip for admin
110
101
  if (!isAdmin) {
111
- availableTimeblocks = filterTimeblocksByMaxArrivals(data, dateStr, availableTimeblocks, reservations, guests);
112
- }
113
-
114
- // Apply max groups filter (skip for admin)
115
- if (!isAdmin) {
116
- availableTimeblocks = filterTimeblocksByMaxGroups(data, dateStr, availableTimeblocks, reservations, guests);
102
+ const settings = data?.['general-settings'] || {};
103
+ const breakfastStop = settings.ontbijtStop || null;
104
+ const lunchStop = settings.lunchStop || null;
105
+ const dinnerStop = settings.dinerStop || null;
106
+
107
+ // Only apply if at least one stop time is configured
108
+ if (breakfastStop || lunchStop || dinnerStop) {
109
+ for (const time in availableTimeblocks) {
110
+ const mealType = getMealTypeByTime(time);
111
+ const timeMinutes = parseTime(time);
112
+ let shouldRemove = false;
113
+
114
+ // Check if this time is AT OR AFTER the stop time for its meal type
115
+ // Using ">=" so that lunchStop="14:00" blocks 14:00 and later times
116
+ if (mealType === 'breakfast' && breakfastStop) {
117
+ const stopMinutes = parseTime(breakfastStop);
118
+ if (timeMinutes >= stopMinutes) {
119
+ shouldRemove = true;
120
+ }
121
+ } else if (mealType === 'lunch' && lunchStop) {
122
+ const stopMinutes = parseTime(lunchStop);
123
+ if (timeMinutes >= stopMinutes) {
124
+ shouldRemove = true;
125
+ }
126
+ } else if (mealType === 'dinner' && dinnerStop) {
127
+ const stopMinutes = parseTime(dinnerStop);
128
+ if (timeMinutes >= stopMinutes) {
129
+ shouldRemove = true;
130
+ }
131
+ }
132
+
133
+ if (shouldRemove) {
134
+ delete availableTimeblocks[time];
135
+ }
136
+ }
137
+ }
117
138
  }
118
139
 
119
140
  return availableTimeblocks;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happychef/algorithm",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "Restaurant and reservation algorithm utilities",
5
5
  "main": "index.js",
6
6
  "author": "happy chef",