Night Audit
Rôle du Night Audit
Section intitulée « Rôle du Night Audit »Le Night Audit est le processus de clôture comptable journalière de l’établissement. Il fait avancer la businessDate (date métier), pose les charges automatiques et prépare le reporting du lendemain.
Il s’exécute à 3h00 heure locale via un Temporal Schedule créé par établissement au démarrage du worker night-audit. Si un établissement est créé après le démarrage du worker, ensureAllSchedules recréé les schedules manquants.
businessDate vs temps système
Section intitulée « businessDate vs temps système »La businessDate est distincte du temps système (new Date()). Elle représente la date commerciale de l’établissement :
- Entre minuit et 3h00 (Night Audit), on est encore sur J — les opérations de la nuit appartiennent à J
- Après le Night Audit, on passe à J+1
La businessDate est propriété de l’établissement, stockée dans pms.establishment_business_dates. Elle est avancée par le Night Audit (étape 6 ci-dessous) de manière idempotente avec advisory xact lock.
Étapes du Night Audit
Section intitulée « Étapes du Night Audit »Le workflow Temporal night-audit exécute ces étapes séquentiellement :
| Étape | Activity | Description |
|---|---|---|
| 0 | verrou | Advisory xact lock par établissement (empêche le double run) |
| 1 | markNoShows | Marque en no-show les réservations Confirmed dont checkInDate = businessDate courante |
| 2 | postRoomCharges | Poste les charges de séjour (revenue) pour toutes les Stay en cours |
| 3 | postTourismTax | Poste la taxe de séjour (collected_tax, compte 4471) sur les folios |
| 4 | postMealPlanCharges | Poste les plans restauration du jour |
| 5 | postDueInstallments | Poste les échéances de contrats long-stay arrivées à échéance (ADR-0019) |
| 6 | avanceBusinessDate | Avance la businessDate de J à J+1 (idempotent, advisory xact lock) |
| 6c | ancrage Merkle WORM | Calcule la racine Merkle de la chaîne d’audit, écrit en stockage objet WORM |
| 7 | snapshot reporting | Publie l’event NightAuditCompleted → consumers reporting projettent les KPIs |
Garde anti-rétroactif
Section intitulée « Garde anti-rétroactif »La garde FOLIO_CHARGE_RETROACTIVE empêche de poster une charge sur une businessDate antérieure à la businessDate courante de l’établissement. Une charge sur une date passée fermée = incohérence comptable.
markNoShows
Section intitulée « markNoShows »Seules les réservations dont checkInDate = businessDate_courante et état Confirmed sont marquées no-show. Les réservations futures restent Confirmed.
Si le depositNature = 'arrhes' (MandateFiscalProfile), les arrhes sont acquises au moment du no-show.
postTourismTax
Section intitulée « postTourismTax »La taxe est calculée depuis le barème compliance-tax (commune, classement, période). Elle est postée comme collected_tax (hors TVA, hors recette). Le reporting lit NightAuditCompleted pour la restituer — la taxe de séjour n’est jamais recalculée dans le reporting (lecture from the event).
Ancrage Merkle WORM (ADR-00D79)
Section intitulée « Ancrage Merkle WORM (ADR-00D79) »À l’étape 6c, la chaîne d’audit est ancrée de manière permanente :
- Calcul de la racine Merkle de toutes les entrées d’audit depuis le dernier ancrage
- Écriture de la racine dans MinIO avec Object Lock COMPLIANCE (durée de rétention : 10 ans)
- La clé d’objet est déterministe (
audit/<tenantId>/<businessDate>) pour garantir l’idempotence en cas de retry - En cas d’échec PUT WORM → le workflow Temporal relance l’étape (retryable)
La vérification de la chaîne est exposée via GET /admin/audit/verification (console admin, pms_control_plane).
NightAuditCompleted
Section intitulée « NightAuditCompleted »L’event publié à la fin du Night Audit (NATS JetStream) :
NightAuditCompleted { tenantId : string (UUID) establishmentId : string (UUID) businessDate : string (YYYY-MM-DD) // date qui VIENT D'ÊTRE CLÔTURÉE roomsOccupied : int32 roomsAvailable : int32 // approché — sans soustraction OOO/OOS (résiduel A) revenueCents : int64 // Σ charges revenue du jour tourismTaxCents : int64 // Σ collected_tax du jour occurredAt : timestamp}3 consumers NATS le consomment pour projeter les KPIs dans pms.reporting_*.
Cas particuliers
Section intitulée « Cas particuliers »Run réconciliation
Section intitulée « Run réconciliation »Si le Night Audit d’un établissement n’a pas tourné pour des raisons techniques, un script de réconciliation reconcile-night-audit.ts permet de le relancer manuellement sur une businessDate spécifique.
Run réel vs tests
Section intitulée « Run réel vs tests »Le Night Audit a été exécuté en run réel (Programme A). Les tests d’intégration utilisent des services réels (Postgres + Temporal), pas de mocks.
Relation avec la businessDate des fronts
Section intitulée « Relation avec la businessDate des fronts »Les fronts consomment la businessDate uniquement depuis l’API :
GET /v1/meretourne la businessDate de l’établissement courantEstablishmentContextServiceinitialise la businessDate au démarrage (APP_INITIALIZER)null= établissement non initialisé → message d’avertissement, pas de fallback surnew Date()
Cela signifie qu’un front peut afficher une businessDate différente de la date système (entre minuit et 3h00 pendant le Night Audit).