@happychef/algorithm 1.0.3 → 1.1.0

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.
@@ -30,9 +30,10 @@ function parseDateTimeInTimeZone(dateStr, timeStr, timeZone) {
30
30
  * @param {number} guests - The number of guests for the reservation.
31
31
  * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
32
32
  * @param {string|null} giftcard - Optional giftcard to filter times by meal.
33
+ * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
33
34
  * @returns {Object} - Returns a pruned object of available time blocks or shifts, or an empty object if out of range.
34
35
  */
35
- function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots = [], giftcard = null) {
36
+ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots = [], giftcard = null, isAdmin = false) {
36
37
  // Get 'uurOpVoorhand' from general settings
37
38
  let uurOpVoorhand = 4;
38
39
  if (
@@ -74,8 +75,8 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
74
75
  targetDate.toLocaleString('en-US', { timeZone: timeZone })
75
76
  );
76
77
 
77
- // Check if targetDateInTimeZone is within dagenInToekomst
78
- if (targetDateInTimeZone > maxAllowedDate) {
78
+ // Check if targetDateInTimeZone is within dagenInToekomst (skip for admin)
79
+ if (!isAdmin && targetDateInTimeZone > maxAllowedDate) {
79
80
  // Out of allowed range, return empty object
80
81
  return {};
81
82
  }
@@ -85,10 +86,10 @@ function getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlot
85
86
  currentTimeInTimeZone.toDateString() === targetDateInTimeZone.toDateString();
86
87
 
87
88
  // Get available time blocks or shifts
88
- const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard);
89
+ const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin);
89
90
 
90
- // If the date is today and uurOpVoorhand is greater than zero, prune time blocks
91
- if (isToday && uurOpVoorhand >= 0) {
91
+ // If the date is today and uurOpVoorhand is greater than zero, prune time blocks (skip for admin)
92
+ if (!isAdmin && isToday && uurOpVoorhand >= 0) {
92
93
  const cutoffTime = new Date(currentTimeInTimeZone.getTime());
93
94
  cutoffTime.setHours(cutoffTime.getHours() + uurOpVoorhand);
94
95
 
package/index.js CHANGED
@@ -3,16 +3,16 @@
3
3
  const processing = {
4
4
  ...require("./processing/dailyGuestCounts"),
5
5
  ...require("./processing/mealTypeCount"),
6
- ...require("./processing/timeblocksAvailable"),
6
+ ...require("./processing/timeblocksAvailable")
7
7
  };
8
8
 
9
9
  const reservation_data = {
10
- ...require("./reservation_data/counter"),
10
+ ...require("./reservation_data/counter")
11
11
  };
12
12
 
13
13
  const restaurant_data = {
14
14
  ...require("./restaurant_data/exceptions"),
15
- ...require("./restaurant_data/openinghours"),
15
+ ...require("./restaurant_data/openinghours")
16
16
  };
17
17
 
18
18
  const test = {
@@ -24,39 +24,19 @@ const test = {
24
24
  ...require("./simulateTableAssignment"),
25
25
  ...require("./isDateAvailableWithTableCheck"),
26
26
  ...require("./tableHelpers"),
27
- ...require("./test"),
27
+ ...require("./test")
28
28
  };
29
29
 
30
30
  const filters = {
31
31
  ...require("./filters/timeFilter"),
32
32
  ...require("./filters/maxArrivalsFilter"),
33
- ...require("./filters/maxGroupsFilter"),
33
+ ...require("./filters/maxGroupsFilter")
34
34
  };
35
-
36
- // ✅ Table-related logic and utilities
37
- const tables = {
38
- ...require("./tables/main.js"),
39
- ...require("./tables/availability/isTimeAvailableSync"),
40
- ...require("./tables/availability/getAvailableTablesForTime"),
41
- ...require("./tables/availability/findMultiTableCombination"),
42
- ...require("./tables/assignment/filterTimeblocksByTableAvailability"),
43
- ...require("./tables/assignment/assignTablesForGivenTime"),
44
- ...require("./tables/utils/safeParseInt"),
45
- ...require("./tables/utils/isTemporaryTableValid"),
46
- ...require("./tables/utils/isTableFreeForAllSlots"),
47
- ...require("./tables/utils/calculateDistance"),
48
- ...require("./tables/time/shifts").shifts,
49
- ...require("./tables/time/parseTime"),
50
- ...require("./tables/time/getMealTypeByTime"),
51
- ...require("./tables/time/computeRequiredSlots"),
52
- };
53
-
54
- // ✅ Export everything
35
+ // Merge all exports into one
55
36
  module.exports = {
56
37
  ...processing,
57
38
  ...reservation_data,
58
39
  ...restaurant_data,
59
40
  ...test,
60
- ...filters,
61
- ...tables,
41
+ ...filters
62
42
  };
@@ -59,16 +59,17 @@ function isDateWithinAllowedRange(data, dateStr) {
59
59
  * @param {number} guests - The number of guests for the reservation.
60
60
  * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
61
61
  * @param {string|null} giftcard - Optional giftcard to filter times by meal.
62
+ * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
62
63
  * @returns {boolean} - Returns true if the date has at least one available timeblock, false otherwise.
63
64
  */
64
- function isDateAvailable(data, dateStr, reservations, guests, blockedSlots = [], giftcard = null) {
65
- // Check if date is within allowed range
66
- if (!isDateWithinAllowedRange(data, dateStr)) {
65
+ function isDateAvailable(data, dateStr, reservations, guests, blockedSlots = [], giftcard = null, isAdmin = false) {
66
+ // Check if date is within allowed range (skip for admin)
67
+ if (!isAdmin && !isDateWithinAllowedRange(data, dateStr)) {
67
68
  return false;
68
69
  }
69
70
 
70
71
  // Get available timeblocks using the existing logic
71
- const availableTimeblocks = getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots, giftcard);
72
+ const availableTimeblocks = getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin);
72
73
 
73
74
  // Return true only if we have at least one available timeblock
74
75
  return Object.keys(availableTimeblocks).length > 0;
@@ -69,10 +69,12 @@ function timeHasGiftcard(data, dateStr, timeStr, giftcard) {
69
69
  * @param {string} dateStr - The date string in "YYYY-MM-DD" format.
70
70
  * @param {Array} reservations - An array of reservation objects.
71
71
  * @param {number} guests - The number of guests for the reservation.
72
+ * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
72
73
  * @param {string|null} selectedGiftcard - The selected giftcard (if any).
74
+ * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
73
75
  * @returns {boolean} - Returns true if the date passes all checks to be available.
74
76
  */
75
- function isDateAvailableWithTableCheck(data, dateStr, reservations, guests, selectedGiftcard) {
77
+ function isDateAvailableWithTableCheck(data, dateStr, reservations, guests, blockedSlots = [], selectedGiftcard = null, isAdmin = false) {
76
78
  // 0) Optionally filter by selected menu item's date range (only when normalOpeningTimes = false)
77
79
  const currentItem = typeof window !== 'undefined' ? window.currentReservationTicketItem : null;
78
80
  if (
@@ -112,7 +114,7 @@ function isDateAvailableWithTableCheck(data, dateStr, reservations, guests, sele
112
114
  console.log(`Table assignment is ${isTableAssignmentEnabled ? 'ENABLED' : 'DISABLED'} for date ${dateStr}`);
113
115
 
114
116
  // 1) First, do the standard day-level checks (including simple giftcard check).
115
- const basicDateAvailable = isDateAvailable(data, dateStr, reservations, guests, selectedGiftcard);
117
+ const basicDateAvailable = isDateAvailable(data, dateStr, reservations, guests, blockedSlots, selectedGiftcard, isAdmin);
116
118
  if (!basicDateAvailable) {
117
119
  console.log(`Date ${dateStr} fails basic availability check`);
118
120
  return false;
@@ -126,7 +128,7 @@ function isDateAvailableWithTableCheck(data, dateStr, reservations, guests, sele
126
128
 
127
129
  // 2) Get all available timeblocks for this date
128
130
  console.log(`Getting available timeblocks for ${dateStr}`);
129
- const availableTimeblocks = getAvailableTimeblocks(data, dateStr, reservations, guests);
131
+ const availableTimeblocks = getAvailableTimeblocks(data, dateStr, reservations, guests, blockedSlots, selectedGiftcard, isAdmin);
130
132
  console.log(`Found ${Object.keys(availableTimeblocks).length} available timeblocks before table check`);
131
133
 
132
134
  // If no timeblocks are available at all, exit early
@@ -9,11 +9,12 @@ const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
9
9
  * @param {number} guests - The number of guests for the new reservation request.
10
10
  * @param {Array} blockedSlots - Optional array of blocked time slots to exclude.
11
11
  * @param {string|null} giftcard - Optional giftcard to filter times by meal.
12
+ * @param {boolean} isAdmin - Optional flag to bypass time restrictions for admin users.
12
13
  * @returns {boolean} - Returns true if the time is available, false otherwise.
13
14
  */
14
- function isTimeAvailable(data, dateStr, timeStr, reservations, guests, blockedSlots = [], giftcard = null) {
15
+ function isTimeAvailable(data, dateStr, timeStr, reservations, guests, blockedSlots = [], giftcard = null, isAdmin = false) {
15
16
  // Get all available timeblocks for the specified date and guest count
16
- const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard);
17
+ const availableTimeblocks = timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots, giftcard, isAdmin);
17
18
 
18
19
  // Check if the specific timeStr is one of the available keys
19
20
  return Object.prototype.hasOwnProperty.call(availableTimeblocks, timeStr);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happychef/algorithm",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Restaurant and reservation algorithm utilities",
5
5
  "main": "index.js",
6
6
  "author": "happy chef",
@@ -89,7 +89,7 @@ function fitsWithinMeal(data, dateStr, startTimeStr, duurReservatie) {
89
89
  return startTime >= mealStartTime && startTime + duurReservatie <= mealEndTime;
90
90
  }
91
91
 
92
- function timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots = [], giftcard = null) {
92
+ function timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots = [], giftcard = null, isAdmin = false) {
93
93
  const duurReservatie = getDuurReservatie(data);
94
94
  const intervalReservatie = getInterval(data);
95
95
 
@@ -149,8 +149,8 @@ function timeblocksAvailable(data, dateStr, reservations, guests, blockedSlots =
149
149
  }
150
150
  }
151
151
 
152
- // Filter out blocked time slots
153
- if (blockedSlots && blockedSlots.length > 0) {
152
+ // Filter out blocked time slots (skip for admin)
153
+ if (!isAdmin && blockedSlots && blockedSlots.length > 0) {
154
154
  for (const blockedSlot of blockedSlots) {
155
155
  // Check if the blocked slot matches the current date
156
156
  if (blockedSlot.date === dateStr && blockedSlot.time) {
@@ -1,79 +0,0 @@
1
- const safeParseInt = require('../utils/safeParseInt');
2
- const computeRequiredSlots = require('../time/computeRequiredSlots');
3
- const getAllTables = require('../data/getAllTables');
4
- const isTemporaryTableValid = require('../utils/isTemporaryTableValid');
5
- const findMultiTableCombination = require('../availability/findMultiTableCombination');
6
- const isTableFreeForAllSlots = require('../utils/isTableFreeForAllSlots');
7
-
8
- /**
9
- * Attempts to assign tables for a single reservation at a given time.
10
- * Returns an array of table numbers (single-table or multi-table).
11
- */
12
- function assignTablesForGivenTime(restaurantData, date, time, guests, tableOccupiedSlots, selectedZitplaats = null) {
13
-
14
- // FIXED: More robust parsing of settings with detailed logging
15
- const generalSettings = restaurantData["general-settings"] || {};
16
-
17
- // Check what format the data is in
18
- // Check for MongoDB format
19
- if (typeof generalSettings.duurReservatie === 'object' && generalSettings.duurReservatie?.$numberInt) {
20
- }
21
- if (typeof generalSettings.intervalReservatie === 'object' && generalSettings.intervalReservatie?.$numberInt) {
22
- }
23
-
24
- // DEFAULT VALUES - this approach mirrors the TypeScript implementation
25
- let duurReservatie = 120; // Default: 2 hours in minutes
26
- let intervalReservatie = 15; // Default: 15 minute intervals
27
-
28
- // Use safeParseInt for robust parsing
29
- duurReservatie = safeParseInt(generalSettings.duurReservatie, 120);
30
- intervalReservatie = safeParseInt(generalSettings.intervalReservatie, 15);
31
-
32
- if (intervalReservatie <= 0) {
33
- console.error("Invalid interval settings.");
34
- return [];
35
- }
36
-
37
- const requiredSlots = computeRequiredSlots(time, duurReservatie, intervalReservatie);
38
- if (!requiredSlots || requiredSlots.length === 0) {
39
- console.error(`Could not compute required slots for ${time}`);
40
- return [];
41
- }
42
-
43
- // Fetch all tables
44
- const allTables = getAllTables(restaurantData, selectedZitplaats);
45
-
46
- // Try single-table assignment first
47
- for (const t of allTables) {
48
- if (!isTemporaryTableValid(t, date, time)) continue;
49
-
50
- if (t.minCapacity <= guests &&
51
- guests <= t.maxCapacity &&
52
- isTableFreeForAllSlots(t.tableNumber, requiredSlots, tableOccupiedSlots))
53
- {
54
- return [t.tableNumber];
55
- }
56
- }
57
-
58
- // Try multi-table assignment
59
- const best = { minDistance: Infinity, tables: [], tableCount: Infinity };
60
- findMultiTableCombination(
61
- allTables,
62
- guests,
63
- 0,
64
- [],
65
- best,
66
- requiredSlots,
67
- tableOccupiedSlots,
68
- date,
69
- time
70
- );
71
-
72
- if (best.tables.length > 0) {
73
- } else {
74
- }
75
-
76
- return best.tables.map(t => t.tableNumber);
77
- }
78
-
79
- module.exports = assignTablesForGivenTime;
@@ -1,48 +0,0 @@
1
- // Import directly to avoid circular dependency with tables/main.js
2
- const isTimeAvailableSync = require('../availability/isTimeAvailableSync');
3
-
4
- /**
5
- * Filters timeblocks based on availability for a given guest count and date.
6
- */
7
- function filterTimeblocksByTableAvailability(
8
- restaurantData,
9
- date,
10
- timeblocks,
11
- guests,
12
- reservations,
13
- selectedZitplaats = null
14
- ) {
15
- if (guests < 0) {
16
- guests = 0; // Use a reasonable default
17
- }
18
-
19
- const tableSettings = restaurantData?.['table-settings'] || {};
20
- const isTableAssignmentEnabled =
21
- tableSettings.isInstalled === true &&
22
- tableSettings.assignmentMode === 'automatic';
23
-
24
- if (!isTableAssignmentEnabled) {
25
- return timeblocks;
26
- }
27
-
28
- const filteredTimeblocks = {};
29
- let availableCount = 0;
30
- let unavailableCount = 0;
31
-
32
- for (const time in timeblocks) {
33
- if (isTimeAvailableSync(restaurantData, date, time, guests, reservations, selectedZitplaats)) {
34
- filteredTimeblocks[time] = timeblocks[time];
35
- availableCount++;
36
- } else {
37
- unavailableCount++;
38
- }
39
- }
40
-
41
- if (availableCount > 0) {
42
- // no-op, preserve original structure
43
- }
44
-
45
- return filteredTimeblocks;
46
- }
47
-
48
- module.exports = filterTimeblocksByTableAvailability;
@@ -1,100 +0,0 @@
1
- const isTemporaryTableValid = require('../utils/isTemporaryTableValid');
2
- const isTableFreeForAllSlots = require('../utils/isTableFreeForAllSlots');
3
- const calculateDistance = require('../utils/calculateDistance');
4
-
5
- /**
6
- * Backtracking function to find a combination of tables (multi-table assignment).
7
- */
8
- function findMultiTableCombination(
9
- tables,
10
- guestsNeeded,
11
- startIndex,
12
- currentSet,
13
- best,
14
- requiredSlots,
15
- tableOccupiedSlots,
16
- reservationDateStr,
17
- reservationTimeStr
18
- ) {
19
- // Base case: All guests are seated
20
- if (guestsNeeded <= 0) {
21
- // Calculate total distance for the current combination
22
- let distanceSum = 0;
23
- for (let i = 0; i < currentSet.length; i++) {
24
- for (let j = i + 1; j < currentSet.length; j++) {
25
- distanceSum += calculateDistance(currentSet[i], currentSet[j]);
26
- }
27
- }
28
- // Update best solution if this one is better (fewer tables, then lower distance)
29
- if (currentSet.length < best.tableCount || (currentSet.length === best.tableCount && distanceSum < best.minDistance)) {
30
- best.minDistance = distanceSum;
31
- best.tables = [...currentSet]; // Store a copy
32
- best.tableCount = currentSet.length;
33
- }
34
- return;
35
- }
36
-
37
- if (startIndex >= tables.length) {
38
- return;
39
- }
40
-
41
- if (currentSet.length >= best.tableCount && best.tableCount !== Infinity) {
42
- return; // Pruning based on table count
43
- }
44
-
45
- let maxPossibleCapacity = 0;
46
- for (let i = startIndex; i < tables.length; i++) {
47
- const tbl = tables[i];
48
- // Only consider valid & free tables for potential capacity
49
- if (isTemporaryTableValid(tbl, reservationDateStr, reservationTimeStr) &&
50
- isTableFreeForAllSlots(tbl.tableNumber, requiredSlots, tableOccupiedSlots))
51
- {
52
- maxPossibleCapacity += tbl.maxCapacity;
53
- }
54
- }
55
-
56
- if (maxPossibleCapacity < guestsNeeded) {
57
- return; // Impossible to seat remaining guests
58
- }
59
-
60
- for (let i = startIndex; i < tables.length; i++) {
61
- const tbl = tables[i];
62
-
63
- // 1. Check if temporary table is valid for this date/time
64
- if (!isTemporaryTableValid(tbl, reservationDateStr, reservationTimeStr)) {
65
- continue;
66
- }
67
-
68
- // 2. Check if table is free for all required slots
69
- if (!isTableFreeForAllSlots(tbl.tableNumber, requiredSlots, tableOccupiedSlots)) {
70
- continue;
71
- }
72
-
73
- // 3. Check if table contributes meaningfully
74
- const canSeat = Math.min(tbl.maxCapacity, guestsNeeded);
75
- if (canSeat < tbl.minCapacity && canSeat < guestsNeeded) {
76
- continue; // Don't use a table for fewer than its min capacity unless it fulfills the remainder
77
- }
78
-
79
- if (canSeat <= 0) continue; // Table doesn't help
80
-
81
- // Recurse
82
- currentSet.push(tbl);
83
-
84
- findMultiTableCombination(
85
- tables,
86
- guestsNeeded - canSeat,
87
- i + 1,
88
- currentSet,
89
- best,
90
- requiredSlots,
91
- tableOccupiedSlots,
92
- reservationDateStr,
93
- reservationTimeStr
94
- );
95
-
96
- currentSet.pop(); // Backtrack
97
- }
98
- }
99
-
100
- module.exports = findMultiTableCombination;
@@ -1,106 +0,0 @@
1
- const safeParseInt = require('../utils/safeParseInt');
2
- const computeRequiredSlots = require('../time/computeRequiredSlots');
3
- const getAllTables = require('../data/getAllTables');
4
- const isTemporaryTableValid = require('../utils/isTemporaryTableValid');
5
- const getActualTableAssignment = require('../data/getActualTableAssignment');
6
- const assignTablesForGivenTime = require('../assignment/assignTablesForGivenTime');
7
-
8
- /**
9
- * Returns array of available individual table objects for a specific time.
10
- */
11
- function getAvailableTablesForTime(restaurantData, date, time, guests, reservations, selectedZitplaats = null) {
12
- try {
13
- if (guests < 0) {
14
- guests = 0;
15
- }
16
-
17
- const tableSettings = restaurantData?.['table-settings'] || {};
18
- const isAutomaticAssignment = tableSettings.isInstalled === true &&
19
- tableSettings.assignmentMode === "automatic";
20
-
21
- if (!isAutomaticAssignment) {
22
- return [];
23
- }
24
-
25
- if (!restaurantData?.floors || !Array.isArray(restaurantData.floors)) {
26
- return [];
27
- }
28
- if (guests <= 0) return [];
29
-
30
- const generalSettings = restaurantData["general-settings"] || {};
31
- const duurReservatie = safeParseInt(generalSettings.duurReservatie, 120);
32
- const intervalReservatie = safeParseInt(generalSettings.intervalReservatie, 15);
33
-
34
- if (intervalReservatie <= 0) {
35
- return [];
36
- }
37
-
38
- const requiredSlots = computeRequiredSlots(time, duurReservatie, intervalReservatie);
39
- if (!requiredSlots || requiredSlots.length === 0) {
40
- return [];
41
- }
42
-
43
- const tableOccupiedSlots = {};
44
- const reservationsForDate = reservations
45
- .filter(r => r.date === date && r.time && r.guests > 0)
46
- .sort((a, b) => {
47
- const timeA = a.time.split(':').map(Number);
48
- const timeB = b.time.split(':').map(Number);
49
- return (timeA[0] * 60 + timeA[1]) - (timeB[0] * 60 + timeB[1]);
50
- });
51
-
52
- for (const r of reservationsForDate) {
53
- const actualTables = getActualTableAssignment(r);
54
- let assignedTables = [];
55
-
56
- if (actualTables.length > 0) {
57
- assignedTables = actualTables;
58
- } else {
59
- assignedTables = assignTablesForGivenTime(restaurantData, r.date, r.time, r.guests, tableOccupiedSlots, null);
60
- }
61
-
62
- if (assignedTables.length > 0) {
63
- const rSlots = computeRequiredSlots(r.time, duurReservatie, intervalReservatie);
64
- if (!rSlots || rSlots.length === 0) continue;
65
- assignedTables.forEach(tableNumber => {
66
- if (!tableOccupiedSlots[tableNumber]) {
67
- tableOccupiedSlots[tableNumber] = new Set();
68
- }
69
- rSlots.forEach(slot => tableOccupiedSlots[tableNumber].add(slot));
70
- });
71
- }
72
- }
73
-
74
- const allTables = getAllTables(restaurantData, selectedZitplaats);
75
- const availableIndividualTables = [];
76
-
77
- for (const t of allTables) {
78
- if (!isTemporaryTableValid(t, date, time)) continue;
79
- if (t.minCapacity <= guests && guests <= t.maxCapacity) {
80
- if (tableOccupiedSlots && requiredSlots && !Array.isArray(requiredSlots)) {
81
- // (no-op guard, keeps logic consistent)
82
- }
83
- const isFree = (function(){
84
- const occupied = tableOccupiedSlots[t.tableNumber] || new Set();
85
- for (const s of requiredSlots) {
86
- if (occupied.has(s)) return false;
87
- }
88
- return true;
89
- })();
90
-
91
- if (isFree) {
92
- availableIndividualTables.push(t);
93
- }
94
- }
95
- }
96
-
97
- availableIndividualTables.sort((a, b) => a.tableNumber - b.tableNumber);
98
-
99
- return availableIndividualTables;
100
-
101
- } catch (error) {
102
- return []; // Return empty on error
103
- }
104
- }
105
-
106
- module.exports = getAvailableTablesForTime;
@@ -1,128 +0,0 @@
1
- const safeParseInt = require('../utils/safeParseInt');
2
- const computeRequiredSlots = require('../time/computeRequiredSlots');
3
- const isTemporaryTableValid = require('../utils/isTemporaryTableValid');
4
- const getAllTables = require('../data/getAllTables');
5
- const getActualTableAssignment = require('../data/getActualTableAssignment');
6
- const assignTablesForGivenTime = require('../assignment/assignTablesForGivenTime');
7
- const isTableFreeForAllSlots = require('../utils/isTableFreeForAllSlots');
8
- const findMultiTableCombination = require('./findMultiTableCombination');
9
-
10
- /**
11
- * Synchronous availability check using table assignment simulation and/or actual assignments.
12
- */
13
- function isTimeAvailableSync(restaurantData, date, time, guests, reservations, selectedZitplaats = null) {
14
-
15
- if (guests < 0) {
16
- guests = 2; // Use a reasonable default
17
- }
18
-
19
- const tableSettings = restaurantData?.['table-settings'] || {};
20
- const isTableAssignmentEnabled = tableSettings.isInstalled === true &&
21
- tableSettings.assignmentMode === "automatic";
22
-
23
- if (!isTableAssignmentEnabled) {
24
- return true;
25
- }
26
-
27
- try {
28
- // Basic data check
29
- if (!restaurantData?.floors || !Array.isArray(restaurantData.floors)) {
30
- console.error(`[isTimeAvailableSync] Missing floors data for ${date} ${time}`);
31
- return false;
32
- }
33
-
34
- if (guests <= 0) {
35
- console.error(`[isTimeAvailableSync] Invalid guest count: ${guests}`);
36
- return false;
37
- }
38
-
39
- const generalSettings = restaurantData["general-settings"] || {};
40
- let duurReservatie = 120; // Default: 2 hours in minutes
41
- let intervalReservatie = 15; // Default: 15 minute intervals
42
- duurReservatie = safeParseInt(generalSettings.duurReservatie, 120);
43
- intervalReservatie = safeParseInt(generalSettings.intervalReservatie, 15);
44
-
45
- if (intervalReservatie <= 0) {
46
- console.error(`[isTimeAvailableSync] Invalid interval settings for ${date} ${time}`);
47
- return false;
48
- }
49
-
50
- const requiredSlots = computeRequiredSlots(time, duurReservatie, intervalReservatie);
51
- if (!requiredSlots || requiredSlots.length === 0) {
52
- console.error(`[isTimeAvailableSync] Could not compute required slots for ${date} ${time}`);
53
- return false;
54
- }
55
-
56
- // Build Occupancy Map using ACTUAL table assignments when available
57
- const tableOccupiedSlots = {}; // { tableNumber: Set<slotMinutes> }
58
- const reservationsForDate = reservations
59
- .filter(r => r.date === date && r.time && r.guests > 0)
60
- .sort((a, b) => {
61
- const timeA = a.time.split(':').map(Number);
62
- const timeB = b.time.split(':').map(Number);
63
- return (timeA[0] * 60 + timeA[1]) - (timeB[0] * 60 + timeB[1]);
64
- });
65
-
66
- for (const r of reservationsForDate) {
67
- // Try to get actual table assignment first
68
- const actualTables = getActualTableAssignment(r);
69
- let assignedTables = [];
70
-
71
- if (actualTables.length > 0) {
72
- assignedTables = actualTables;
73
- } else {
74
- // Fall back to simulation for backwards compatibility
75
- assignedTables = assignTablesForGivenTime(restaurantData, r.date, r.time, r.guests, tableOccupiedSlots);
76
- }
77
-
78
- // Update the occupancy map based on the actual or simulated assignment
79
- if (assignedTables.length > 0) {
80
- const rSlots = computeRequiredSlots(r.time, duurReservatie, intervalReservatie);
81
- if (!rSlots || rSlots.length === 0) continue;
82
-
83
- assignedTables.forEach(tableNumber => {
84
- if (!tableOccupiedSlots[tableNumber]) {
85
- tableOccupiedSlots[tableNumber] = new Set();
86
- }
87
- rSlots.forEach(slot => tableOccupiedSlots[tableNumber].add(slot));
88
- });
89
- }
90
- }
91
-
92
- const allTables = getAllTables(restaurantData, selectedZitplaats);
93
-
94
- // 1. Try single table assignment
95
- for (const t of allTables) {
96
- if (!isTemporaryTableValid(t, date, time)) continue;
97
-
98
- if (t.minCapacity <= guests &&
99
- guests <= t.maxCapacity &&
100
- isTableFreeForAllSlots(t.tableNumber, requiredSlots, tableOccupiedSlots))
101
- {
102
- return true;
103
- }
104
- }
105
-
106
- // 2. Try multi-table assignment
107
- const best = { minDistance: Infinity, tables: [], tableCount: Infinity };
108
- findMultiTableCombination(
109
- allTables,
110
- guests,
111
- 0, [], best,
112
- requiredSlots,
113
- tableOccupiedSlots,
114
- date, time
115
- );
116
-
117
- const result = best.tables.length > 0;
118
- if (result) {
119
- } else {
120
- }
121
-
122
- return result;
123
- } catch (error) {
124
- return false; // Assume unavailable on error
125
- }
126
- }
127
-
128
- module.exports = isTimeAvailableSync;