# Technische beslissingen

Overzicht van afgesproken keuzes en de reden daarvoor. Bijwerken zodra een
beslissing verandert of er een nieuwe bijkomt.

---

## Architectuur

### SQL alleen in Repositories
SQL mag uitsluitend in Repository-klassen staan. Controllers en Services
gebruiken repositories voor alle databasetoegang. Dit zorgt voor een duidelijke
scheidingslijn en maakt SQL-aanpassingen voorspelbaar te vinden.

### Database altijd opnieuw aanmaken
De ontwikkelomgeving werkt met `php setup_database.php && php seed_database.php`.
Er zijn geen incrementele migraties nodig voor de lokale developer-flow; alles
start fris. Productie-updates gaan via `database/migrations/*.sql`.

---

## Gasttypen

### Geen hardcoded gasttypen — volledig dynamisch
Er zijn **geen** hardcoded gasttype-IDs (`adults`, `children`, `infants`) in de
engine. Gasttypen worden altijd geconfigureerd per klant (in
`customer.data.guest_types`) en optioneel overschreven per rentable-groep
(`rentable_group.data.guest_types`).

De seed-database bevat de bekende typen `adults / children / infants` als
**standaard-demonstratie**, niet als hardcoded systeemwaarden.

**`Guests`-klasse** werkt in twee modi:
- *Configured mode*: caller geeft `$guestTypes` mee; defaults en `counts_for_capacity` worden gerespecteerd.
- *Pass-through mode*: geen `$guestTypes` → alle aangeleverde waarden worden geaccepteerd; elk type telt mee voor capaciteit.

### Conditiesleutels voor gasttypen
Per-type condities volgen het patroon `min_guest_{type_id}` / `max_guest_{type_id}`.
De engine itereert dynamisch over alle condities met dit prefix; er zijn
geen sleutels hardcoded in de code.

Overige gastcondities die altijd beschikbaar zijn (op totaal):
`min_persons`, `max_persons`, `min_total_guests`, `max_total_guests`.

### flat_per_guest
`guest_type` in de actie moet overeenkomen met een geconfigureerd type-id
(bijv. `adults`, `children`) of `all` (totaal van alle gasten).
Positief bedrag = toeslag, negatief = korting.

---

## Prijsregels

### condition_operator: AND/OR per regel
Elke `pricing_rule` heeft een `condition_operator`-kolom (`and` of `or`, default `and`).
- **AND**: alle ingevulde condities moeten tegelijkertijd voldaan zijn.
- **OR**: het is voldoende als één conditie voldaan is.
- Leege condities (`{}`) matchen altijd, ongeacht de operator.

**Reden**: klanten hebben soms regels die op meerdere situaties moeten slaan
(bijv. toeslag als het weekend is OF als er ≥4 volwassenen zijn).

### flat_per_guest: positief = toeslag, negatief = korting
Er is geen aparte `discount_per_guest` actie. Negatieve bedragen zijn
kortingen, positieve zijn toeslagen. Dit houdt het actiemodel eenvoudig.

### flat_fee vervangt fixed
`flat_fee` is de nieuwe canonical naam voor een vast bedrag. De oude naam
`fixed` blijft werken (backward-compatible in de engine).

---

## Prijsmodus (pricing_mode)

### Opgeslagen in JSON-kolommen, geen schema-aanpassing
`pricing_mode` wordt opgeslagen in `customer.data.pricing_mode` en kan worden
overschreven in `rentable_group.data.pricing_mode`. Geen nieuwe tabelkolom nodig.

Beschikbare modi (voorlopig documentatie, UI nog niet compleet):

| Mode | Gebruik |
|------|---------|
| `simple` | Één basisprijs |
| `per_person` | Prijs p.p. + gasttypen-toeslagen |
| `seasonal` | Periodeblokken per datumbereik |
| `full` | Volledige regeleditor |

**Reden**: klanten denken anders over tariefinvoer. De backend rekent altijd
hetzelfde; de modus bepaalt welk invoerformulier de admin te zien krijgt.

---

## Admin-menu

### "In ontwikkeling"-sectie onderaan de sidebar
Menuonderdelen die nog niet volledig werken (Taken, Rapportages, Kostenposten,
BTW-groepen, Sjablonen) staan tijdelijk onder een ingeklapte
`<details>`-sectie onderaan de sidebar, zodat ze bereikbaar blijven tijdens
de bouw. Zodra een onderdeel klaar is, verplaatsen naar het juiste menu-blok
en uit de dev-sectie verwijderen.

---

## Testing

### Altijd seed + tests bijwerken bij nieuwe functionaliteit
Nieuwe features krijgen altijd:
1. Seed-regels in `seed_database.php` als demo/testdata
2. Unit-tests in `tests/Unit/Services/` of het passende testbestand

### API-integratietests skippen als de server niet bereikbaar is
`ApiTestCase::isServerReachable()` controleert éénmalig of
`https://api.booking.nl.test` beschikbaar is. Zo draaien de unit-tests altijd,
ook offline, terwijl de integratietests op de lokale omgeving volledig werken.
