@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.
- package/RESERVERINGEN_GIDS.md +986 -0
- package/getAvailableTimeblocks.js +7 -6
- package/index.js +7 -27
- package/isDateAvailable.js +5 -4
- package/isDateAvailableWithTableCheck.js +5 -3
- package/isTimeAvailable.js +3 -2
- package/package.json +1 -1
- package/processing/timeblocksAvailable.js +3 -3
- package/tables/assignment/assignTablesForGivenTime.js +0 -79
- package/tables/assignment/filterTimeblocksByTableAvailability.js +0 -48
- package/tables/availability/findMultiTableCombination.js +0 -100
- package/tables/availability/getAvailableTablesForTime.js +0 -106
- package/tables/availability/isTimeAvailableSync.js +0 -128
- package/tables/data/extractTableNumbers.js +0 -17
- package/tables/data/getActualTableAssignment.js +0 -16
- package/tables/data/getAllTables.js +0 -84
- package/tables/main.js +0 -33
- package/tables/time/computeRequiredSlots.js +0 -27
- package/tables/time/getMealTypeByTime.js +0 -26
- package/tables/utils/safeParseInt.js +0 -29
|
@@ -0,0 +1,986 @@
|
|
|
1
|
+
# Handleiding Reserveringssysteem HappyChef
|
|
2
|
+
|
|
3
|
+
## Inhoudsopgave
|
|
4
|
+
|
|
5
|
+
1. [Overzicht van het Systeem](#overzicht-van-het-systeem)
|
|
6
|
+
2. [Hoe Beschikbaarheid Wordt Berekend](#hoe-beschikbaarheid-wordt-berekend)
|
|
7
|
+
3. [Datamodellen en Configuratie](#datamodellen-en-configuratie)
|
|
8
|
+
4. [Boekingsfilters en Validatie](#boekingsfilters-en-validatie)
|
|
9
|
+
5. [Tafeltoewijzing](#tafeltoewijzing)
|
|
10
|
+
6. [Volledige Reserveringsflow](#volledige-reserveringsflow)
|
|
11
|
+
7. [Belangrijke Validatieregels](#belangrijke-validatieregels)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Overzicht van het Systeem
|
|
16
|
+
|
|
17
|
+
Het HappyChef reserveringssysteem is een gelaagd algoritme voor het berekenen van restaurantbeschikbaarheid en het toewijzen van tafels aan reserveringen. Het systeem bestaat uit de volgende kerncomponenten:
|
|
18
|
+
|
|
19
|
+
### Bestandsstructuur
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
├── Core Beschikbaarheidslogica
|
|
23
|
+
│ ├── isDateAvailable.js - Controleert of een datum beschikbaar is
|
|
24
|
+
│ ├── isTimeAvailable.js - Controleert of een tijdstip beschikbaar is
|
|
25
|
+
│ └── getAvailableTimeblocks.js - Haalt beschikbare tijdsblokken op
|
|
26
|
+
│
|
|
27
|
+
├── Processing Engines (processing/)
|
|
28
|
+
│ ├── timeblocksAvailable.js - Kernmotor voor beschikbaarheid
|
|
29
|
+
│ ├── dailyGuestCounts.js - Berekent dagelijkse gastentellingen
|
|
30
|
+
│ └── mealTypeCount.js - Berekent gasten per maaltijdtype
|
|
31
|
+
│
|
|
32
|
+
├── Datamodellen (restaurant_data/)
|
|
33
|
+
│ ├── openinghours.js - Openingstijden configuratie
|
|
34
|
+
│ ├── exceptions.js - Uitzonderingen en sluitingen
|
|
35
|
+
│ └── counter.js - Gastentelling functies
|
|
36
|
+
│
|
|
37
|
+
├── Boekingsfilters (filters/)
|
|
38
|
+
│ ├── timeFilter.js - Tijdsfilter
|
|
39
|
+
│ ├── maxArrivalsFilter.js - Maximum aankomsten filter
|
|
40
|
+
│ └── maxGroupsFilter.js - Maximum groepen filter
|
|
41
|
+
│
|
|
42
|
+
└── Tafelbeheer
|
|
43
|
+
├── assignTables.js - Tafeltoewijzing logica
|
|
44
|
+
├── grouping.js - Tafelgroepering
|
|
45
|
+
└── tableHelpers.js - Hulpfuncties voor tafels
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Hoe Beschikbaarheid Wordt Berekend
|
|
51
|
+
|
|
52
|
+
De beschikbaarheidberekening gebeurt in meerdere lagen, van datum tot specifiek tijdstip.
|
|
53
|
+
|
|
54
|
+
### 1. Beschikbaarheid op Datumniveau (`isDateAvailable.js`)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
isDateAvailable(data, dateStr, reservations, guests)
|
|
58
|
+
↓
|
|
59
|
+
Stap 1: Controleer of datum binnen toegestane toekomstbereik ligt (dagenInToekomst: standaard 90 dagen)
|
|
60
|
+
Stap 2: Roep getAvailableTimeblocks() aan om beschikbare tijdsslots te vinden
|
|
61
|
+
Stap 3: Geef TRUE terug als er minstens ÉÉN tijdsblok beschikbaar is
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Belangrijke Instellingen:**
|
|
65
|
+
- `dagenInToekomst`: Maximum aantal dagen in de toekomst (standaard: 90)
|
|
66
|
+
- `uurOpVoorhand`: Aantal uren van tevoren vereist om te boeken (standaard: 4)
|
|
67
|
+
- Tijdzone: Europe/Amsterdam
|
|
68
|
+
|
|
69
|
+
### 2. Tijdsblok Beschikbaarheid (`getAvailableTimeblocks.js`)
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
getAvailableTimeblocks(data, dateStr, reservations, guests)
|
|
73
|
+
↓
|
|
74
|
+
Stap 1: Valideer dat datum binnen dagenInToekomst bereik ligt
|
|
75
|
+
Stap 2: Voor VANDAAG: Verwijder slots binnen uurOpVoorhand venster
|
|
76
|
+
Stap 3: Roep timeblocksAvailable() aan voor alle potentiële slots
|
|
77
|
+
Stap 4: Geef gefilterd tijdsblok object terug met tijden en beschikbaarheid
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Voorbeeld output:**
|
|
81
|
+
```javascript
|
|
82
|
+
{
|
|
83
|
+
"12:00": true, // Beschikbaar
|
|
84
|
+
"12:15": true,
|
|
85
|
+
"12:30": false, // Niet beschikbaar
|
|
86
|
+
"12:45": true
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3. Kern Beschikbaarheidsmotor (`processing/timeblocksAvailable.js`)
|
|
91
|
+
|
|
92
|
+
Dit is het hart van het systeem en werkt in twee modi:
|
|
93
|
+
|
|
94
|
+
#### **A. SHIFTS MODUS** (indien ingeschakeld)
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
timeblocksAvailable(data, dateStr, reservations, guests)
|
|
98
|
+
↓
|
|
99
|
+
Stap 1: Controleer maaltijdtypes met shifts ingeschakeld
|
|
100
|
+
Stap 2: Voor elke shift tijd:
|
|
101
|
+
- Tel beschikbare stoelen (maxCapacity - huidigeGasten)
|
|
102
|
+
Stap 3: Geef beschikbare shifts terug waar: beschikbareStoelen >= gasten
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Voorbeeld:**
|
|
106
|
+
```javascript
|
|
107
|
+
// Shift configuratie
|
|
108
|
+
shifts: [
|
|
109
|
+
{ name: "Eerste dienst", time: "12:00" },
|
|
110
|
+
{ name: "Tweede dienst", time: "14:00" }
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
// Berekening voor 4 gasten
|
|
114
|
+
Shift "12:00": maxCapacity (30) - bezet (25) = 5 beschikbaar → ✓ BESCHIKBAAR
|
|
115
|
+
Shift "14:00": maxCapacity (30) - bezet (28) = 2 beschikbaar → ✗ NIET BESCHIKBAAR
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### **B. TIJDSINTERVAL MODUS** (standaard)
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
timeblocksAvailable(data, dateStr, reservations, guests)
|
|
122
|
+
↓
|
|
123
|
+
Stap 1: Genereer alle tijdslots binnen maaltijdperiodes met regelmatige intervallen
|
|
124
|
+
(intervalReservatie: standaard 15 minuten)
|
|
125
|
+
|
|
126
|
+
Stap 2: Voor elk interval:
|
|
127
|
+
- Tel beschikbare stoelen met getDailyGuestCounts()
|
|
128
|
+
- Controleer of er voldoende opeenvolgende slots bestaan voor volledige reserveringsduur
|
|
129
|
+
(duurReservatie: standaard 120 minuten)
|
|
130
|
+
|
|
131
|
+
Stap 3: Valideer dat starttijd + duur binnen maaltijdtijdvenster past
|
|
132
|
+
|
|
133
|
+
Stap 4: Geef beschikbare tijdsblok objecten terug
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Voorbeeld berekening voor 4 gasten om 12:00:**
|
|
137
|
+
```
|
|
138
|
+
Reserveringsduur: 120 minuten
|
|
139
|
+
Vereiste slots: 12:00, 12:15, 12:30, 12:45, 13:00, 13:15, 13:30, 13:45
|
|
140
|
+
|
|
141
|
+
Check per slot:
|
|
142
|
+
12:00 → beschikbaar: 6 stoelen ✓
|
|
143
|
+
12:15 → beschikbaar: 5 stoelen ✓
|
|
144
|
+
12:30 → beschikbaar: 3 stoelen ✗ (te weinig!)
|
|
145
|
+
|
|
146
|
+
Resultaat: 12:00 NIET BESCHIKBAAR (niet alle slots hebben voldoende capaciteit)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 4. Dagelijkse Gastentelling (`processing/dailyGuestCounts.js`)
|
|
150
|
+
|
|
151
|
+
Deze functie berekent hoeveel gasten er op elk moment van de dag aanwezig zijn.
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
getDailyGuestCounts(data, dateStr, reservations)
|
|
155
|
+
↓
|
|
156
|
+
Stap 1: Verwerk drie maaltijdtypes: ontbijt, lunch, diner
|
|
157
|
+
|
|
158
|
+
Stap 2: Voor elke maaltijd:
|
|
159
|
+
- Haal gastentellingen op per interval via getGuestCountsForMeal()
|
|
160
|
+
|
|
161
|
+
Stap 3: Merge resultaten op basis van maaltijdprioriteit:
|
|
162
|
+
DINER (hoogste) > LUNCH > ONTBIJT (laagste)
|
|
163
|
+
- Als tijden overlappen tussen maaltijden, wordt capaciteit van
|
|
164
|
+
maaltijd met hoogste prioriteit gebruikt
|
|
165
|
+
|
|
166
|
+
Stap 4: Geef terug:
|
|
167
|
+
- guestCounts: { "12:00": 5, "12:15": 3, ... } (beschikbare stoelen)
|
|
168
|
+
- shiftsInfo: [{ mealType, shiftName, time, availableSeats }, ...]
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Voorbeeld overlapping:**
|
|
172
|
+
```javascript
|
|
173
|
+
// Lunch eindigt om 15:00, Diner start om 14:00
|
|
174
|
+
|
|
175
|
+
14:00 → Lunch capaciteit: 25, Diner capaciteit: 30
|
|
176
|
+
Systeem kiest: 30 (diner heeft hogere prioriteit)
|
|
177
|
+
|
|
178
|
+
14:30 → Lunch capaciteit: 25, Diner capaciteit: 30
|
|
179
|
+
Systeem kiest: 30 (diner heeft hogere prioriteit)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 5. Gastentelling per Maaltijdtype (`processing/mealTypeCount.js`)
|
|
183
|
+
|
|
184
|
+
Voor elk maaltijdtype (ontbijt/lunch/diner):
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
getGuestCountsForMeal(data, dateStr, mealType, reservations)
|
|
188
|
+
↓
|
|
189
|
+
ALS shifts ingeschakeld:
|
|
190
|
+
Voor elke gedefinieerde shift tijd:
|
|
191
|
+
- Tel bezette gasten op dat moment via getGuestCountAtHour()
|
|
192
|
+
- beschikbareStoelen = maxCapacity - bezetteGasten
|
|
193
|
+
|
|
194
|
+
ANDERS (normale intervallen):
|
|
195
|
+
Genereer tijdslots: startTime tot endTime met intervalReservatie intervallen
|
|
196
|
+
Voor elk tijdslot:
|
|
197
|
+
- Tel bezette gasten via getGuestCountAtHour()
|
|
198
|
+
- beschikbareStoelen = maxCapacity - bezetteGasten
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 6. Gastentelling op Specifiek Uur (`reservation_data/counter.js`)
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
getGuestCountAtHour(data, reservations, hour, dateStr)
|
|
205
|
+
↓
|
|
206
|
+
Som gasten van alle reserveringen waar:
|
|
207
|
+
- datum === dateStr
|
|
208
|
+
- startTime <= hour < (startTime + duurReservatie)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Voorbeeld:**
|
|
212
|
+
```javascript
|
|
213
|
+
// Bestaande reserveringen op 2025-01-15:
|
|
214
|
+
Reservering A: 12:00, 4 gasten, duur 120 min (12:00 - 14:00)
|
|
215
|
+
Reservering B: 12:30, 2 gasten, duur 120 min (12:30 - 14:30)
|
|
216
|
+
Reservering C: 14:00, 6 gasten, duur 120 min (14:00 - 16:00)
|
|
217
|
+
|
|
218
|
+
// Gastentelling per tijdslot:
|
|
219
|
+
12:00 → 4 gasten (alleen A)
|
|
220
|
+
12:15 → 4 gasten (alleen A)
|
|
221
|
+
12:30 → 6 gasten (A + B)
|
|
222
|
+
12:45 → 6 gasten (A + B)
|
|
223
|
+
13:00 → 6 gasten (A + B)
|
|
224
|
+
13:30 → 6 gasten (A + B)
|
|
225
|
+
14:00 → 8 gasten (B + C, A is beëindigd)
|
|
226
|
+
14:30 → 8 gasten (B + C)
|
|
227
|
+
15:00 → 6 gasten (alleen C, B is beëindigd)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Datamodellen en Configuratie
|
|
233
|
+
|
|
234
|
+
### 1. Algemene Instellingen (`general-settings`)
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
{
|
|
238
|
+
zitplaatsen: 30, // Totaal aantal stoelen in restaurant
|
|
239
|
+
uurOpVoorhand: 4, // Aantal uren van tevoren vereist voor reservering
|
|
240
|
+
dagenInToekomst: 90, // Maximaal aantal dagen vooruit boeken
|
|
241
|
+
maxGasten: 8, // Maximum aantal gasten per reservering
|
|
242
|
+
intervalReservatie: 15, // Tijdslot interval in minuten
|
|
243
|
+
duurReservatie: 120, // Duur van reservering in minuten
|
|
244
|
+
minutenTotEinde: 135, // Minuten tot einde van dienst
|
|
245
|
+
ontbijtStop: "10:00", // Stoptijd voor ontbijt (dynamische stop)
|
|
246
|
+
lunchStop: "14:00", // Stoptijd voor lunch
|
|
247
|
+
dinerStop: "20:00" // Stoptijd voor diner
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Uitleg per instelling:**
|
|
252
|
+
|
|
253
|
+
- **zitplaatsen**: Het totaal aantal beschikbare stoelen in het hele restaurant
|
|
254
|
+
- **uurOpVoorhand**: Voorkomt last-minute reserveringen; gasten moeten X uren van tevoren boeken
|
|
255
|
+
- **dagenInToekomst**: Beperkt hoe ver vooruit klanten kunnen reserveren
|
|
256
|
+
- **maxGasten**: Maximum groepsgrootte voor online reserveringen
|
|
257
|
+
- **intervalReservatie**: Hoe vaak nieuwe tijdslots beginnen (bv. 15 min = 12:00, 12:15, 12:30...)
|
|
258
|
+
- **duurReservatie**: Standaard duur van elke reservering
|
|
259
|
+
- **minutenTotEinde**: Buffer tijd aan het einde van dienst (voorkomt te late reserveringen)
|
|
260
|
+
- **ontbijtStop/lunchStop/dinerStop**: Voor reserveringen op VANDAAG - stopt nieuwe boekingen na deze tijd
|
|
261
|
+
|
|
262
|
+
### 2. Maaltijdconfiguratie (`openinghours-breakfast/lunch/dinner`)
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
{
|
|
266
|
+
schemeSettings: {
|
|
267
|
+
Monday: {
|
|
268
|
+
enabled: true, // Is deze maaltijd beschikbaar op maandag?
|
|
269
|
+
startTime: "07:00", // Starttijd van maaltijd
|
|
270
|
+
endTime: "11:00", // Eindtijd van maaltijd
|
|
271
|
+
maxCapacity: 30, // Maximum capaciteit voor deze maaltijd
|
|
272
|
+
shiftsEnabled: false, // Gebruik shifts modus?
|
|
273
|
+
shifts: [], // Shift configuraties
|
|
274
|
+
giftcardsEnabled: false, // Cadeaubonnen beschikbaar?
|
|
275
|
+
giftcards: [] // Cadeaubon configuraties
|
|
276
|
+
},
|
|
277
|
+
Tuesday: { /* ... */ },
|
|
278
|
+
Wednesday: { /* ... */ },
|
|
279
|
+
Thursday: { /* ... */ },
|
|
280
|
+
Friday: { /* ... */ },
|
|
281
|
+
Saturday: { /* ... */ },
|
|
282
|
+
Sunday: { /* ... */ }
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Voorbeeld met shifts:**
|
|
288
|
+
```javascript
|
|
289
|
+
{
|
|
290
|
+
Monday: {
|
|
291
|
+
enabled: true,
|
|
292
|
+
startTime: "18:00",
|
|
293
|
+
endTime: "22:00",
|
|
294
|
+
maxCapacity: 40,
|
|
295
|
+
shiftsEnabled: true,
|
|
296
|
+
shifts: [
|
|
297
|
+
{ name: "Vroege dienst", time: "18:00" },
|
|
298
|
+
{ name: "Late dienst", time: "20:00" }
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 3. Uitzonderingen (`exceptions`)
|
|
305
|
+
|
|
306
|
+
Uitzonderingen overschrijven de normale openingstijden.
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
{
|
|
310
|
+
type: "Sluiting", // Type: "Sluiting", "Opening", "Uitzondering"
|
|
311
|
+
startDate: "2024-12-25", // Startdatum van uitzondering
|
|
312
|
+
endDate: "2024-12-25", // Einddatum van uitzondering
|
|
313
|
+
daysOfWeek: ["maandag"], // Dagen van de week (optioneel)
|
|
314
|
+
timeframe: "Volledige Dag", // "Volledige Dag" of specifiek maaltijdtype
|
|
315
|
+
startHour: "12:00", // Starttijd (voor gedeeltelijke sluitingen)
|
|
316
|
+
endHour: "15:00", // Eindtijd (voor gedeeltelijke sluitingen)
|
|
317
|
+
maxSeats: 20 // Aangepaste capaciteit (voor "Uitzondering")
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Prioriteitsvolgorde van uitzonderingen:**
|
|
322
|
+
1. **Opening** (hoogste prioriteit) - Opent het restaurant op normaal gesloten dag
|
|
323
|
+
2. **Sluiting** - Sluit het restaurant (geheel of gedeeltelijk)
|
|
324
|
+
3. **Uitzondering** (laagste prioriteit) - Past capaciteit aan zonder te sluiten
|
|
325
|
+
|
|
326
|
+
**Voorbeelden:**
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
// Voorbeeld 1: Volledige sluiting op Kerstmis
|
|
330
|
+
{
|
|
331
|
+
type: "Sluiting",
|
|
332
|
+
startDate: "2024-12-25",
|
|
333
|
+
endDate: "2024-12-25",
|
|
334
|
+
timeframe: "Volledige Dag"
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Voorbeeld 2: Speciale opening op zondag
|
|
338
|
+
{
|
|
339
|
+
type: "Opening",
|
|
340
|
+
startDate: "2024-12-01",
|
|
341
|
+
endDate: "2024-12-01",
|
|
342
|
+
daysOfWeek: ["zondag"],
|
|
343
|
+
timeframe: "Lunch",
|
|
344
|
+
startHour: "12:00",
|
|
345
|
+
endHour: "16:00",
|
|
346
|
+
maxSeats: 25
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Voorbeeld 3: Verminderde capaciteit voor privé evenement
|
|
350
|
+
{
|
|
351
|
+
type: "Uitzondering",
|
|
352
|
+
startDate: "2024-11-20",
|
|
353
|
+
endDate: "2024-11-20",
|
|
354
|
+
timeframe: "Diner",
|
|
355
|
+
maxSeats: 15 // In plaats van normale 30
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Boekingsfilters en Validatie
|
|
362
|
+
|
|
363
|
+
Na de basis beschikbaarheidsberekening worden extra filters toegepast om de beschikbare tijdslots verder te verfijnen.
|
|
364
|
+
|
|
365
|
+
### 1. Tijdsfilter (`filters/timeFilter.js`)
|
|
366
|
+
|
|
367
|
+
Verwijdert tijdslots op basis van stoptijden voor boekingen op dezelfde dag.
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
Functionaliteit:
|
|
371
|
+
- Verwijdert tijdslots na ontbijtStop/lunchStop/dinerStop
|
|
372
|
+
- Geldt ALLEEN voor reserveringen op VANDAAG
|
|
373
|
+
- Gebruikt Europe/Brussels tijdzone
|
|
374
|
+
|
|
375
|
+
Voorbeeld:
|
|
376
|
+
Huidige tijd: 09:30
|
|
377
|
+
ontbijtStop: "10:00"
|
|
378
|
+
dateStr: vandaag
|
|
379
|
+
|
|
380
|
+
Tijdslots voor ontbijt:
|
|
381
|
+
09:00 ✗ (verleden)
|
|
382
|
+
09:15 ✗ (verleden)
|
|
383
|
+
09:30 ✗ (binnen uurOpVoorhand venster)
|
|
384
|
+
09:45 ✗ (binnen uurOpVoorhand venster)
|
|
385
|
+
10:00 ✗ (na ontbijtStop)
|
|
386
|
+
10:15 ✗ (na ontbijtStop)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 2. Maximum Aankomsten Filter (`filters/maxArrivalsFilter.js`)
|
|
390
|
+
|
|
391
|
+
Beperkt het aantal gasten dat op exact hetzelfde tijdstip mag aankomen.
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
filterTimeblocksByMaxArrivals(restaurantData, date, timeblocks, reservations, guests)
|
|
395
|
+
↓
|
|
396
|
+
Voor elk tijdsblok:
|
|
397
|
+
Stap 1: Bepaal maaltijdtype op basis van tijd
|
|
398
|
+
Stap 2: Haal maxArrivalsConfig[maaltijdtype][tijd] instelling op
|
|
399
|
+
Stap 3: Tel huidige aankomsten op exact deze tijd op deze datum
|
|
400
|
+
Stap 4: Sta alleen toe als: huidigeAankomsten + nieuweGasten <= maxAankomsten
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Data structuur:**
|
|
404
|
+
```javascript
|
|
405
|
+
{
|
|
406
|
+
"max-arrivals-breakfast": {
|
|
407
|
+
"08:00": 5, // Max 5 gasten mogen om 08:00 aankomen
|
|
408
|
+
"08:15": 5,
|
|
409
|
+
"08:30": 8,
|
|
410
|
+
"08:45": 8
|
|
411
|
+
},
|
|
412
|
+
"max-arrivals-lunch": {
|
|
413
|
+
"12:00": 10, // Max 10 gasten mogen om 12:00 aankomen
|
|
414
|
+
"12:15": 10,
|
|
415
|
+
"12:30": 12,
|
|
416
|
+
"12:45": 12
|
|
417
|
+
},
|
|
418
|
+
"max-arrivals-dinner": {
|
|
419
|
+
"18:00": 15, // Max 15 gasten mogen om 18:00 aankomen
|
|
420
|
+
"18:30": 15,
|
|
421
|
+
"19:00": 12,
|
|
422
|
+
"19:30": 12
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Voorbeeld scenario:**
|
|
428
|
+
```javascript
|
|
429
|
+
// Bestaande reserveringen op 2025-01-15 om 12:00:
|
|
430
|
+
Reservering A: 12:00, 4 gasten
|
|
431
|
+
Reservering B: 12:00, 3 gasten
|
|
432
|
+
Totaal: 7 gasten
|
|
433
|
+
|
|
434
|
+
// Nieuwe aanvraag: 4 gasten om 12:00
|
|
435
|
+
maxArrivals voor 12:00 lunch: 10
|
|
436
|
+
|
|
437
|
+
Berekening: 7 (bestaand) + 4 (nieuw) = 11 > 10
|
|
438
|
+
Resultaat: ✗ GEWEIGERD (te veel aankomsten op dit tijdstip)
|
|
439
|
+
|
|
440
|
+
// Maar 12:15 is nog beschikbaar:
|
|
441
|
+
Aankomsten om 12:15: 0 gasten
|
|
442
|
+
Berekening: 0 + 4 = 4 ≤ 10
|
|
443
|
+
Resultaat: ✓ TOEGESTAAN
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### 3. Maximum Groepen Filter (`filters/maxGroupsFilter.js`)
|
|
447
|
+
|
|
448
|
+
Beperkt het aantal grote groepen tijdens een maaltijdperiode.
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
filterTimeblocksByMaxGroups(restaurantData, date, timeblocks, reservations, guests)
|
|
452
|
+
↓
|
|
453
|
+
Voor elk tijdsblok:
|
|
454
|
+
Stap 1: Bepaal maaltijdtype
|
|
455
|
+
Stap 2: Haal max-groups-{maaltijdtype} instellingen op
|
|
456
|
+
Stap 3: Instellingenstructuur: { "6": maxAantal, "7": maxAantal, ... }
|
|
457
|
+
- Definieert: max groepen van grootte >= 6, >= 7, etc.
|
|
458
|
+
Stap 4: Tel bestaande groepen >= gastGrootte in die maaltijd
|
|
459
|
+
Stap 5: Sta alleen toe als: bestaandeGroepen + 1 <= maxToegstaneGroepen
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Data structuur:**
|
|
463
|
+
```javascript
|
|
464
|
+
{
|
|
465
|
+
"max-groups-breakfast": {
|
|
466
|
+
"6": 2, // Maximaal 2 groepen van 6+ personen
|
|
467
|
+
"7": 1, // Maximaal 1 groep van 7+ personen
|
|
468
|
+
"8": 0 // Geen groepen van 8+ personen toegestaan
|
|
469
|
+
},
|
|
470
|
+
"max-groups-lunch": {
|
|
471
|
+
"6": 3, // Maximaal 3 groepen van 6+ personen
|
|
472
|
+
"7": 2, // Maximaal 2 groepen van 7+ personen
|
|
473
|
+
"8": 1 // Maximaal 1 groep van 8+ personen
|
|
474
|
+
},
|
|
475
|
+
"max-groups-dinner": {
|
|
476
|
+
"5": 5, // Maximaal 5 groepen van 5+ personen
|
|
477
|
+
"6": 3, // Maximaal 3 groepen van 6+ personen
|
|
478
|
+
"7": 2 // Maximaal 2 groepen van 7+ personen
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Voorbeeld scenario:**
|
|
484
|
+
```javascript
|
|
485
|
+
// Bestaande reserveringen tijdens lunch:
|
|
486
|
+
Reservering A: 6 gasten
|
|
487
|
+
Reservering B: 7 gasten
|
|
488
|
+
Reservering C: 4 gasten (telt niet mee voor "6+" filter)
|
|
489
|
+
|
|
490
|
+
// Nieuwe aanvraag: 6 gasten voor lunch
|
|
491
|
+
max-groups-lunch configuratie: { "6": 3, "7": 2 }
|
|
492
|
+
|
|
493
|
+
Berekening voor grootte "6+":
|
|
494
|
+
Bestaande groepen >= 6: 2 (Reservering A en B)
|
|
495
|
+
Nieuwe groep: 1
|
|
496
|
+
Totaal: 2 + 1 = 3 ≤ 3 (max)
|
|
497
|
+
Resultaat: ✓ TOEGESTAAN
|
|
498
|
+
|
|
499
|
+
// Maar als er een 7e gast bijkomt:
|
|
500
|
+
Nieuwe aanvraag: 7 gasten voor lunch
|
|
501
|
+
|
|
502
|
+
Berekening voor grootte "7+":
|
|
503
|
+
Bestaande groepen >= 7: 1 (Reservering B)
|
|
504
|
+
Nieuwe groep: 1
|
|
505
|
+
Totaal: 1 + 1 = 2 ≤ 2 (max)
|
|
506
|
+
Resultaat: ✓ TOEGESTAAN
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Hoe de drempel werkt:**
|
|
510
|
+
```javascript
|
|
511
|
+
// Voor een groep van 6 personen:
|
|
512
|
+
Controleert: max-groups["6"] → controleert limiet voor groepen van 6+ personen
|
|
513
|
+
|
|
514
|
+
// Voor een groep van 8 personen:
|
|
515
|
+
Controleert: max-groups["8"], max-groups["7"], max-groups["6"]
|
|
516
|
+
Moet voldoen aan ALLE limieten:
|
|
517
|
+
- Voldoende ruimte binnen limiet voor 8+ groepen
|
|
518
|
+
- Voldoende ruimte binnen limiet voor 7+ groepen
|
|
519
|
+
- Voldoende ruimte binnen limiet voor 6+ groepen
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Tafeltoewijzing
|
|
525
|
+
|
|
526
|
+
Het systeem kan optioneel automatisch tafels toewijzen aan reserveringen.
|
|
527
|
+
|
|
528
|
+
### 1. Tafel Data Structuur
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
{
|
|
532
|
+
tableNumber: 5, // Uniek tafelnummer
|
|
533
|
+
tableId: "id123", // Unieke tafel ID
|
|
534
|
+
minCapacity: 2, // Minimum gasten om tafel te vullen
|
|
535
|
+
maxCapacity: 4, // Maximum capaciteit
|
|
536
|
+
priority: 1, // Lagere waarde = hogere prioriteit
|
|
537
|
+
x: 100, // X-coördinaat op plattegrond
|
|
538
|
+
y: 150, // Y-coördinaat op plattegrond
|
|
539
|
+
isTemporary: false, // Is dit een tijdelijke tafel?
|
|
540
|
+
startDate: "2024-12-01", // Startdatum (voor tijdelijke tafels)
|
|
541
|
+
endDate: "2024-12-31", // Einddatum (voor tijdelijke tafels)
|
|
542
|
+
application: "dinner" // Maaltijdtype restrictie (breakfast/lunch/dinner)
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### 2. Enkele Tafel Toewijzing
|
|
547
|
+
|
|
548
|
+
```
|
|
549
|
+
assignTablesIfPossible({db, reservation, enforceTableAvailability})
|
|
550
|
+
↓
|
|
551
|
+
Stap 1: Bereken vereiste slots op basis van startTime + duurReservatie
|
|
552
|
+
(bv. 12:00 + 120 min = slots: 12:00, 12:15, 12:30, ..., 13:45)
|
|
553
|
+
|
|
554
|
+
Stap 2: Haal alle tafels op van restaurant.floors
|
|
555
|
+
|
|
556
|
+
Stap 3: Valideer tijdelijke tafels:
|
|
557
|
+
- Controleer datum binnen startDate/endDate bereik
|
|
558
|
+
- Controleer maaltijdtype restrictie (application veld)
|
|
559
|
+
|
|
560
|
+
Stap 4: Bouw tableOccupiedSlots: { tableNumber: Set([slot1, slot2, ...]) }
|
|
561
|
+
→ Welke tijdslots zijn al bezet per tafel
|
|
562
|
+
|
|
563
|
+
Stap 5: Probeer eerst vastgepinde groepen (via tryGroupTables)
|
|
564
|
+
|
|
565
|
+
Stap 6: Als geen groep match:
|
|
566
|
+
- Vind tafels waar: minCapacity <= gasten <= maxCapacity
|
|
567
|
+
- Controleer dat tafel vrij is voor ALLE vereiste slots
|
|
568
|
+
- Selecteer op basis van:
|
|
569
|
+
1. Kleinste capaciteit die past (minimaliseert verspilling)
|
|
570
|
+
2. Dan priority (lagere waarde eerst)
|
|
571
|
+
3. Dan minCapacity (kleinere tafels eerst)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**Voorbeeld tafeltoewijzing:**
|
|
575
|
+
```javascript
|
|
576
|
+
// Beschikbare tafels:
|
|
577
|
+
Tafel 1: min 2, max 4, priority 1
|
|
578
|
+
Tafel 2: min 2, max 4, priority 2
|
|
579
|
+
Tafel 3: min 4, max 6, priority 1
|
|
580
|
+
Tafel 4: min 6, max 8, priority 1
|
|
581
|
+
|
|
582
|
+
// Nieuwe reservering: 4 gasten om 12:00
|
|
583
|
+
|
|
584
|
+
Stap 1: Filter tafels waar 4 gasten passen
|
|
585
|
+
→ Tafel 1 ✓ (2-4)
|
|
586
|
+
→ Tafel 2 ✓ (2-4)
|
|
587
|
+
→ Tafel 3 ✓ (4-6)
|
|
588
|
+
→ Tafel 4 ✗ (6-8, te groot - minCapacity > 4)
|
|
589
|
+
|
|
590
|
+
Stap 2: Sorteer op capaciteit, dan priority, dan minCapacity
|
|
591
|
+
1. Tafel 1: maxCap 4, priority 1, minCap 2
|
|
592
|
+
2. Tafel 2: maxCap 4, priority 2, minCap 2
|
|
593
|
+
3. Tafel 3: maxCap 6, priority 1, minCap 4
|
|
594
|
+
|
|
595
|
+
Stap 3: Controleer beschikbaarheid in volgorde
|
|
596
|
+
Tafel 1: Vrij voor alle slots? → JA
|
|
597
|
+
|
|
598
|
+
Resultaat: Tafel 1 toegewezen ✓
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### 3. Vastgepinde Groepen Toewijzing (`grouping.js`)
|
|
602
|
+
|
|
603
|
+
Voor grote groepen kunnen meerdere tafels worden gecombineerd.
|
|
604
|
+
|
|
605
|
+
```
|
|
606
|
+
tryGroupTables({restaurantSettings, allTables, guests, ...})
|
|
607
|
+
↓
|
|
608
|
+
Stap 1: Controleer of groepering ingeschakeld is in grouping-settings
|
|
609
|
+
|
|
610
|
+
Stap 2: Valideer dat vastgepinde groepen bestaan en subtables hebben
|
|
611
|
+
|
|
612
|
+
Stap 3: Voor elke vastgepinde groep:
|
|
613
|
+
a) Haal alle subtables op
|
|
614
|
+
b) Controleer: totalMin <= gasten <= totalMax
|
|
615
|
+
c) Alloceer gasten:
|
|
616
|
+
- Vul eerst minCapacity van elke subtable
|
|
617
|
+
- Distribueer resterende gasten
|
|
618
|
+
d) Verifieer dat alle subtables vrij zijn voor alle vereiste slots
|
|
619
|
+
|
|
620
|
+
Stap 4: Geef gematchte groep terug met tableNumbers en viaGroup identifier
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**Data structuur vastgepinde groepen:**
|
|
624
|
+
```javascript
|
|
625
|
+
{
|
|
626
|
+
"grouping-settings": {
|
|
627
|
+
groupingEnabled: true,
|
|
628
|
+
pinnedGroups: [
|
|
629
|
+
{
|
|
630
|
+
groupId: "group1",
|
|
631
|
+
groupName: "Grote Groep Zone A",
|
|
632
|
+
subtables: [
|
|
633
|
+
{ tableNumber: 5, minCapacity: 2, maxCapacity: 4 },
|
|
634
|
+
{ tableNumber: 6, minCapacity: 2, maxCapacity: 4 },
|
|
635
|
+
{ tableNumber: 7, minCapacity: 2, maxCapacity: 4 }
|
|
636
|
+
]
|
|
637
|
+
}
|
|
638
|
+
]
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Totale capaciteit: min 6 (2+2+2), max 12 (4+4+4)
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**Voorbeeld groepstoewijzing:**
|
|
646
|
+
```javascript
|
|
647
|
+
// Vastgepinde groep: Tafels 5, 6, 7
|
|
648
|
+
Tafel 5: min 2, max 4
|
|
649
|
+
Tafel 6: min 2, max 4
|
|
650
|
+
Tafel 7: min 2, max 4
|
|
651
|
+
Totaal: min 6, max 12
|
|
652
|
+
|
|
653
|
+
// Nieuwe reservering: 10 gasten
|
|
654
|
+
|
|
655
|
+
Stap 1: Check capaciteit
|
|
656
|
+
10 >= 6 (min) ✓
|
|
657
|
+
10 <= 12 (max) ✓
|
|
658
|
+
|
|
659
|
+
Stap 2: Alloceer gasten
|
|
660
|
+
Eerst minCapacity vullen:
|
|
661
|
+
Tafel 5: 2 gasten (min)
|
|
662
|
+
Tafel 6: 2 gasten (min)
|
|
663
|
+
Tafel 7: 2 gasten (min)
|
|
664
|
+
Totaal: 6 gasten, resterend: 4 gasten
|
|
665
|
+
|
|
666
|
+
Dan resterende gasten verdelen:
|
|
667
|
+
Tafel 5: +2 gasten → 4 gasten (max bereikt)
|
|
668
|
+
Tafel 6: +2 gasten → 4 gasten (max bereikt)
|
|
669
|
+
Tafel 7: +0 gasten → 2 gasten
|
|
670
|
+
Totaal: 10 gasten ✓
|
|
671
|
+
|
|
672
|
+
Stap 3: Controleer beschikbaarheid
|
|
673
|
+
Tafel 5 vrij? ✓
|
|
674
|
+
Tafel 6 vrij? ✓
|
|
675
|
+
Tafel 7 vrij? ✓
|
|
676
|
+
|
|
677
|
+
Resultaat: Tafels 5, 6, 7 toegewezen als groep ✓
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## Volledige Reserveringsflow
|
|
683
|
+
|
|
684
|
+
Hier is de complete flow van een reservering van begin tot eind:
|
|
685
|
+
|
|
686
|
+
```
|
|
687
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
688
|
+
│ Gebruiker vraagt beschikbaarheid voor X gasten op datum D │
|
|
689
|
+
└────────────────────────┬────────────────────────────────────┘
|
|
690
|
+
↓
|
|
691
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
692
|
+
│ STAP 1: isDateAvailableWithTableCheck() │
|
|
693
|
+
│ │
|
|
694
|
+
│ a) isDateAvailable() → Basis beschikbaarheidscheck │
|
|
695
|
+
│ - Controleer dagenInToekomst bereik │
|
|
696
|
+
│ - Haal beschikbare timeblocks op │
|
|
697
|
+
│ │
|
|
698
|
+
│ b) ALS tafeltoewijzing ingeschakeld: │
|
|
699
|
+
│ Voor elk beschikbaar timeblock: │
|
|
700
|
+
│ - Check timeHasGiftcard() als cadeaubon geselecteerd │
|
|
701
|
+
│ - Check isTimeAvailableSync() → kunnen tafels worden │
|
|
702
|
+
│ toegewezen? │
|
|
703
|
+
│ Return TRUE als ER EEN timeblock beschikbare tafel heeft │
|
|
704
|
+
│ │
|
|
705
|
+
│ c) Return beschikbaarheidsflag │
|
|
706
|
+
└────────────────────────┬────────────────────────────────────┘
|
|
707
|
+
↓
|
|
708
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
709
|
+
│ Gebruiker selecteert tijd T op datum D │
|
|
710
|
+
└────────────────────────┬────────────────────────────────────┘
|
|
711
|
+
↓
|
|
712
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
713
|
+
│ STAP 2: assignTablesIfPossible() │
|
|
714
|
+
│ │
|
|
715
|
+
│ Probeer vastgepinde groep → │
|
|
716
|
+
│ Probeer enkele tafel → │
|
|
717
|
+
│ Wijs toe │
|
|
718
|
+
└────────────────────────┬────────────────────────────────────┘
|
|
719
|
+
↓
|
|
720
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
721
|
+
│ STAP 3: Maak reservering met toegewezen tafel(s) │
|
|
722
|
+
└─────────────────────────────────────────────────────────────┘
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
### Gedetailleerde Stappen
|
|
726
|
+
|
|
727
|
+
#### **Stap 1a: Basis Beschikbaarheidscheck**
|
|
728
|
+
|
|
729
|
+
```javascript
|
|
730
|
+
// Voorbeeld aanroep
|
|
731
|
+
isDateAvailable(restaurantData, "2025-01-15", bestaandeReserveringen, 4)
|
|
732
|
+
|
|
733
|
+
// Interne flow:
|
|
734
|
+
1. Check: "2025-01-15" binnen 90 dagen? ✓
|
|
735
|
+
2. Roep getAvailableTimeblocks() aan
|
|
736
|
+
→ Krijg: { "12:00": true, "12:15": true, "12:30": false, ... }
|
|
737
|
+
3. Filter lege resultaten
|
|
738
|
+
4. Return: true (er zijn beschikbare tijden)
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
#### **Stap 1b: Tafelbeschikbaarheidscheck** (optioneel)
|
|
742
|
+
|
|
743
|
+
```javascript
|
|
744
|
+
// Voor elk beschikbaar tijdsblok
|
|
745
|
+
Voor tijd "12:00":
|
|
746
|
+
1. Check: Heeft deze tijd cadeaubon als vereist? ✓
|
|
747
|
+
2. Roep isTimeAvailableSync("2025-01-15", "12:00", 4) aan
|
|
748
|
+
→ Probeer tafel toe te wijzen (zonder op te slaan)
|
|
749
|
+
→ Return: true als tafel beschikbaar
|
|
750
|
+
3. Als true: behoud "12:00" in beschikbare lijst
|
|
751
|
+
4. Als false: verwijder "12:00" uit beschikbare lijst
|
|
752
|
+
|
|
753
|
+
Uiteindelijk resultaat: Alleen tijden waar zowel capaciteit ALS tafels beschikbaar zijn
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
#### **Stap 2: Tafel Toewijzing**
|
|
757
|
+
|
|
758
|
+
```javascript
|
|
759
|
+
// Wanneer gebruiker tijd selecteert
|
|
760
|
+
assignTablesIfPossible({
|
|
761
|
+
db: database,
|
|
762
|
+
reservation: {
|
|
763
|
+
date: "2025-01-15",
|
|
764
|
+
startTime: "12:00",
|
|
765
|
+
guests: 4,
|
|
766
|
+
durationMinutes: 120
|
|
767
|
+
},
|
|
768
|
+
enforceTableAvailability: true
|
|
769
|
+
})
|
|
770
|
+
|
|
771
|
+
// Interne flow:
|
|
772
|
+
1. Bereken slots: ["12:00", "12:15", "12:30", ..., "13:45"]
|
|
773
|
+
2. Haal alle tafels op en filter tijdelijke/applicatie restricties
|
|
774
|
+
3. Bouw occupied slots map voor elke tafel
|
|
775
|
+
4. Probeer groepstoewijzing (als 4 >= min groepsgrootte)
|
|
776
|
+
→ Geen match gevonden
|
|
777
|
+
5. Probeer enkele tafel:
|
|
778
|
+
→ Tafel 3 past (min 2, max 4)
|
|
779
|
+
→ Tafel 3 is vrij voor alle slots ✓
|
|
780
|
+
→ Wijs Tafel 3 toe
|
|
781
|
+
6. Return: { success: true, assignedTables: [3] }
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
#### **Stap 3: Reservering Opslaan**
|
|
785
|
+
|
|
786
|
+
```javascript
|
|
787
|
+
// Reservering object
|
|
788
|
+
{
|
|
789
|
+
id: "res123",
|
|
790
|
+
date: "2025-01-15",
|
|
791
|
+
startTime: "12:00",
|
|
792
|
+
guests: 4,
|
|
793
|
+
assignedTables: [3],
|
|
794
|
+
customerName: "Jan Janssen",
|
|
795
|
+
email: "jan@example.com",
|
|
796
|
+
phone: "+31612345678"
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Opgeslagen in database
|
|
800
|
+
// Toekomstige beschikbaarheidschecks zullen deze reservering meenemen
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
## Belangrijke Validatieregels
|
|
806
|
+
|
|
807
|
+
Bij elke reserveringsaanvraag worden de volgende regels gecontroleerd:
|
|
808
|
+
|
|
809
|
+
### 1. **Datumvalidatie**
|
|
810
|
+
```
|
|
811
|
+
Regel: Datum moet binnen dagenInToekomst bereik liggen
|
|
812
|
+
Check: dateStr >= vandaag EN dateStr <= vandaag + dagenInToekomst
|
|
813
|
+
|
|
814
|
+
Voorbeeld:
|
|
815
|
+
Vandaag: 2025-01-15
|
|
816
|
+
dagenInToekomst: 90
|
|
817
|
+
Toegestaan bereik: 2025-01-15 tot 2025-04-15
|
|
818
|
+
|
|
819
|
+
Aanvraag: 2025-02-01 ✓ (binnen bereik)
|
|
820
|
+
Aanvraag: 2025-05-01 ✗ (buiten bereik)
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### 2. **Tijdvalidatie**
|
|
824
|
+
```
|
|
825
|
+
Regel: Tijd moet binnen maaltijd operationele uren vallen (met uitzonderingen)
|
|
826
|
+
Check: startTime >= mealStartTime EN startTime < mealEndTime
|
|
827
|
+
|
|
828
|
+
Voorbeeld:
|
|
829
|
+
Lunch: 12:00 - 15:00
|
|
830
|
+
|
|
831
|
+
Aanvraag: 12:00 ✓ (binnen maaltijd)
|
|
832
|
+
Aanvraag: 14:30 ✓ (binnen maaltijd)
|
|
833
|
+
Aanvraag: 15:00 ✗ (na eindtijd)
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### 3. **Duurvalidatie**
|
|
837
|
+
```
|
|
838
|
+
Regel: Starttijd + duurReservatie moet binnen maaltijd eindtijd passen
|
|
839
|
+
Check: startTime + duurReservatie <= mealEndTime
|
|
840
|
+
|
|
841
|
+
Voorbeeld:
|
|
842
|
+
Lunch eindigt: 15:00
|
|
843
|
+
Duur reservering: 120 minuten
|
|
844
|
+
|
|
845
|
+
Aanvraag: 12:00 → eindigt 14:00 ✓ (past binnen lunch)
|
|
846
|
+
Aanvraag: 13:30 → eindigt 15:30 ✗ (loopt over eindtijd heen)
|
|
847
|
+
|
|
848
|
+
Let op: minutenTotEinde (135 min) wordt ook gecontroleerd
|
|
849
|
+
Laatste toegestane start: 15:00 - 135 min = 12:45
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### 4. **Capaciteitsvalidatie**
|
|
853
|
+
```
|
|
854
|
+
Regel: Beschikbare stoelen >= aantal gasten voor ALLE opeenvolgende slots
|
|
855
|
+
Check: Voor elke vereiste slot, availableSeats >= requestedGuests
|
|
856
|
+
|
|
857
|
+
Voorbeeld:
|
|
858
|
+
Reservering: 4 gasten om 12:00, duur 120 min
|
|
859
|
+
Vereiste slots: 12:00, 12:15, 12:30, 12:45, 13:00, 13:15, 13:30, 13:45
|
|
860
|
+
|
|
861
|
+
Beschikbare stoelen per slot:
|
|
862
|
+
12:00 → 6 stoelen ✓
|
|
863
|
+
12:15 → 5 stoelen ✓
|
|
864
|
+
12:30 → 3 stoelen ✗ (TE WEINIG!)
|
|
865
|
+
|
|
866
|
+
Resultaat: ✗ AFGEWEZEN (niet alle slots hebben voldoende capaciteit)
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### 5. **Vooraf Boekingsvalidatie**
|
|
870
|
+
```
|
|
871
|
+
Regel: Voor reserveringen VANDAAG, moet starttijd >= uurOpVoorhand uren van nu
|
|
872
|
+
Check: startDateTime >= huidigeDateTime + uurOpVoorhand
|
|
873
|
+
|
|
874
|
+
Voorbeeld:
|
|
875
|
+
Huidige tijd: 10:00
|
|
876
|
+
uurOpVoorhand: 4 uren
|
|
877
|
+
Minimum starttijd: 14:00
|
|
878
|
+
|
|
879
|
+
Aanvraag: 12:00 ✗ (binnen 4 uur venster)
|
|
880
|
+
Aanvraag: 14:00 ✓ (exact 4 uur vooruit)
|
|
881
|
+
Aanvraag: 15:00 ✓ (meer dan 4 uur vooruit)
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
### 6. **Stoptijdvalidatie**
|
|
885
|
+
```
|
|
886
|
+
Regel: Kan niet boeken na ontbijtStop/lunchStop/dinerStop als datum = vandaag
|
|
887
|
+
Check: Als dateStr === vandaag, filter tijden > stopTijd voor die maaltijd
|
|
888
|
+
|
|
889
|
+
Voorbeeld:
|
|
890
|
+
Huidige tijd: 09:30
|
|
891
|
+
ontbijtStop: "10:00"
|
|
892
|
+
Datum: vandaag
|
|
893
|
+
|
|
894
|
+
Beschikbare ontbijttijden:
|
|
895
|
+
09:00 ✗ (in het verleden)
|
|
896
|
+
09:15 ✗ (in het verleden)
|
|
897
|
+
09:30 ✗ (binnen uurOpVoorhand)
|
|
898
|
+
09:45 ✗ (binnen uurOpVoorhand)
|
|
899
|
+
10:00 ✗ (na stopTijd)
|
|
900
|
+
10:15 ✗ (na stopTijd)
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
### 7. **Tafelvalidatie** (indien tafeltoewijzing ingeschakeld)
|
|
904
|
+
```
|
|
905
|
+
Regel: Tafel moet vrije slots hebben + capaciteit matchen + aan restricties voldoen
|
|
906
|
+
Check:
|
|
907
|
+
a) minCapacity <= gasten <= maxCapacity
|
|
908
|
+
b) Alle vereiste tijdslots zijn vrij
|
|
909
|
+
c) Als tijdelijk: datum binnen startDate/endDate
|
|
910
|
+
d) Als application beperkt: maaltijdtype matcht
|
|
911
|
+
|
|
912
|
+
Voorbeeld:
|
|
913
|
+
Tafel 5: min 2, max 4, application: "dinner", tijdelijk: 01-12 tot 31-12
|
|
914
|
+
|
|
915
|
+
Aanvraag: 4 gasten, lunch, 15 december
|
|
916
|
+
Capaciteit: 2 <= 4 <= 4 ✓
|
|
917
|
+
Datum: 15-12 binnen 01-12 tot 31-12 ✓
|
|
918
|
+
Maaltijdtype: lunch ≠ dinner ✗
|
|
919
|
+
|
|
920
|
+
Resultaat: ✗ AFGEWEZEN (maaltijdtype restrictie)
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### 8. **Maximum Aankomsten Validatie**
|
|
924
|
+
```
|
|
925
|
+
Regel: Totale aankomsten op exact tijdstip <= maxArrivals voor die tijd
|
|
926
|
+
Check: bestaandeAankomsten[tijd] + nieuweGasten <= maxArrivals[tijd]
|
|
927
|
+
|
|
928
|
+
Voorbeeld:
|
|
929
|
+
maxArrivals lunch 12:00: 10 gasten
|
|
930
|
+
Bestaande aankomsten om 12:00: 7 gasten
|
|
931
|
+
|
|
932
|
+
Aanvraag: 4 gasten om 12:00
|
|
933
|
+
7 + 4 = 11 > 10 ✗
|
|
934
|
+
|
|
935
|
+
Resultaat: ✗ AFGEWEZEN (te veel gelijktijdige aankomsten)
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
### 9. **Maximum Groepen Validatie**
|
|
939
|
+
```
|
|
940
|
+
Regel: Aantal groepen van grootte X in maaltijd <= maxGroups voor die grootte
|
|
941
|
+
Check: bestaandeGroepen[grootte] + 1 <= maxGroups[grootte]
|
|
942
|
+
|
|
943
|
+
Voorbeeld:
|
|
944
|
+
maxGroups lunch: { "6": 3, "7": 2 }
|
|
945
|
+
Bestaande lunch reserveringen:
|
|
946
|
+
- 6 gasten (groep 1)
|
|
947
|
+
- 7 gasten (groep 2)
|
|
948
|
+
- 4 gasten (telt niet mee)
|
|
949
|
+
|
|
950
|
+
Aanvraag: 6 gasten voor lunch
|
|
951
|
+
Voor grootte "6+": 2 bestaande + 1 nieuwe = 3 <= 3 ✓
|
|
952
|
+
|
|
953
|
+
Resultaat: ✓ TOEGESTAAN (precies aan limiet)
|
|
954
|
+
|
|
955
|
+
Volgende aanvraag: nog 6 gasten
|
|
956
|
+
Voor grootte "6+": 3 bestaande + 1 nieuwe = 4 > 3 ✗
|
|
957
|
+
|
|
958
|
+
Resultaat: ✗ AFGEWEZEN (te veel grote groepen)
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
---
|
|
962
|
+
|
|
963
|
+
## Samenvatting
|
|
964
|
+
|
|
965
|
+
Het HappyChef reserveringssysteem is een geavanceerd, meerlagig algoritme dat:
|
|
966
|
+
|
|
967
|
+
1. **Beschikbaarheid berekent** op basis van capaciteit, openingstijden, en bestaande reserveringen
|
|
968
|
+
2. **Meerdere maaltijdtypes ondersteunt** met verschillende configuraties en prioriteiten
|
|
969
|
+
3. **Uitzonderingen afhandelt** zoals feestdagen, sluitingen, en speciale evenementen
|
|
970
|
+
4. **Filters toepast** om ongewenste scenario's te voorkomen (te veel aankomsten, te grote groepen)
|
|
971
|
+
5. **Automatisch tafels toewijst** met ondersteuning voor enkele tafels en gecombineerde groepen
|
|
972
|
+
6. **Intelligent valideert** met 9 verschillende validatieregels
|
|
973
|
+
|
|
974
|
+
Het systeem is gebouwd om flexibel, schaalbaar en betrouwbaar te zijn voor moderne restaurantoperaties.
|
|
975
|
+
|
|
976
|
+
---
|
|
977
|
+
|
|
978
|
+
## Bestandslocaties
|
|
979
|
+
|
|
980
|
+
Alle belangrijke bestanden bevinden zich in `/home/user/15-happy-algorithm/`:
|
|
981
|
+
|
|
982
|
+
- **Hoofdingang**: `index.js`
|
|
983
|
+
- **Restaurant data**: `restaurant_data/openinghours.js`, `restaurant_data/exceptions.js`
|
|
984
|
+
- **Processing engines**: `processing/timeblocksAvailable.js`, `processing/dailyGuestCounts.js`
|
|
985
|
+
- **Filters**: `filters/timeFilter.js`, `filters/maxArrivalsFilter.js`, `filters/maxGroupsFilter.js`
|
|
986
|
+
- **Tafelbeheer**: `assignTables.js`, `grouping.js`, `tableHelpers.js`
|