# Phase 10 – Ventes et enchères (bébés et animaux adultes)

**Objectif :** Mise en vente de bébés et d’animaux depuis le camion ; affichage des ventes sur la carte du monde ; expiration des annonces (bébé invendu meurt) ; enchères (offres joueurs/bots), validation par le vendeur, transfert vers l’acheteur (nurserie/accueil), persistance serveur (API/BDD).

**Référence :** `docs/plan-implementation-rappel-grandes-regles.md` phase 10.

## Vérification des phases précédentes

Voir `docs/plan-verification-phases-0-a-9.md`. Les prérequis (4, 6, 7, 9) sont en place.

## Impacts

- Glisser un **bébé mature** (nurserie) ou un **animal prêt** (accueil) sur la zone camion (vente) → l’entité est retirée de la nurserie/accueil et ajoutée à `state.saleListings` ; création côté serveur via `POST /api/sales` ; l’annonce apparaît sur la carte du monde dans la case du zoo joueur.
- Les annonces expirent après un délai configurable (côté client via `tickSaleListings` et côté serveur via `expireSaleListings`) ; si l’annonce concernait un **bébé** et n’a pas été vendue, le bébé est considéré mort (`deathCountRecent` incrémenté côté client et serveur).
- **Enchères :** les joueurs et les bots peuvent enchérir sur les ventes actives (`POST /api/sales/:id/bid`). Le vendeur peut accepter ou refuser la meilleure enchère (`POST /api/sales/:id/accept`, `POST /api/sales/:id/reject`). À l’acceptation, la vente passe en statut `sold` avec un délai de **validation différée de 10 minutes** : les pièces ne sont pas transférées immédiatement ; `validated_at = now() + 10 min`. Un traitement à la lecture (`processValidatedSales`) exécute le transfert (débit acheteur, crédit vendeur) et passe le statut à `validated` lorsque `validated_at <= now()`. L’acheteur ne peut « Récupérer » qu’après validation ; un **sablier** (compte à rebours) s’affiche pour les ventes en attente.

## Modifications

- **config.js** : `Sale.ListingDurationSeconds` (3600), `Sale.DefaultPrice` (50).
- **trade.js** : `addMatureBabyToSale`, `addReceptionAnimalToSale`, `tickSaleListings`.
- **game-loop.js** : appel à `tickSaleListings` après `tickReproduction`.
- **ui.js** : zone camion (drop) crée la vente localement et appelle `createSale` si API configurée ; carte du monde : affichage des ventes (sync avec `getSales()` à l’ouverture) ; panneau « Mes ventes » (Accepter / Refuser), « À récupérer » (Récupérer), « Enchères » (saisie montant + Enchérir).
- **types.js** : `SaleListing` étendu avec `serverId?`, `bestBidAmount?`, `bestBidderZooId?`, `status?` ; `GameState.salesFromApi?` pour les données renvoyées par `GET /api/sales`.
- **api-client.js** : `getSales()`, `createSale(payload)`, `placeBid(listingId, amount)`, `acceptSale(listingId)`, `rejectSale(listingId)`, `deliverSale(listingId)`.
- **Serveur**
  - **server/migrations/001_sale_listings.sql** : tables `sale_listings` (…), **002_sale_listings_validated_at.sql** : colonne `validated_at TIMESTAMPTZ`, statut `validated` ajouté au CHECK.
  - **server/db.js** : `mapSaleListingRow(row)` centralise le mapping des lignes `sale_listings` (utilisé par `getSaleListingById`, `getActiveSaleListings`, `getSalesForZoo`). `mapZooRowBase(row)` pour zoos (getAllZoos, getZooById, getBotZoosForTick). `validateListingForSeller(listingId, sellerZooId)` pour les vérifications vendeur (acceptSale, rejectSale). … `acceptSale` (ne transfère plus les pièces ; pose `sold_at`, `validated_at = now() + 10 min`), `processValidatedSales()` (transfert et passage à `validated`), `markSaleDelivered` (exige `status = 'validated'`).
  - **server/routes/sales.js** : GET `/api/sales` appelle `processValidatedSales()` avant `getSalesForZoo` si auth. Réponses incluent `validated_at`.
  - **server/bot-tick.js** : après `expireSaleListings`, récupération des ventes actives et enchère aléatoire d’un bot (si assez de pièces, pas vendeur).
- **web** : panneau « À récupérer » : sablier et compte à rebours (ex. « Validation dans X min ») pour les ventes `status === 'sold'` avec `validated_at` dans le futur ; bouton « Récupérer » désactivé jusqu’à validation. **texts-fr.js** : `salesPendingValidation`, `salesValidationInMinutes`.
- **texts-fr.js** : `errorMessage.BabyNotMature`, `NoBabyInNursery`, `AnimalNotReady`, `NoAnimalInReception`.
- **web/css/main.css** : styles `.world-map-sales-panel`, `.sales-panel-title`, `.sales-panel-row`, boutons Accepter/Refuser/Récupérer/Enchérir.

## Modalités de déploiement

- **Base de données :** exécuter la migration `server/migrations/001_sale_listings.sql`, puis `server/migrations/002_sale_listings_validated_at.sql` (validation différée).
- Redémarrage serveur et rechargement client.

## Modalités d’analyse

- Glisser un bébé mature / animal prêt sur la zone camion : annonce créée localement et sur le serveur (si API configurée) ; affichage sur la carte du monde.
- Ouvrir la carte du monde avec API + auth : `getSales()` est appelé ; panneau « Mes ventes », « À récupérer », « Enchères » affiché selon les données.
- Enchérir sur une vente active : saisir un montant > meilleure enchère puis « Enchérir ».
- En tant que vendeur : « Accepter » ou « Refuser » sur la meilleure enchère ; à l’acceptation, vente en statut `sold`, `validated_at = now() + 10 min` ; pas de transfert de pièces immédiat.
- En tant qu’acheteur ayant gagné une vente : affichage sablier « Validation dans X min » tant que `validated_at > now()` ; après 10 min, un prochain GET exécute `processValidatedSales`, la vente passe en `validated`, le bouton « Récupérer » s’active ; clic ajoute le bébé/animal à la nurserie/accueil puis appelle `deliverSale`.
- Expiration : après `end_at`, la vente passe en `expired` ; si bébé, `deathCountRecent` du vendeur incrémenté (GET /api/sales ou bot tick).
- Bots : à chaque tick, un bot peut placer une enchère sur une vente active (autre zoo, assez de pièces).

## Phase suivante

Phase 11 – Villes : `docs/features/villes-phase11.md`.
