# Logique Metier

## Vue d'ensemble

La logique metier est repartie en plusieurs couches complementaires :

```
Controleur
  |-> Action (logique transactionnelle, effets de bord)
  |-> Service (integration externe, calcul complexe)
  |-> Observer (side effects automatiques sur cycle de vie Eloquent)
  |-> Event -> Listener (workflow event-driven)
  |-> Job (traitement asynchrone en file d'attente)
  |-> Notification (communication utilisateur)
```

---

## 1. Actions (`app/Actions/`)

Les Actions encapsulent la logique metier transactionnelle. Chaque action est une classe invokable (`__invoke`) avec une responsabilite unique.

### Booking

#### CreateAttendeeBookingAction
**Fichier :** `app/Actions/Booking/CreateAttendeeBookingAction.php`

Cree une reservation avec validation complete :
1. Verifie que le lieu est ouvert (`CheckIfPlaceIsOpenAction`)
2. Verifie que l'attendee n'a pas de conflit de planning (`BusyAttendeeException`)
3. Verifie que le coach est disponible (`BusyCoachException`)
4. Verifie les slots maximum (`MaximumSlotsException`)
5. Gere les linked services (services lies a une activite)
6. Debite le wallet immediatement (credits/daypass/guest_pass)
7. Cree le Booking en base
8. Declenche les notifications (push + email)

#### CancelAttendeeBookingAction
**Fichier :** `app/Actions/Booking/CancelAttendeeBookingAction.php`

Soft-delete de la reservation. Le remboursement est gere separement.

#### RefundAttendeeBookingAction
**Fichier :** `app/Actions/Booking/RefundAttendeeBookingAction.php`

Rembourse le wallet de l'utilisateur :
- Detecte automatiquement le type de transaction (credits, guest_pass, daypass)
- Gere les annulations tardives (`late_cancellation`)
- Cree un WalletTransaction de type CREDIT ou CREDIT_ADJUSTMENT
- Marque le booking comme `refunded`

---

### Course (Disponibilites)

#### DetermineAvailablePersonalCoursesAction
**Fichier :** `app/Actions/Course/DetermineAvailablePersonalCoursesAction.php`

Determine les creneaux disponibles pour les cours personnels :
- Croise les disponibilites du coach avec les bookings existants
- Filtre par guest/kid/recreational area selon le contexte
- Retourne des `SlotData` avec coach, horaire, pricing

#### DetermineAvailableWellnessCoursesAction
Meme logique pour les cours wellness.

#### DetermineAvailableServicesAction / DetermineAvailableActivitiesAction
Determine les creneaux pour services/activites :
- Prend en compte la capacite (`capacity`)
- Gere les slots de 30 min (`half_hour`)
- Gere les slots simultanes (`simultaneous_slots`)
- Prend en compte les fermetures exceptionnelles (`ExceptionalClosingHour`)
- Prend en compte les horaires d'ouverture du service

---

### Availability

#### ComputeAvailabilityStatsAction
**Fichier :** `app/Actions/Availability/ComputeAvailabilityStatsAction.php`

Pre-calcule et met en cache les statistiques de disponibilite :
- Agrege par lieu, categorie, service, activite
- Compte les creneaux disponibles par type d'orderable
- Stocke dans `AvailabilityStat` pour acces rapide par l'app mobile

#### SyncAvailabilitiesAction
**Fichier :** `app/Actions/Availability/SyncAvailabilitiesAction.php`

Synchronise les creneaux de disponibilite d'un coach :
- Recoit un tableau de dates/heures
- Supprime les anciens creneaux non utilises
- Cree les nouveaux creneaux
- Wrap dans une transaction DB

---

### DoorAccess

#### GenerateUserAccessAction
**Fichier :** `app/Actions/DoorAccess/GenerateUserAccessAction.php`

Genere les acces physiques (QR codes, cartes BioStar2) :
- Cree un utilisateur BioStar2 si necessaire
- Genere un QR code unique
- Met en cache la session pour eviter les doublons
- Whiteliste/blackliste l'utilisateur dans le systeme de portes

#### PurgeCustomerDoorAccessAction
Supprime les acces d'un customer (hard delete).

#### PurgeCoachAndMemberDoorAccessAction
Supprime les acces coach/membre (soft delete pour maintenir le statut).

#### PurgeAllDoorAccessForUserAction
Purge complete pour suppression de compte.

---

### User

#### CreateUserAction
**Fichier :** `app/Actions/User/CreateUserAction.php`

Cree un utilisateur customer :
- Hash du mot de passe
- Definition langue par defaut
- Envoi notification de bienvenue (Sendinblue)
- Liaison automatique des guest invitations en attente

#### DeleteUserAction
**Fichier :** `app/Actions/User/DeleteUserAction.php`

Supprime un utilisateur avec verifications :
- Empeche la suppression si abonnement actif (`CustomerMemberCannotDeleteAccountException`)
- Empeche la suppression si bookings futurs (`CustomerHasFutureBookingsException`)

#### BuyPackAction
**Fichier :** `app/Actions/User/BuyPackAction.php`

Achat de pack via le desk (paiement sur place) :
- Cree une commande avec statut PAID
- Provider = DESK
- Declenche l'event OrderPaid

#### CreateUserMembershipAction
**Fichier :** `app/Actions/User/CreateUserMembershipAction.php`

Cree un abonnement :
- Deduit le wallet
- Supporte les cadeaux (`is_gift`)
- Supporte les unites familiales
- Pricing multi-tier (normal, membre, resort_money)

---

### Order

#### UpsertOrderAction
**Fichier :** `app/Actions/UpsertOrderAction.php`

Creation/modification atomique de commande :
- **Retry logic** : 3 tentatives en cas de deadlock DB
- Auto-creation de daypasses pour non-membres
- Deduplication : empeche les doublons de daypass pour le meme jour
- Calcul automatique des prix, taxes et credits
- Gestion multi-items polymorphiques

---

### Shop Manager (`app/Actions/ShopManager/`)

#### AdjustStockAction
**Fichier :** `app/Actions/ShopManager/AdjustStockAction.php`

Ajuste le stock d'une variation de produit :
- Cree un `ProductVariationStock` entry (+/-)
- Logue l'activite sur le Product model (Spatie ActivityLog)
- Stocke variation_id et reason dans les properties

#### ProcessPosPaymentAction
**Fichier :** `app/Actions/ShopManager/ProcessPosPaymentAction.php`

Traite un paiement TPE (POS) :
- Valide les montants et la photo de recu
- Cree la WalletTransaction avec type `CREDIT_CARD`
- Met a jour le statut de la commande a PAID avec provider `POS`
- Declenche l'event `OrderPaid`

#### ProcessCashPaymentAction
**Fichier :** `app/Actions/ShopManager/ProcessCashPaymentAction.php`

Traite un paiement en especes :
- Valide les montants, la monnaie rendue, et la photo de recu
- Cree la WalletTransaction avec type `CASH`
- Met a jour le statut de la commande a PAID avec provider `CASH`
- Declenche l'event `OrderPaid`

#### CreateQuickCustomerAction
**Fichier :** `app/Actions/ShopManager/CreateQuickCustomerAction.php`

Creation rapide d'un client pour associer a un cart :
- Cree un User avec les infos minimales (nom, email, phone)
- Cree un Wallet automatiquement
- Retourne le User pour liaison au cart

---

### Place

#### CheckIfPlaceIsOpenAction
**Fichier :** `app/Actions/Place/CheckIfPlaceIsOpenAction.php`

Verifie que le lieu est ouvert a une date/heure donnee :
- Compare avec `opening_hours` (JSON)
- Prend en compte le fuseau horaire du lieu
- Lance `PlaceClosedException` si ferme

---

### GuestInvitation

#### LinkGuestInvitationUserAction
Lie une invitation guest a un utilisateur enregistre (par email).

#### LinkUserGuestInvitationsAction
Lie en masse les invitations en attente a un utilisateur nouvellement inscrit.

---

## 2. Services (`app/Services/`)

### AccessValidationService
**Fichier :** `app/Services/Access/AccessValidationService.php`

Service central de validation des acces QR :
- Valide les membres (abonnement actif)
- Valide les day passes (valide pour aujourd'hui)
- Valide les guest passes
- Valide les invitations guest
- Valide les formulaires pre-arrivee
- Gestion timezone-aware
- Enregistre les logs d'acces dans `PlaceAccessLog`
- Retourne un `AccessValidationResult` avec statut et details

### BioStar2Service
**Fichier :** `app/Services/BioStar2/BioStar2Service.php`

Integration avec le systeme de controle d'acces physique BioStar2 :
- Creation d'utilisateurs dans BioStar2
- Gestion de QR codes (creation, mise a jour, suppression)
- Ajout/suppression de cartes d'acces
- Whitelisting/Blacklisting
- Gestion des expirations

### Chat Services

#### AiService
**Fichier :** `app/Services/Chat/AiService.php`

Orchestrateur de conversations IA :
- Cree des sessions IA dans les conversations
- Route les messages vers le bon driver (BoldAI, OpenAI, Mistral, Claude)
- Gere l'historique des messages IA

#### Drivers IA (`app/Services/Chat/Drivers/`)

| Driver | Fichier | Description |
|--------|---------|-------------|
| `BoldAiDriver` | `BoldAiDriver.php` | Integration BoldAI (contacts, messages, webhooks) |
| `OpenAiDriver` | `OpenAiDriver.php` | OpenAI GPT chat completions |
| `MistralDriver` | `MistralDriver.php` | Mistral AI avec gestion historique |
| `ClaudeDriver` | `ClaudeDriver.php` | Anthropic Claude API |
| `SelfHostedDriver` | `SelfHostedDriver.php` | Placeholder pour IA auto-hebergee |

Tous implementent `AiDriverInterface` :
```php
interface AiDriverInterface {
    public function createConversation(Conversation $conversation): void;
    public function sendMessage(Conversation $conversation, string $message): string;
}
```

#### ChatUnreadService
Compte les messages non lus par lieu et par conversation avec gestion complexe des accuses de reception.

#### MistralIntentClient
Client Mistral AI avec interpretation d'intentions et appel de fonctions (function calling). Configure avec 25+ outils dans `config/chat.php` pour le coaching, booking et paiement.

### GoogleGeocoderService
**Fichier :** `app/Services/GoogleGeocoder/GoogleGeocoderService.php`

Integration Google Places API :
- Recherche d'adresses (autocomplete)
- Recuperation de details d'adresse
- Parsing des coordonnees geographiques
- Retourne des `GooglePlaceData` DTOs

### PresenceResolver
**Fichier :** `app/Services/Presence/PresenceResolver.php`

Determine le statut de presence d'un utilisateur/invite :
- `NOT_SCHEDULED` : Aucune reservation aujourd'hui
- `NOT_ARRIVED` : Reservation mais pas encore arrive
- `ARRIVED` : Present (access log confirme)
- `LEFT` : Parti (deuxieme access log)

Timezone-aware, se base sur les access logs et les bookings.

### Shop Manager Services (`app/Services/ShopManager/`)

#### DashboardService
**Fichier :** `app/Services/ShopManager/DashboardService.php`

Service utilitaire central pour les widgets du dashboard :
- `resolvePeriod()` : Convertit les periodes (today, 7d, 14d, 30d, custom) en dates Carbon
- `previousPeriod()` : Calcule la periode precedente equivalente pour comparaisons
- `paidOrdersQuery()` : Query de base pour les commandes PAID d'un lieu sur une plage de dates
- `sparkline()` : Genere des data points quotidiens (count ou sum)
- `percentageChange()` : Calcul du % de variation entre deux valeurs

#### DashboardCacheService
**Fichier :** `app/Services/ShopManager/DashboardCacheService.php`

Service de cache Redis pour le dashboard :
- **Pre-compute a minuit** : `warmCache()` pre-calcule tous les widgets pour toutes les periodes (today, 7d, 14d, 30d) avec TTL 24h
- **Delta intra-jour** : `remember()` cache les resultats avec TTL 5 min (2 min pour recent-activity et inventory-alerts)
- **Custom bypasse le cache** : Les periodes custom ne sont pas cachees
- **Invalidation** : `invalidate()` purge tout le cache d'un lieu
- **Commande schedule** : `shop:dashboard:warm-cache` (quotidien a minuit)
- Calcule : revenue, checkout-snapshot, sales-performance (par metric), best-sellers, top-categories, recent-activity, inventory-alerts

### QuickBooks Services

#### QuickbooksService
**Fichier :** `app/Services/Quickbooks/QuickbooksService.php`

Service principal d'integration QuickBooks :
- Synchronisation des clients, articles, comptes, transactions
- Gestion de 15+ jobs de synchronisation
- Interface avec `QuickbooksClient` pour les appels API

#### QuickbooksClient
**Fichier :** `app/Services/Quickbooks/QuickbooksClient.php`

Client HTTP bas-niveau pour QuickBooks :
- Gestion OAuth2 avec refresh automatique des tokens
- Creation, mise a jour, requetes d'entites
- Classification des erreurs (retryable, fatal, etc.)
- Retry automatique sur erreurs transitoires

#### QuickbooksCircuitBreaker
**Fichier :** `app/Services/Quickbooks/QuickbooksCircuitBreaker.php`

Pattern Circuit Breaker :
- Seuil : 5 echecs consecutifs
- Timeout de recuperation : 5 minutes
- Empeche les appels QuickBooks quand le circuit est ouvert
- Re-teste automatiquement apres le timeout

#### QuickbooksRateLimiter
Limite a 500 requetes/minute par realm QuickBooks avec tracking par fenetre temporelle.

### Translation Services

#### AutoTranslateService
**Fichier :** `app/Services/Translate/AutoTranslateService.php`

Traduction automatique par lot :
- Batch de 2 chaines par defaut
- Support de plusieurs providers (Google Translate, OpenAI)
- Traduction entre toutes les langues supportees

#### Providers
- `GoogleTranslateProvider` : Utilise l'API Google Translate
- `OpenAIProvider` : Utilise GPT pour les traductions

---

## 3. Jobs (`app/Jobs/`)

### Notifications

| Job | Description | Frequence |
|-----|-------------|-----------|
| `NotifyCustomerBeforeCourseJob` | Rappel 30 min avant le cours | Toutes les 15 min |
| `NotifyPlaceCoordinatorsBeforeServiceJob` | Notification coordinateurs 30 min avant | Toutes les 15 min |
| `NotifyMaintenanceAfterServiceJob` | Notification maintenance apres service | Post-service |

### QuickBooks Sync (13 jobs)

Tous dans `app/Jobs/Quickbooks/` :

| Job | Description | Queue |
|-----|-------------|-------|
| `SyncQuickbooksCustomerJob` | Sync un client vers QuickBooks | quickbooks-customers |
| `SyncQuickbooksCustomersJob` | Sync batch de clients | quickbooks-customers |
| `SyncQuickbooksItemJob` | Sync un article | quickbooks-catalog |
| `SyncQuickbooksAccountItemJob` | Sync un compte article | quickbooks-accounts |
| `SyncQuickbooksProductJob` | Sync un produit | quickbooks-catalog |
| `SyncQuickbooksAccountsJob` | Sync les comptes | quickbooks-accounts |
| `SyncQuickbooksCoachAccountJob` | Sync compte coach | quickbooks-accounts |
| `SyncQuickbooksWalletTransactionJob` | Sync transaction wallet | quickbooks-wallet |
| `SyncQuickbooksTransactionJob` | Sync une transaction | quickbooks |
| `SyncQuickbooksTransactionsJob` | Sync batch transactions | quickbooks |
| `SyncQuickbooksCatalogJob` | Sync catalogue complet | quickbooks-catalog |

**Concerns partages :**
- `ConfiguresQuickbooksQueue` : Configuration de la queue QuickBooks
- `HandlesQuickbooksCircuitBreaker` : Integration du circuit breaker dans les jobs

---

## 4. Events & Listeners

### OrderPaid Event

**Fichier :** `app/Events/OrderPaid.php`

Declenche quand une commande passe au statut PAID. C'est l'event central du systeme de paiement.

**Listeners :**

| Listener | Description |
|----------|-------------|
| `CreateBookingWhenOrderConfirmed` | Cree les bookings a partir des order items via les BookingFactories |
| `CreditWalletWhenPackOrderConfirmed` | Credite le wallet pour les packs (credits, daypasses, guest_passes) |
| `FulfillFlexMembershipWhenOrderPaid` | Cree l'abonnement et credite le wallet pour les flex memberships |
| `UpdateWalletWhenStripeOrderConfirmed` | Double debit/credit pour les items Stripe non-pack |

### Chat Events

| Event | Description | Canal Broadcasting |
|-------|-------------|-------------------|
| `MessageSent` | Message envoye dans une conversation | `conversation.{id}`, `place.{id}` |
| `MessageDeleted` | Message supprime | `conversation.{id}` |
| `MessageReceiptUpdated` | Accuse reception mis a jour | `conversation.{id}` |
| `Typing` | Indicateur de saisie | `conversation.{id}` |

### Listener Media

| Listener | Description |
|----------|-------------|
| `GenerateMediaBlurHash` | Genere un blurhash pour les images uploadees (Spatie Media) |
| `Chat/SendMessageToBoldAi` | Forward les messages chat vers BoldAI et cree les messages IA |

---

## 5. Observers (`app/Observers/`)

Les observers reagissent automatiquement aux evenements du cycle de vie Eloquent.

### BookingObserver
**Fichier :** `app/Observers/BookingObserver.php`

| Evenement | Actions |
|-----------|---------|
| `created` | Dispatch rappels (customer + coach), genere door access, lie guest users, notifie coordinateurs, genere QR codes |
| `updated` | Met a jour les acces si changement de creneau |
| `deleting` | Purge les acces physiques lies |

### UserObserver
**Fichier :** `app/Observers/UserObserver.php`

| Evenement | Actions |
|-----------|---------|
| `created` | Cree un wallet, lie les guest invitations en attente, dispatch sync QuickBooks |
| `deleted` | Purge tous les acces physiques |

### MembershipObserver
**Fichier :** `app/Observers/MembershipObserver.php`

| Evenement | Actions |
|-----------|---------|
| `created` | Genere les acces physiques pour la duree de l'abonnement |
| `updated` | Met a jour les acces si dates modifiees |
| `deleted` | Purge les acces ou maintient si bookings existants |

### CoachObserver
Lie les places du coach, met a jour les attributs de recherche.

### WalletTransactionObserver
Synchronise les transactions vers QuickBooks.

### FamilyObserver
Synchronise les donnees famille vers QuickBooks.

### AvailabilityObserver
Met a jour les stats de disponibilite quand un coach modifie ses creneaux.

### ExceptionalClosingHourObserver
Cascade les suppressions et mises a jour de disponibilite.

### ActivityObserver, ServiceObserver, GroupCourseObserver, EventObserver
Mettent a jour les attributs de recherche et les stats de disponibilite.

### TrackingEntryObserver
Gestion des evenements de presence.

---

## 6. Notifications (`app/Notifications/`)

### Par Email (Sendinblue)

| Notification | Declencheur | Template |
|-------------|-------------|----------|
| `CustomerWelcomeNotification` | Inscription | Email bienvenue |
| `CustomerNewBookingNotification` | Booking cree | Confirmation reservation |
| `CustomerNewDaypassNotification` | Daypass achete | Confirmation daypass |
| `CustomerNewGuestPassNotification` | Guest pass achete | Confirmation guest pass |
| `CustomerCancelGuestPassNotification` | Guest pass annule | Confirmation annulation |
| `CustomerOtpNotification` | Demande OTP | Code OTP |
| `ResetPasswordCodeNotification` | Reset password | Code reinitialisation |
| `CustomerDeletedNotification` | Suppression compte | Confirmation |
| `CustomerPhysicalAssessmentRequestNotification` | Demande evaluation | Notification |
| `CustomerReportRequestNotification` | Demande rapport | Notification |
| `MemberImportWelcomeNotification` | Import membre | Bienvenue import |
| `HospitalityWelcomeNotification` | Bienvenue hospitality | Email specifique |

### Push Notifications (Expo)

Toutes heritent de `BaseAppNotification` dans `app/Notifications/App/`.

| Notification | Declencheur |
|-------------|-------------|
| `CoachNewBookingNotification` | Nouveau booking pour un coach |
| `CoachBookingReminderNotification` | Rappel booking coach |
| `CancelBookingNotification` | Annulation de booking |
| `CoordinatorNewBookingNotification` | Nouveau booking (coordinateur) |
| `CoordinatorBookingCanceledNotification` | Annulation (coordinateur) |
| `CustomerCourseReminderNotification` | Rappel cours client |
| `CustomerBookingReminderNotification` | Rappel booking client |
| `ServiceUsageReminderNotification` | Rappel utilisation service |
| `ServiceUsedNotification` | Service utilise |
| `NewEventNotification` | Nouvel evenement |
| `NewGroupCourseNotification` | Nouveau cours collectif |
| `PaymentSuccessNotification` | Paiement reussi |
| `NewTrackingEntryNotification` | Nouvelle entree suivi |
| `ChatNotification` | Nouveau message chat |
| `ChatDataMessage` | Message data chat (silencieux) |
| `AdminCustomNotification` | Notification admin personnalisee |
| `CancelEventNotification` | Annulation evenement |
| `CancelGroupCourseNotification` | Annulation cours collectif |

### Autres Notifications

| Notification | Type |
|-------------|------|
| `PlanningNotification` | Rappel planning coach (quotidien) |
| `AccessLogsNotification` | Resume des acces du jour |
| `AdminMembershipExpiryNotification` | Alerte expiration abonnement |
| `PreArrivalFormConfirmationNotification` | Confirmation formulaire pre-arrivee |
| `GuestInvitationNotification` | Invitation guest |

---

## 7. Commandes Console (`app/Console/Commands/`)

### Scheduler (Kernel.php)

| Commande | Frequence | Description |
|----------|-----------|-------------|
| `model:prune` | Toutes les 15 min | Nettoyage modeles expires |
| `customer:booking:remind` | Toutes les 15 min | Rappels bookings clients |
| `wallettransactions:description:fix` | Toutes les 15 min | Correction descriptions wallet |
| `availability:compute` | Toutes les 15 min | Calcul stats disponibilites |
| `coach:booking:remind` | Quotidien 19:00 | Rappels planning coaches |
| `membership:expiry:remind` | Hebdo lundi 07:00 | Rappels expiration abonnements |
| `guestpass:reset` | Mensuel 1er 00:00 | Reset des guest passes |
| `planning:notify {uuid}` | Quotidien 08:00 | Notification planning |
| `access:logs:notify {uuid}` | Quotidien 23:59 | Resume acces du jour |
| `quickbooks:recover-stuck` | Horaire | Recovery jobs QuickBooks bloques |
| `shop:dashboard:warm-cache` | Quotidien 00:00 | Pre-compute cache Redis dashboard Shop Manager |

### Commandes Manuelles

| Commande | Description |
|----------|-------------|
| `sync:boldai:users` | Sync utilisateurs vers BoldAI |
| `import:members` | Import de membres depuis CSV |
| `import:users` | Import d'utilisateurs |
| `fix:categories` | Correction categories |
| `purge:courses` | Purge des anciens cours |
| `adjust:hours` | Ajustement horaires |
| `translations:import` | Import traductions POEditor |
| `translations:sync` | Sync traductions |
| `translations:cache:clear` | Vider cache traductions |
| `door:access:purge` | Purge acces portes |
| `bookings:set-refund-status` | Definir statut remboursement |
| `bookings:place:grant-access` | Accorder acces lieu |
| `quickbooks:sync:recover` | Recovery sync QuickBooks |
| `ai:conversations:cleanup` | Nettoyage conversations IA |

---

## 8. Enums (`app/Enums/`)

### Enums Metier

| Enum | Valeurs | Usage |
|------|---------|-------|
| `OrderableType` | personal_course, wellness_course, group_course, service, activity, place, pack, product, flex_membership, event | Type d'element commandable |
| `OrderStatus` | WAITING_PAYMENT, PAID, CANCELLED | Statut de commande |
| `PaymentProvider` | STRIPE, WALLET, NOWPAYMENTS, DESK, FREE | Fournisseur de paiement |
| `WalletTransactionType` | DEBIT, CREDIT, DEBIT_ADJUSTMENT, CREDIT_ADJUSTMENT | Type de transaction |
| `PackType` | CREDIT, DAYPASS, GUEST_PASS, MEMBERSHIP | Type de pack |
| `Gender` | MALE, FEMALE, OTHER | Genre |
| `UserType` | CUSTOMER, COACH, EMPLOYEE | Type d'utilisateur |
| `ServiceType` | gym, co_working, spa, pool, etc. | Type de service |
| `DoorSystem` | none, biostar2 | Systeme de porte |
| `TaskStatus` | pending, in_progress, done | Statut de tache |
| `PresenceStatus` | NOT_SCHEDULED, NOT_ARRIVED, ARRIVED, LEFT | Statut de presence |
| `Currency` | USD, EUR, AED, SAR, etc. | Devises supportees |

### Enums Chat

| Enum | Valeurs |
|------|---------|
| `AiDriver` | bold, claude, openai, mistral, self_hosted |
| `MessageType` | text, attachment |
| `ReceiptStatus` | delivered, read |
| `ConversationType` | direct, group, ai |
| `AttachmentType` | image, video, audio, file |

### Traits d'Enums

- `HasEnumStaticMethods` : Methodes utilitaires (values(), names(), toArray())
- `HasOptions` : Methode `options()` pour les selects Filament

---

## 9. Exceptions Metier (`app/Exceptions/`)

| Exception | Declencheur |
|-----------|-------------|
| `BusyAttendeeException` | Client a deja un booking au meme creneau |
| `BusyCoachException` | Coach deja occupe au creneau demande |
| `CoachNotWorkInThisPlaceException` | Coach non rattache au lieu |
| `CategoryNotAttachedToCoachException` | Categorie non liee au coach |
| `PlaceClosedException` | Lieu ferme au creneau demande |
| `NoServiceAvailableException` | Aucun service disponible |
| `MaximumSlotsException` | Capacite maximale atteinte |
| `CustomerHasFutureBookingsException` | Client a des bookings futurs (suppression impossible) |
| `CustomerMemberCannotDeleteAccountException` | Membre actif (suppression impossible) |
| `EnsureFlexMembershipIsActive` | Flex membership inactif |
| `OrderAlreadyPaidException` | Commande deja payee |
| `OrderCantBeEditedException` | Commande non modifiable |
| `InsufficientWalletBalanceException` | Solde wallet insuffisant |
| `NotFoundUserDoorPassException` | Pass porte introuvable |
| `NotFoundWalletException` | Wallet introuvable |
| `OrderableConstraintFailedException` | Contrainte d'orderable non respectee |

### Architecture des Exceptions

Les exceptions metier heritent de `ApiException` qui implemente `Responsable` :
- Chaque exception a un code metier (ex: `BUSY_ATTENDEE`, `PLACE_CLOSED`)
- Les exceptions `silenced = true` ne sont pas reportees a Sentry
- Le Handler convertit automatiquement en reponse JSON

---

## 10. BookingFactories (`app/BookingFactories/`)

Le pattern Factory est utilise pour creer des bookings selon le type d'orderable lors du paiement.

### Interface

```php
interface BookingFactoryInterface
{
    public function create(OrderItem $item, Order $order): Booking;
}
```

### Implementations

| Factory | Orderable | Specifite |
|---------|-----------|-----------|
| `ActivityBookingFactory` | Activity | Validation des slots de service lies |
| `GroupBookingFactory` | GroupCourse | Verification de capacite |
| `ServiceBookingFactory` | Service | Parsing des metadata de creneau |
| `PersonalCourseBookingFactory` | PersonalCourse | Verification disponibilite coach |
| `WellnessCourseBookingFactory` | WellnessCourse | Similaire a PersonalCourse |
| `PlaceBookingFactory` | Place | Booking de lieu (daypass, resort access) |
| `EventBookingFactory` | Event | Verification capacite evenement |

**Declenchement :** Le listener `CreateBookingWhenOrderConfirmed` selectionne la bonne factory selon le `orderable_type` de chaque `OrderItem`.

---

## 11. OrderableConstraints (`app/OrderableConstraints/`)

Les contraintes d'orderable valident et transforment les items de commande en pipeline.

### Pattern Pipeline

```php
// Chaque modele Orderable definit ses contraintes
public function orderableConstraints(): array
{
    return [
        new RequiresMetadata(['start_at', 'end_at']),
        new MustBeTodayOrFuture(),
        new TransformPublicIdToId(),
        new ActivityMustBeAvailable(),
    ];
}
```

### Classe de base

```php
abstract class OrderableConstraint
{
    // Valide l'item (leve une exception si invalide)
    abstract public function check(OrderItem $item): bool;

    // Transforme l'item (ex: convertit public_id -> id)
    public function mutate(OrderItem $item): void {}

    // Execute uniquement lors de l'ajout (pas les updates)
    public function onlyWhenAdding(): bool { return false; }
}
```

### Contraintes existantes

| Contrainte | Description |
|-----------|-------------|
| `RequiresMetadata` | Verifie la presence des champs requis dans metadata |
| `MustBeTodayOrFuture` | Empeche les reservations dans le passe |
| `TransformPublicIdToId` | Convertit les public_id en id internes |
| `TransformDateToDatetimeRange` | Parse les dates en plages start_at/end_at |
| `PlaceMustBeOpen` | Verifie les horaires d'ouverture |
| `InjectPackCredits` | Calcule les credits pour les packs |
| `ActivityMustBeAvailable` | Verifie la disponibilite de l'activite |
| `ServiceMustBeAvailable` | Verifie la disponibilite du service |
| `PersonalCourseCoachMustBeAvailable` | Verifie la disponibilite du coach |
| `EnsureFlexMembershipIsActive` | Verifie que le flex membership est actif |

---

## 12. DTOs — Spatie Data (`app/Data/`)

Les Data Transfer Objects utilisent Spatie LaravelData pour la validation et la serialisation.

| DTO | Description |
|-----|-------------|
| `OrderItemData` | Item de commande (orderable_type, id, metadata, guest/kid flags) |
| `SlotData` | Creneau horaire avec capacite disponible |
| `ActivityData` | Activite avec collection de slots disponibles |
| `PersonalCourseData` | Cours perso avec coach et pricing |
| `WellnessCourseData` | Cours wellness avec coach et pricing |
| `ServiceData` | Service avec creneaux |
| `SearchResultData` | Resultat de recherche multi-modele |
| `PlanningEntryData` | Entree de planning coach |

---

## 13. Packages Personnalises (`storage/packages/yieldstudio/`)

### eloquent-public-id

Gestion des UUID publics pour les modeles Eloquent :
- **HasPublicId** trait : auto-genere un UUID ordonne a la creation, masque l'ID interne, fournit `findByPublicId()`
- **ConvertPublicId** : Convertisseur pour le route model binding
- **NotFoundModel** : Exception 404 quand le public_id n'existe pas

### laravel-expo-notifier

Systeme de push notifications pour Expo (React Native) :
- **ExpoNotificationsService** : Envoi et batch de notifications push
- **ExpoNotificationsChannel** : Canal Laravel Notification
- **Commandes** : `SendPendingNotifications`, `CheckTickets`
- **Storage MySQL** : Tokens, tickets en attente, notifications
- **Event InvalidExpoToken** + **Listener DeleteInvalidExpoToken** : Nettoyage auto des tokens invalides

### laravel-sendinblue-notifier

Integration Sendinblue pour emails et SMS transactionnels :
- **SendinblueService** : Client HTTP pour l'API Sendinblue
- **SendinblueEmailChannel** : Canal email (templates Sendinblue avec parametres)
- **SendinblueSmsChannel** : Canal SMS
- **SendinblueEmailMessage / SendinblueSmsMessage** : Builders de messages avec template ID, destinataire, parametres
