Ga naar inhoud

Beschikbaarheid (Backend)

De backend van de module Beschikbaarheid beheert de opslag en validatie van de inzetbaarheid van medewerkers. De logica is ondergebracht in de features.beschikbaarheid Django-app. De opgeslagen beschikbaarheid dient als invoer voor de roosterplanning; de module maakt zelf geen rooster.

Technisch Ontwerp

De module gebruikt een "full-replace" strategie bij het opslaan van beschikbaarheid per week: bij een PUT worden alle bestaande records van de ingelogde gebruiker voor die week eerst verwijderd en daarna opnieuw aangemaakt. Dit houdt de database schoon en voorkomt synchronisatievraagstukken tussen oude en nieuwe selecties.

Opslagproces (Flowchart)

flowchart TD
    A[PUT /api/beschikbaarheid/] --> B{week_start geldig?}
    B -- Nee --> C[400 Bad Request]
    B -- Ja --> D{availability een object?}
    D -- Nee --> C
    D -- Ja --> E{Binnen limieten?<br/>max 7 dagen,<br/>max 50 dagdelen/dag}
    E -- Nee --> C
    E -- Ja --> F[services.save_availability]
    F --> G[Verwijder bestaande records<br/>user + week_start_date]
    G --> H[bulk_create nieuwe records<br/>voor request.user]
    H --> I[services.get_availability<br/>Return JSON]

De caps BESCHIKBAARHEID_MAX_DAGEN (7) en BESCHIKBAARHEID_MAX_DAGDELEN_PER_DAG (50) worden in de view afgedwongen voordat de payload bulk_create bereikt, zodat een enkel verzoek nooit duizenden rijen kan aanmaken.

Automatische vulling

De service fill_beschikbaarheid_from_vaste_werkdagen maakt voor elke VasteWerkdag (uit de module Rooster) records met available=True aan voor de huidige week plus de komende weken (standaard 12). Het proces is idempotent: bulk_create(..., ignore_conflicts=True) overschrijft bestaande records, inclusief handmatige overrides, nooit. De service draait via de Celery-task fill_beschikbaarheid_weekly en wordt daarnaast direct aangeroepen wanneer vaste werkdagen in de module Rooster worden opgeslagen.

Datamodel (ERD)

De kern van de module is het Beschikbaarheid model. Elke rij beschrijft de beschikbaarheid van een medewerker voor een enkel dagdeel in een enkele week.

erDiagram
    User ||--o{ Beschikbaarheid : "heeft"
    Beschikbaarheid {
        uuid id PK
        bigint user_id FK
        date week_start_date "altijd een maandag, db_index"
        smallint day_index "0-6 (ma-zo)"
        uuid dagdeel_id "db_index, geen FK"
        bool available "default true"
        datetime updated_at
    }
  • user_id: verwijst naar accounts.User (bigint primary key, BigAutoField). De foreign key gebruikt db_constraint=False.
  • dagdeel_id: slaat de UUID van het dagdeel op als los veld zonder Foreign Key. Hierdoor blijven beschikbaarheidsrecords behouden als de configuratie van dagdelen in de module Rooster wijzigt of wordt verwijderd.
  • Constraints: UniqueConstraint op de combinatie user, week_start_date, day_index en dagdeel_id. Indexen op (user, week_start_date) en (week_start_date, dagdeel_id).

API & Communicatie

De communicatie verloopt via een enkele APIView (BeschikbaarheidView) op /api/beschikbaarheid/. De querystring week_start=YYYY-MM-DD (een maandag) is op beide methoden verplicht.

  • GET /api/beschikbaarheid/?week_start=YYYY-MM-DD: haalt de beschikbaarheid voor de gevraagde week op. Standaard ziet een gebruiker alleen de eigen data. Een gebruiker met roster.edit ziet de data van alle medewerkers, zodat de planner de beschikbaarheid kan inplannen.
  • PUT /api/beschikbaarheid/?week_start=YYYY-MM-DD: slaat de beschikbaarheid van de ingelogde gebruiker voor de gevraagde week op (full-replace). De body heeft de vorm { "availability": { "<day_index>": { "<dagdeel_id>": true|false } } }.

Beide methoden geven hetzelfde antwoord terug:

{
  "week_start_date": "2026-03-16",
  "availability": {
    "42": { "0": { "<dagdeel-uuid>": true, "<dagdeel-uuid>": false } }
  }
}

De sleutels van availability zijn medewerker-id, day_index en dagdeel_id (alle als string). Bij een gewone gebruiker bevat de map alleen de eigen user_id.

Foutafhandeling & Statuscodes

  • 400 Bad Request: ontbrekende of ongeldige week_start parameter, availability is geen object, een dag-map is geen object, of de limieten (BESCHIKBAARHEID_MAX_DAGEN, BESCHIKBAARHEID_MAX_DAGDELEN_PER_DAG) worden overschreden.
  • 401 Unauthorized: geen geldige sessie of token.

Ongeldige entries binnen een verder geldige payload (een niet-numerieke day_index of een ongeldige dagdeel_id UUID) worden in save_availability stil overgeslagen; de overige records worden gewoon opgeslagen.

Autorisatie & Beveiliging

  • Tenant Isolatie: alle queries lopen binnen het schema van de huidige tenant via TenantEnforcedJWTAuthentication en de django-tenants middleware.
  • Basis-teammodule, geen schrijf-RBAC: BeschikbaarheidView gebruikt HasPermissionCode zonder required_permission, dus elke ingelogde medewerker mag de eigen beschikbaarheid opslaan. save_availability schrijft uitsluitend records voor request.user; een gebruiker kan de beschikbaarheid van een ander niet bewerken. Er bestaat geen availability.edit permissiecode.
  • Inzage in andermans beschikbaarheid: get_availability verbreedt de GET alleen tot alle medewerkers wanneer de gebruiker roster.edit heeft. Zonder die code wordt de queryset gefilterd op de eigen gebruiker.

Bestandsstructuur & Verantwoordelijkheden

  • models.py: definieert het Beschikbaarheid model (constraints en indexen).
  • services.py: business logic voor get_availability, save_availability (full-replace) en fill_beschikbaarheid_from_vaste_werkdagen (automatische vulling).
  • views.py: BeschikbaarheidView met GET en PUT, parameter-validatie en de payload-limieten.
  • serializers.py: BeschikbaarheidSerializer voor de transformatie naar JSON.
  • urls.py: koppelt de view aan /api/beschikbaarheid/.
  • tasks.py: Celery-taken cleanup_old_beschikbaarheid (verwijdert records ouder dan 4 weken) en fill_beschikbaarheid_weekly (automatische vulling per tenant).

Belangrijke bestanden

  • backend/features/beschikbaarheid/models.py
  • backend/features/beschikbaarheid/services.py
  • backend/features/beschikbaarheid/views.py

API & Communicatie (Swagger)

De volledige API-specificatie voor deze module is hieronder beschikbaar: