# Phase 24 : Programme Fidélité Avancé

**Priorité :** P2
**Complexité :** MOYENNE
**Routes :** ~6
**Entités :** 2

---

## Objectif

Étendre le système de points existant (`Customer.loyaltyPoints`, `PosSettings.enableLoyalty`) avec paliers (Bronze/Silver/Gold/Platinum), récompenses échangeables, et historique.

---

## Entités

### `LoyaltyTier` (Palier)
Table : `pos_loyalty_tier`

| Champ | Type | Description |
|-------|------|-------------|
| id | int PK | |
| company | FK Company CASCADE | |
| name | string 50 | Bronze, Silver, Gold, Platinum |
| minPoints | int | Seuil minimum pour atteindre ce palier |
| pointsMultiplier | decimal 5,2, default 1.00 | Multiplicateur de gain (ex: 1.5x pour Gold) |
| discountPercent | decimal 5,2, default 0 | Remise automatique sur chaque achat |
| color | string 20 | Pour badges UI |
| position | int | Ordre (1=Bronze, 2=Silver, etc.) |
| isActive | bool default true | |
| TimestampableEntity | | |

### `LoyaltyReward` (Récompense)
Table : `pos_loyalty_reward`

| Champ | Type | Description |
|-------|------|-------------|
| id | int PK | |
| company | FK Company CASCADE | |
| name | string 100 | Ex: "Remise 5000 FCFA", "Produit offert" |
| description | text nullable | |
| pointsCost | int | Combien de points pour obtenir |
| type | string 20 | `discount_percent`, `discount_fixed`, `free_product` |
| value | decimal 12,2 | Montant remise ou prix produit offert |
| targetProductId | int nullable | Si type=free_product, ID du produit offert |
| isActive | bool default true | |
| maxRedemptions | int nullable | null = illimité |
| currentRedemptions | int default 0 | |
| TimestampableEntity | | |

---

## Modification entité existante

### `Customer.php`
Ajouter :
- `loyaltyTier` FK LoyaltyTier SET NULL nullable — palier actuel

---

## Service

### `LoyaltyService`

**`calculateTier(Customer, Company): ?LoyaltyTier`** — détermine le palier selon les points actuels

**`updateCustomerTier(Customer, Company): void`** — recalcule et assigne le bon palier

**`getPointsMultiplier(Customer, Company): string`** — retourne le multiplicateur du palier actuel

**`getAutoDiscount(Customer, Company): string`** — retourne la remise auto du palier

**`getAvailableRewards(Customer, Company): array`** — récompenses dont le client a assez de points

**`redeemReward(Customer, LoyaltyReward, User): void`** — déduire points, incrémenter compteur

**`getRewardHistory(Customer): array`** — via PosAuditLog

---

## Intégration existante

### `SaleService.createSale()`
Après la vente :
1. `CustomerService.recordPurchase()` existe déjà (ajoute points)
2. Ajouter : appliquer le `pointsMultiplier` du palier
3. Ajouter : appliquer la `discountPercent` du palier comme remise globale
4. Ajouter : recalculer le palier si les points ont changé

### Interface caissier
- Afficher palier du client (badge couleur)
- Afficher points disponibles
- Bouton "Utiliser récompense" si disponible

---

## Routes Manager (~6)

| Route | Méthode | Path | Voter |
|-------|---------|------|-------|
| `manager_pos_loyalty_tiers` | GET/POST | `/manager/pos/loyalty/tiers` | POS_MANAGE |
| `manager_pos_loyalty_tier_edit` | GET/POST | `/manager/pos/loyalty/tiers/{id}` | POS_MANAGE |
| `manager_pos_loyalty_tier_delete` | POST | `/manager/pos/loyalty/tiers/{id}/delete` | POS_MANAGE |
| `manager_pos_loyalty_rewards` | GET/POST | `/manager/pos/loyalty/rewards` | POS_MANAGE |
| `manager_pos_loyalty_reward_edit` | GET/POST | `/manager/pos/loyalty/rewards/{id}` | POS_MANAGE |
| `manager_pos_loyalty_reward_delete` | POST | `/manager/pos/loyalty/rewards/{id}/delete` | POS_MANAGE |

---

## Templates (2)
- `loyalty/tiers.html.twig` — CRUD paliers sur une seule page (liste + formulaire inline)
- `loyalty/rewards.html.twig` — CRUD récompenses (liste + formulaire)

## Menu
- Ajouter "Fidélité" après "Clients" dans `_menu_manager.html.twig`
