@happychef/algorithm 1.4.0 → 1.4.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.
- package/BRANCH_PROTECTION_SETUP.md +167 -167
- package/README.md +144 -144
- package/__tests__/filters.test.js +276 -276
- package/__tests__/isDateAvailable.test.js +175 -179
- package/__tests__/isTimeAvailable.test.js +168 -174
- package/__tests__/restaurantData.test.js +422 -422
- package/__tests__/tableHelpers.test.js +247 -247
- package/assignTables.js +0 -62
- package/bundle_entry.js +100 -0
- package/getAvailableTimeblocks.js +12 -0
- package/index.js +1 -7
- package/jest.config.js +23 -23
- package/moment-timezone-shim.js +179 -0
- package/nul +0 -0
- package/package.json +1 -1
- package/processing/timeblocksAvailable.js +26 -183
- package/reservation_data/counter.js +60 -67
- package/restaurant_data/openinghours.js +23 -0
- package/simulateTableAssignment.js +2 -108
- package/tableHelpers.js +5 -2
- package/test-detailed-filter.js +100 -100
- package/test-lunch-debug.js +110 -110
- package/test-max-arrivals-filter.js +79 -79
- package/test-timezone-debug.js +47 -47
- package/.claude/settings.local.json +0 -16
- package/__tests__/crossMidnight.test.js +0 -63
- package/__tests__/crossMidnightTimeblocks.test.js +0 -312
- package/__tests__/edgeCases.test.js +0 -271
- package/changes/2026/February/PR16_add__.md +0 -20
- package/changes/2026/February/PR16_add_getDateClosingReasons.md +0 -31
- package/dateHelpers.js +0 -31
- package/getDateClosingReasons.js +0 -193
|
@@ -1,82 +1,75 @@
|
|
|
1
1
|
// counter.js
|
|
2
2
|
|
|
3
|
-
const { getPrevDateStr } = require('../dateHelpers');
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Parses a time string in "HH:MM" format into minutes since midnight.
|
|
7
5
|
* @param {string} timeStr - The time string in "HH:MM" format.
|
|
8
6
|
* @returns {number} The time in minutes since midnight.
|
|
9
7
|
*/
|
|
10
8
|
function parseTime(timeStr) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function getDuurReservatie(data) {
|
|
16
|
-
let duurReservatie = 120;
|
|
17
|
-
if (
|
|
18
|
-
data['general-settings'] &&
|
|
19
|
-
data['general-settings'].duurReservatie &&
|
|
20
|
-
parseInt(data['general-settings'].duurReservatie, 10) > 0
|
|
21
|
-
) {
|
|
22
|
-
duurReservatie = parseInt(data['general-settings'].duurReservatie, 10);
|
|
9
|
+
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
10
|
+
return hours * 60 + minutes;
|
|
23
11
|
}
|
|
24
12
|
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
function getDuurReservatie(data) {
|
|
14
|
+
let duurReservatie = 120;
|
|
15
|
+
if (
|
|
16
|
+
data['general-settings'] &&
|
|
17
|
+
data['general-settings'].duurReservatie &&
|
|
18
|
+
parseInt(data['general-settings'].duurReservatie, 10) > 0
|
|
19
|
+
) {
|
|
20
|
+
duurReservatie = parseInt(data['general-settings'].duurReservatie, 10);
|
|
21
|
+
}
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
* Parses a reservation's duration, falling back to the default.
|
|
30
|
-
*/
|
|
31
|
-
function getReservationDuration(reservation, defaultDuration) {
|
|
32
|
-
if (reservation.duration) {
|
|
33
|
-
const rawDur = reservation.duration.$numberInt ?? reservation.duration;
|
|
34
|
-
const parsed = parseInt(rawDur, 10);
|
|
35
|
-
if (!isNaN(parsed) && parsed > 0) {
|
|
36
|
-
return parsed;
|
|
37
|
-
}
|
|
23
|
+
return duurReservatie;
|
|
38
24
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Calculates the total number of guests for reservations that cover a specific hour on a specific date.
|
|
28
|
+
* @param {Object} data - The main data object containing general settings.
|
|
29
|
+
* @param {Array} reservations - An array of reservation objects.
|
|
30
|
+
* @param {string} hour - The hour to check in "HH:MM" format.
|
|
31
|
+
* @param {string} dateStr - The date string in "YYYY-MM-DD" format.
|
|
32
|
+
* @returns {number} The total number of guests for that hour on the specified date.
|
|
33
|
+
*/
|
|
34
|
+
function getGuestCountAtHour(data, reservations, hour, dateStr) {
|
|
35
|
+
// Get 'duurReservatie' from general settings, default to 120 if not set or zero
|
|
36
|
+
let duurReservatie = getDuurReservatie(data)
|
|
37
|
+
|
|
38
|
+
// Convert the target hour to minutes since midnight
|
|
39
|
+
const targetTime = parseTime(hour);
|
|
40
|
+
|
|
41
|
+
let totalGuests = 0;
|
|
42
|
+
|
|
43
|
+
for (const reservation of reservations) {
|
|
44
|
+
// Only consider reservations on the specified date
|
|
45
|
+
if (reservation.date !== dateStr) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const startTime = parseTime(reservation.time);
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
// Use reservation specific duration if available, else default
|
|
52
|
+
let rDuration = duurReservatie;
|
|
53
|
+
if (reservation.duration) {
|
|
54
|
+
const rawDur = reservation.duration.$numberInt ?? reservation.duration;
|
|
55
|
+
const parsed = parseInt(rawDur, 10);
|
|
56
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
57
|
+
rDuration = parsed;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
totalGuests += parseInt(reservation.guests, 10);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
61
|
+
const endTime = startTime + rDuration;
|
|
62
|
+
// Check if the target time is within the reservation time range
|
|
63
|
+
// Start time is inclusive, end time is exclusive
|
|
64
|
+
if (targetTime >= startTime && targetTime < endTime) {
|
|
65
|
+
totalGuests += parseInt(reservation.guests, 10);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return totalGuests;
|
|
75
70
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
getGuestCountAtHour,
|
|
82
|
-
};
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
getGuestCountAtHour,
|
|
74
|
+
};
|
|
75
|
+
|
|
@@ -38,6 +38,11 @@ function getMealTypeByTime(timeStr) {
|
|
|
38
38
|
return mealType;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
// Times at or past dinner end are considered dinner (cross-midnight support for bowling)
|
|
42
|
+
const dinnerEnd = parseTime(shifts.dinner.end);
|
|
43
|
+
if (time >= dinnerEnd) {
|
|
44
|
+
return 'dinner';
|
|
45
|
+
}
|
|
41
46
|
return null;
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -104,6 +109,24 @@ function getDataByDateAndMeal(data, dateStr, mealType) {
|
|
|
104
109
|
|
|
105
110
|
adjustMealData(mealData, data['general-settings']);
|
|
106
111
|
|
|
112
|
+
// Extend dinner end time past midnight for bowling venues
|
|
113
|
+
const hoursOpenedPastMidnight = Number(mealData.hoursOpenedPastMidnight) || 0;
|
|
114
|
+
if (hoursOpenedPastMidnight > 0 && mealType === 'dinner') {
|
|
115
|
+
const newEndMinutes = 1440 + (hoursOpenedPastMidnight * 60); // midnight + hours
|
|
116
|
+
const hours = String(Math.floor(newEndMinutes / 60)).padStart(2, '0');
|
|
117
|
+
const minutes = String(newEndMinutes % 60).padStart(2, '0');
|
|
118
|
+
mealData.endTime = `${hours}:${minutes}`;
|
|
119
|
+
} else if (mealType === 'dinner') {
|
|
120
|
+
// For bowling accounts, ensure dinner extends to at least 24:00 (midnight)
|
|
121
|
+
const isBowling = data?.account_type === 'bowling' || data?.accountType === 'bowling' || data?.['general-settings']?.account_type === 'bowling';
|
|
122
|
+
if (isBowling) {
|
|
123
|
+
const currentEndMinutes = parseTime(mealData.endTime);
|
|
124
|
+
if (currentEndMinutes < 1440) {
|
|
125
|
+
mealData.endTime = '24:00';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
107
130
|
// Add duurReservatie to endTime
|
|
108
131
|
addDuurReservatieToEndTime(mealData, data);
|
|
109
132
|
|
|
@@ -8,7 +8,6 @@ const {
|
|
|
8
8
|
getAllTables,
|
|
9
9
|
isTemporaryTableValid,
|
|
10
10
|
} = require('./tableHelpers');
|
|
11
|
-
const { getNextDateStr, getPrevDateStr } = require('./dateHelpers');
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -84,7 +83,8 @@ function getActualTableAssignment(reservation) {
|
|
|
84
83
|
* Converts a reservation's start time and duration into discrete time slots (in minutes from midnight).
|
|
85
84
|
*/
|
|
86
85
|
function computeRequiredSlots(timeString, durationMinutes, intervalMinutes) {
|
|
87
|
-
|
|
86
|
+
// Support cross-midnight times (e.g., "24:00", "25:00") for bowling venues
|
|
87
|
+
const timePattern = /^([0-2]\d):([0-5]\d)$/;
|
|
88
88
|
if (!timeString || !timePattern.test(timeString)) {
|
|
89
89
|
console.error(`Invalid time format for computeRequiredSlots: ${timeString}. Expected 'HH:MM'.`);
|
|
90
90
|
return [];
|
|
@@ -524,59 +524,6 @@ function isTimeAvailableSync(restaurantData, date, time, guests, reservations, s
|
|
|
524
524
|
}
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
-
// Cross-date: include previous-day reservations that spill past midnight
|
|
528
|
-
const prevDateStr = getPrevDateStr(date);
|
|
529
|
-
const prevDayReservations = reservations
|
|
530
|
-
.filter(r => r.date === prevDateStr && r.time && r.guests > 0);
|
|
531
|
-
|
|
532
|
-
for (const r of prevDayReservations) {
|
|
533
|
-
const rDuration = safeParseInt(r.duration, defaultDuurReservatie);
|
|
534
|
-
const startMinutes = r.time.split(':').map(Number);
|
|
535
|
-
const startMin = startMinutes[0] * 60 + startMinutes[1];
|
|
536
|
-
if (startMin + rDuration > 1440) {
|
|
537
|
-
const actualTables = getActualTableAssignment(r);
|
|
538
|
-
let assignedTables = actualTables.length > 0 ? actualTables :
|
|
539
|
-
assignTablesForGivenTime(restaurantData, r.date, r.time, r.guests, tableOccupiedSlots, r.zitplaats || null, rDuration);
|
|
540
|
-
if (assignedTables.length > 0) {
|
|
541
|
-
const rSlots = computeRequiredSlots(r.time, rDuration, intervalReservatie);
|
|
542
|
-
if (rSlots && rSlots.length > 0) {
|
|
543
|
-
assignedTables.forEach(tableNumber => {
|
|
544
|
-
if (!tableOccupiedSlots[tableNumber]) {
|
|
545
|
-
tableOccupiedSlots[tableNumber] = new Set();
|
|
546
|
-
}
|
|
547
|
-
rSlots.forEach(slot => tableOccupiedSlots[tableNumber].add(slot));
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// Cross-date: if new reservation crosses midnight, include next-day reservations offset by +1440
|
|
555
|
-
const crossesMidnight = requiredSlots.some(s => s >= 1440);
|
|
556
|
-
if (crossesMidnight) {
|
|
557
|
-
const nextDateStr = getNextDateStr(date);
|
|
558
|
-
const nextDayReservations = reservations
|
|
559
|
-
.filter(r => r.date === nextDateStr && r.time && r.guests > 0);
|
|
560
|
-
|
|
561
|
-
for (const r of nextDayReservations) {
|
|
562
|
-
const rDuration = safeParseInt(r.duration, defaultDuurReservatie);
|
|
563
|
-
const actualTables = getActualTableAssignment(r);
|
|
564
|
-
let assignedTables = actualTables.length > 0 ? actualTables :
|
|
565
|
-
assignTablesForGivenTime(restaurantData, r.date, r.time, r.guests, tableOccupiedSlots, r.zitplaats || null, rDuration);
|
|
566
|
-
if (assignedTables.length > 0) {
|
|
567
|
-
const rSlots = computeRequiredSlots(r.time, rDuration, intervalReservatie);
|
|
568
|
-
if (rSlots && rSlots.length > 0) {
|
|
569
|
-
assignedTables.forEach(tableNumber => {
|
|
570
|
-
if (!tableOccupiedSlots[tableNumber]) {
|
|
571
|
-
tableOccupiedSlots[tableNumber] = new Set();
|
|
572
|
-
}
|
|
573
|
-
rSlots.forEach(slot => tableOccupiedSlots[tableNumber].add(slot + 1440));
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
527
|
console.log(`[isTimeAvailableSync] Occupancy map built, checking availability for new reservation`);
|
|
581
528
|
|
|
582
529
|
const allTables = getAllTables(restaurantData, selectedZitplaats); // Get all tables
|
|
@@ -747,59 +694,6 @@ function getAvailableTablesForTime(restaurantData, date, time, guests, reservati
|
|
|
747
694
|
});
|
|
748
695
|
}
|
|
749
696
|
}
|
|
750
|
-
// Cross-date: include previous-day reservations that spill past midnight
|
|
751
|
-
const prevDateStr = getPrevDateStr(date);
|
|
752
|
-
const prevDayReservationsGAT = reservations
|
|
753
|
-
.filter(r => r.date === prevDateStr && r.time && r.guests > 0);
|
|
754
|
-
|
|
755
|
-
for (const r of prevDayReservationsGAT) {
|
|
756
|
-
const rDuration = safeParseInt(r.duration, duurReservatie);
|
|
757
|
-
const startMinutes = r.time.split(':').map(Number);
|
|
758
|
-
const startMin = startMinutes[0] * 60 + startMinutes[1];
|
|
759
|
-
if (startMin + rDuration > 1440) {
|
|
760
|
-
const actualTables = getActualTableAssignment(r);
|
|
761
|
-
let assignedTables = actualTables.length > 0 ? actualTables :
|
|
762
|
-
assignTablesForGivenTime(restaurantData, r.date, r.time, r.guests, tableOccupiedSlots, r.zitplaats || null, rDuration);
|
|
763
|
-
if (assignedTables.length > 0) {
|
|
764
|
-
const rSlots = computeRequiredSlots(r.time, rDuration, intervalReservatie);
|
|
765
|
-
if (rSlots && rSlots.length > 0) {
|
|
766
|
-
assignedTables.forEach(tableNumber => {
|
|
767
|
-
if (!tableOccupiedSlots[tableNumber]) {
|
|
768
|
-
tableOccupiedSlots[tableNumber] = new Set();
|
|
769
|
-
}
|
|
770
|
-
rSlots.forEach(slot => tableOccupiedSlots[tableNumber].add(slot));
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// Cross-date: if new reservation crosses midnight, include next-day reservations offset by +1440
|
|
778
|
-
const crossesMidnightGAT = requiredSlots.some(s => s >= 1440);
|
|
779
|
-
if (crossesMidnightGAT) {
|
|
780
|
-
const nextDateStr = getNextDateStr(date);
|
|
781
|
-
const nextDayReservationsGAT = reservations
|
|
782
|
-
.filter(r => r.date === nextDateStr && r.time && r.guests > 0);
|
|
783
|
-
|
|
784
|
-
for (const r of nextDayReservationsGAT) {
|
|
785
|
-
const rDuration = safeParseInt(r.duration, duurReservatie);
|
|
786
|
-
const actualTables = getActualTableAssignment(r);
|
|
787
|
-
let assignedTables = actualTables.length > 0 ? actualTables :
|
|
788
|
-
assignTablesForGivenTime(restaurantData, r.date, r.time, r.guests, tableOccupiedSlots, r.zitplaats || null, rDuration);
|
|
789
|
-
if (assignedTables.length > 0) {
|
|
790
|
-
const rSlots = computeRequiredSlots(r.time, rDuration, intervalReservatie);
|
|
791
|
-
if (rSlots && rSlots.length > 0) {
|
|
792
|
-
assignedTables.forEach(tableNumber => {
|
|
793
|
-
if (!tableOccupiedSlots[tableNumber]) {
|
|
794
|
-
tableOccupiedSlots[tableNumber] = new Set();
|
|
795
|
-
}
|
|
796
|
-
rSlots.forEach(slot => tableOccupiedSlots[tableNumber].add(slot + 1440));
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
697
|
console.log("[getAvailableTablesForTime] Occupancy map built");
|
|
804
698
|
const allTables = getAllTables(restaurantData, selectedZitplaats); // Get all tables with zitplaats filter applied
|
|
805
699
|
const availableIndividualTables = [];
|
package/tableHelpers.js
CHANGED
|
@@ -12,7 +12,7 @@ function parseTime(timeStr) {
|
|
|
12
12
|
if (parts.length !== 2) return NaN;
|
|
13
13
|
const hours = parseInt(parts[0], 10);
|
|
14
14
|
const minutes = parseInt(parts[1], 10);
|
|
15
|
-
if (isNaN(hours) || isNaN(minutes) || hours < 0 ||
|
|
15
|
+
if (isNaN(hours) || isNaN(minutes) || hours < 0 || minutes < 0 || minutes > 59) {
|
|
16
16
|
return NaN;
|
|
17
17
|
}
|
|
18
18
|
return hours * 60 + minutes;
|
|
@@ -43,7 +43,10 @@ function getMealTypeByTime(timeStr) {
|
|
|
43
43
|
return mealType;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
// Times at or past dinner end are considered dinner (cross-midnight support for bowling)
|
|
47
|
+
const dinnerEnd = parseTime(shifts.dinner.end);
|
|
48
|
+
if (!isNaN(dinnerEnd) && time >= dinnerEnd) return 'dinner';
|
|
49
|
+
return null;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
|
package/test-detailed-filter.js
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
// Detailed filter test - simulating getAvailableTimeblocks logic
|
|
2
|
-
const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
|
|
3
|
-
const { getMealTypeByTime, parseTime } = require('./tableHelpers');
|
|
4
|
-
|
|
5
|
-
const restaurantData = {
|
|
6
|
-
"_id": "demo",
|
|
7
|
-
"openinghours-lunch": {
|
|
8
|
-
"schemeSettings": {
|
|
9
|
-
"Monday": {
|
|
10
|
-
"enabled": true,
|
|
11
|
-
"startTime": "14:15",
|
|
12
|
-
"endTime": "15:00",
|
|
13
|
-
"maxCapacityEnabled": true,
|
|
14
|
-
"maxCapacity": "10"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"general-settings": {
|
|
19
|
-
"zitplaatsen": 13,
|
|
20
|
-
"uurOpVoorhand": 0,
|
|
21
|
-
"dagenInToekomst": 365,
|
|
22
|
-
"intervalReservatie": 30,
|
|
23
|
-
"duurReservatie": 60,
|
|
24
|
-
"ontbijtStop": "",
|
|
25
|
-
"lunchStop": "",
|
|
26
|
-
"dinerStop": ""
|
|
27
|
-
},
|
|
28
|
-
"max-arrivals-lunch": {
|
|
29
|
-
"14:15": 10,
|
|
30
|
-
"14:30": 10,
|
|
31
|
-
"14:45": 10
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const testDate = "2025-12-08";
|
|
36
|
-
const guests = 2;
|
|
37
|
-
const reservations = [];
|
|
38
|
-
const uurOpVoorhand = 0;
|
|
39
|
-
const isAdmin = false;
|
|
40
|
-
|
|
41
|
-
console.log('=== DETAILED FILTER TEST ===\n');
|
|
42
|
-
|
|
43
|
-
// Get timeblocks
|
|
44
|
-
const availableTimeblocks = timeblocksAvailable(restaurantData, testDate, reservations, guests, [], null, isAdmin);
|
|
45
|
-
console.log('Initial timeblocks:', Object.keys(availableTimeblocks));
|
|
46
|
-
|
|
47
|
-
// Simulate the time filtering logic
|
|
48
|
-
const now = new Date();
|
|
49
|
-
const [year, month, day] = testDate.split('-').map(Number);
|
|
50
|
-
const targetDateInTimeZone = new Date(year, month - 1, day);
|
|
51
|
-
const isToday = now.toDateString() === targetDateInTimeZone.toDateString();
|
|
52
|
-
|
|
53
|
-
console.log('\nTime filter check:');
|
|
54
|
-
console.log('Is today:', isToday);
|
|
55
|
-
console.log('uurOpVoorhand:', uurOpVoorhand);
|
|
56
|
-
console.log('Current time:', now.toString());
|
|
57
|
-
|
|
58
|
-
if (isToday && uurOpVoorhand >= 0) {
|
|
59
|
-
const cutoffTime = new Date(now.getTime());
|
|
60
|
-
cutoffTime.setHours(cutoffTime.getHours() + uurOpVoorhand);
|
|
61
|
-
console.log('Cutoff time:', cutoffTime.toString());
|
|
62
|
-
|
|
63
|
-
for (const key of Object.keys(availableTimeblocks)) {
|
|
64
|
-
const timeStr = key;
|
|
65
|
-
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
66
|
-
const timeBlockDateTime = new Date(year, month - 1, day, hours, minutes);
|
|
67
|
-
|
|
68
|
-
console.log(`\n Checking ${timeStr}:`);
|
|
69
|
-
console.log(` timeBlockDateTime: ${timeBlockDateTime.toString()}`);
|
|
70
|
-
console.log(` cutoffTime: ${cutoffTime.toString()}`);
|
|
71
|
-
console.log(` timeBlockDateTime < cutoffTime: ${timeBlockDateTime < cutoffTime}`);
|
|
72
|
-
console.log(` Would be deleted: ${timeBlockDateTime < cutoffTime ? 'YES' : 'NO'}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Check lunchStop filter
|
|
77
|
-
console.log('\n\nLunch Stop filter check:');
|
|
78
|
-
const settings = restaurantData['general-settings'];
|
|
79
|
-
const lunchStop = settings.lunchStop || null;
|
|
80
|
-
console.log('lunchStop value:', JSON.stringify(lunchStop));
|
|
81
|
-
console.log('lunchStop truthy:', !!lunchStop);
|
|
82
|
-
|
|
83
|
-
if (lunchStop) {
|
|
84
|
-
console.log('LunchStop is set, would apply filter');
|
|
85
|
-
for (const time in availableTimeblocks) {
|
|
86
|
-
const mealType = getMealTypeByTime(time);
|
|
87
|
-
console.log(` ${time}: mealType=${mealType}`);
|
|
88
|
-
|
|
89
|
-
if (mealType === 'lunch') {
|
|
90
|
-
const timeMinutes = parseTime(time);
|
|
91
|
-
const stopMinutes = parseTime(lunchStop);
|
|
92
|
-
console.log(` timeMinutes=${timeMinutes}, stopMinutes=${stopMinutes}`);
|
|
93
|
-
console.log(` Would remove: ${timeMinutes >= stopMinutes ? 'YES' : 'NO'}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
} else {
|
|
97
|
-
console.log('LunchStop is NOT set, no filter applied');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
console.log('\n=== END TEST ===');
|
|
1
|
+
// Detailed filter test - simulating getAvailableTimeblocks logic
|
|
2
|
+
const { timeblocksAvailable } = require('./processing/timeblocksAvailable');
|
|
3
|
+
const { getMealTypeByTime, parseTime } = require('./tableHelpers');
|
|
4
|
+
|
|
5
|
+
const restaurantData = {
|
|
6
|
+
"_id": "demo",
|
|
7
|
+
"openinghours-lunch": {
|
|
8
|
+
"schemeSettings": {
|
|
9
|
+
"Monday": {
|
|
10
|
+
"enabled": true,
|
|
11
|
+
"startTime": "14:15",
|
|
12
|
+
"endTime": "15:00",
|
|
13
|
+
"maxCapacityEnabled": true,
|
|
14
|
+
"maxCapacity": "10"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"general-settings": {
|
|
19
|
+
"zitplaatsen": 13,
|
|
20
|
+
"uurOpVoorhand": 0,
|
|
21
|
+
"dagenInToekomst": 365,
|
|
22
|
+
"intervalReservatie": 30,
|
|
23
|
+
"duurReservatie": 60,
|
|
24
|
+
"ontbijtStop": "",
|
|
25
|
+
"lunchStop": "",
|
|
26
|
+
"dinerStop": ""
|
|
27
|
+
},
|
|
28
|
+
"max-arrivals-lunch": {
|
|
29
|
+
"14:15": 10,
|
|
30
|
+
"14:30": 10,
|
|
31
|
+
"14:45": 10
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const testDate = "2025-12-08";
|
|
36
|
+
const guests = 2;
|
|
37
|
+
const reservations = [];
|
|
38
|
+
const uurOpVoorhand = 0;
|
|
39
|
+
const isAdmin = false;
|
|
40
|
+
|
|
41
|
+
console.log('=== DETAILED FILTER TEST ===\n');
|
|
42
|
+
|
|
43
|
+
// Get timeblocks
|
|
44
|
+
const availableTimeblocks = timeblocksAvailable(restaurantData, testDate, reservations, guests, [], null, isAdmin);
|
|
45
|
+
console.log('Initial timeblocks:', Object.keys(availableTimeblocks));
|
|
46
|
+
|
|
47
|
+
// Simulate the time filtering logic
|
|
48
|
+
const now = new Date();
|
|
49
|
+
const [year, month, day] = testDate.split('-').map(Number);
|
|
50
|
+
const targetDateInTimeZone = new Date(year, month - 1, day);
|
|
51
|
+
const isToday = now.toDateString() === targetDateInTimeZone.toDateString();
|
|
52
|
+
|
|
53
|
+
console.log('\nTime filter check:');
|
|
54
|
+
console.log('Is today:', isToday);
|
|
55
|
+
console.log('uurOpVoorhand:', uurOpVoorhand);
|
|
56
|
+
console.log('Current time:', now.toString());
|
|
57
|
+
|
|
58
|
+
if (isToday && uurOpVoorhand >= 0) {
|
|
59
|
+
const cutoffTime = new Date(now.getTime());
|
|
60
|
+
cutoffTime.setHours(cutoffTime.getHours() + uurOpVoorhand);
|
|
61
|
+
console.log('Cutoff time:', cutoffTime.toString());
|
|
62
|
+
|
|
63
|
+
for (const key of Object.keys(availableTimeblocks)) {
|
|
64
|
+
const timeStr = key;
|
|
65
|
+
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
66
|
+
const timeBlockDateTime = new Date(year, month - 1, day, hours, minutes);
|
|
67
|
+
|
|
68
|
+
console.log(`\n Checking ${timeStr}:`);
|
|
69
|
+
console.log(` timeBlockDateTime: ${timeBlockDateTime.toString()}`);
|
|
70
|
+
console.log(` cutoffTime: ${cutoffTime.toString()}`);
|
|
71
|
+
console.log(` timeBlockDateTime < cutoffTime: ${timeBlockDateTime < cutoffTime}`);
|
|
72
|
+
console.log(` Would be deleted: ${timeBlockDateTime < cutoffTime ? 'YES' : 'NO'}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check lunchStop filter
|
|
77
|
+
console.log('\n\nLunch Stop filter check:');
|
|
78
|
+
const settings = restaurantData['general-settings'];
|
|
79
|
+
const lunchStop = settings.lunchStop || null;
|
|
80
|
+
console.log('lunchStop value:', JSON.stringify(lunchStop));
|
|
81
|
+
console.log('lunchStop truthy:', !!lunchStop);
|
|
82
|
+
|
|
83
|
+
if (lunchStop) {
|
|
84
|
+
console.log('LunchStop is set, would apply filter');
|
|
85
|
+
for (const time in availableTimeblocks) {
|
|
86
|
+
const mealType = getMealTypeByTime(time);
|
|
87
|
+
console.log(` ${time}: mealType=${mealType}`);
|
|
88
|
+
|
|
89
|
+
if (mealType === 'lunch') {
|
|
90
|
+
const timeMinutes = parseTime(time);
|
|
91
|
+
const stopMinutes = parseTime(lunchStop);
|
|
92
|
+
console.log(` timeMinutes=${timeMinutes}, stopMinutes=${stopMinutes}`);
|
|
93
|
+
console.log(` Would remove: ${timeMinutes >= stopMinutes ? 'YES' : 'NO'}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
console.log('LunchStop is NOT set, no filter applied');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log('\n=== END TEST ===');
|