# Pension — CRM canin Symfony

Application en français pour gérer une pension canine, avec une plateforme multi-pension disponible. Symfony 7.4, PHP 8.2 ou supérieur et Twig ; aucune compilation JavaScript nécessaire.

## Plateforme multi-pension

**Production sur `backmapension.bouillot.dev` avec MySQL : suivre [la procédure complète](DEPLOYMENT.md).** Elle couvre l’hébergement, le DNS, HTTPS, les bases, le premier compte et l’import des données locales.

Pour tester le nouveau mode avec super administrateur et sous-domaines :

```sh
php bin/platform-local
```

La première exécution demande votre compte super administrateur. Ouvrir **http://backmapension.localhost:8080/plateforme**, changer le mot de passe provisoire puis activer la double authentification. Créer ensuite les pensions depuis cette administration ; chaque pension dispose de son adresse et de ses comptes de personnel.

Ce démarrage utilise des stockages JSON séparés pour les tests sans installation supplémentaire. Le déploiement accepte **MySQL/MariaDB ou PostgreSQL**, avec une base et un utilisateur distincts par pension ainsi qu’une base pour le registre global. Sur un hébergement MySQL, les bases sont créées dans le panneau de l’hébergeur puis renseignées lors de la création des pensions. Les données historiques ne sont pas déplacées automatiquement. Voir [le guide de démarrage, migration et sauvegarde](PLATFORM.md).

## Démarrage du CRM historique à une pension

Depuis le dossier du projet :

```sh
composer install
php bin/console app:demo  # facultatif : exemples fictifs, uniquement si les données sont vides
php bin/console app:user:create-admin  # à faire une seule fois pour créer votre accès
php -S 127.0.0.1:8000 -t public public/router.php
```

Ouvrir **http://127.0.0.1:8000**. Arrêter le serveur avec `Ctrl+C`. Les dépendances sont déjà installées sur ce poste.

Extensions PHP utilisées : ctype, fileinfo, GD, iconv, mbstring, session, XML (déjà disponibles sur ce poste). Symfony documente les prérequis sur https://symfony.com/doc/7.4/setup.html.

## Fonctionnement

1. Créer un **client** : nom et prénom obligatoires, adresse, téléphone et e-mail. Recherche et modification depuis la liste.
2. Créer un **chien** : nom, numéro de puce, race, consignes de promenade et de nourriture. Dans le module **Maîtres**, rechercher un client par nom, prénom, téléphone ou e-mail (au moins deux caractères), puis cliquer sur **Ajouter**. Les résultats sont limités à 20 : affiner la recherche si nécessaire. Les maîtres associés sont affichés séparément et peuvent être retirés. Les changements sont sauvegardés avec **Enregistrer la fiche**. Un maître peut avoir plusieurs chiens. Le numéro de puce est facultatif, mais unique lorsqu’il est renseigné.
3. Sur la fiche du chien, ajouter les **vaccins**, les traitements **anti-puces / tiques** et les **vermifuges** avec leur produit, leur date et un commentaire. Le carnet est trié par date décroissante ; une saisie incorrecte peut être modifiée ; les valeurs antérieures sont conservées dans l’historique.
4. Créer une **réservation** : rechercher puis sélectionner le chien par nom, race, numéro de puce ou nom de maître (au moins deux caractères), puis renseigner les dates d’arrivée et de départ, matin ou soir, commentaire. Vous pouvez ajouter plusieurs chiens un par un, ou cliquer sur **Ajouter les chiens** d’une famille dans les résultats. Une famille regroupe les chiens rattachés au même maître, sans rapprochement automatique par nom de famille. Les mêmes dates et le commentaire sont appliqués à tous les chiens sélectionnés ; un séjour est conservé par chien. Dans la liste des réservations et la vue d’ensemble, les chiens partageant un maître et les mêmes dates / demi-journées sont regroupés sur une seule ligne, y compris pour les réservations existantes. Un bouton confirme l’arrivée ou le départ de tous les chiens de cette ligne, avec le même instant pour les confirmations effectuées ensemble. Les chiens aux dates différentes restent séparés. Si un chien est indisponible, aucun séjour du lot n’est créé et la sélection est conservée pour correction. Une réservation existante peut se modifier chien par chien, ou via **Modifier la réservation familiale** pour appliquer en une fois les dates, demi-journées et le commentaire à tous les chiens de la ligne. Les demi-journées des deux extrémités sont incluses ; un séjour peut durer une seule demi-journée. Deux réservations d’un même chien ne peuvent pas partager une demi-journée. Des chiens différents peuvent être réservés en même temps.
5. Consulter le **planning** : sept jours à partir de la date sélectionnée, colonnes matin / soir et nombre de chiens prévu. Les couleurs reflètent le statut actuel du séjour. Le planning conserve les dates prévues même après les confirmations.
6. Cliquer sur **Confirmer l’arrivée**, puis **Confirmer le départ**, depuis la vue d’ensemble, les réservations ou la fiche du chien. Pour une famille, la confirmation s’applique à tous les chiens encore concernés ; une confirmation individuelle déjà enregistrée est conservée. Le départ commun demande que tous soient arrivés. Si un chien a un autre séjour en cours, la confirmation commune d’arrivée est refusée sans modification partielle. Chaque confirmation enregistre l’instant du clic, à la seconde, affiché en heure de Paris. Les confirmations répétées et le départ avant l’arrivée sont refusés. En cas d’erreur, **Annuler l’arrivée** remet le chien en attente ; **Annuler le départ** le remet en présence tout en conservant son heure d’arrivée. Ces boutons existent aussi pour la famille entière et dans les détails individuels. Un dialogue demande confirmation avant l’annulation. Pour annuler une arrivée après un départ, annuler d’abord le départ. Une annulation de départ est refusée si elle rendrait le chien présent dans deux séjours à la fois ; pour une famille, aucun chien n’est modifié si un seul est en conflit. L’accueil avant la date prévue reste possible pour enregistrer une arrivée anticipée réelle.

La vue d’ensemble distingue les chiens réellement présents des arrivées et départs prévus aujourd’hui. Un séjour commencé reste modifiable pour le départ prévu et le commentaire ; le chien, l’arrivée prévue et les heures de confirmation sont conservés. Il ne peut pas être supprimé. Une réservation non commencée peut être modifiée ou annulée. Un chien possédant des séjours et un maître encore lié à un chien sont protégés contre la suppression.

## Données locales

Les données sont enregistrées dans **`var/data/pension.json`**, hors du dossier public et exclues de Git. Ce choix permet de tester immédiatement sans l’extension SQLite, absente du PHP de ce poste. Le stockage possède un verrou partagé pour la lecture, exclusif pour les modifications, et remplace le fichier atomiquement après validation. Aucun appel à un service externe n’est nécessaire dans l’interface.

Sauvegarde : arrêter le serveur et copier ce fichier ainsi que ses dossiers `.documents` et `.photos` s’ils existent. Pour repartir de zéro, arrêter le serveur, sauvegarder si nécessaire, puis supprimer uniquement `var/data/pension.json`. Ne pas supprimer ce fichier pour simplement redémarrer l’application : les données sont conservées entre les lancements.

Le chemin peut être changé avec la variable d’environnement `DATA_FILE` (chemin absolu recommandé). L’accès est protégé par une connexion personnelle, même en local. Pour un hébergement public, utiliser HTTPS, configurer `APP_ENV=prod` et un `APP_SECRET` aléatoire, et servir uniquement le dossier `public/` avec un serveur web et PHP-FPM. Le stockage JSON avec verrouillage reste prévu pour une seule instance ; une base relationnelle sera nécessaire si plusieurs serveurs doivent partager les données. Garder l’écoute sur `127.0.0.1` pour les tests.

## Vérification

```sh
composer test
php bin/console lint:twig templates
php bin/console lint:yaml config
```

Le test crée un stockage temporaire isolé puis le supprime. Il vérifie les formulaires HTTP Symfony, les relations entre maîtres et chiens, les soins, les validations, les chevauchements, les transitions de présence, la persistance, les protections CSRF et les pages vides / remplies. Vos fiches locales ne sont pas modifiées.

## Tarifs et prix des séjours

Dans **Tarifs**, renseigner le prix total journalier pour un chien, puis les prix pour les familles de 2, 3 chiens, etc. **Ajouter un tarif familial** permet de définir autant de tailles de famille que nécessaire. Le tarif pour un chien est obligatoire ; une ligne vide n’est pas enregistrée. Les montants sont en euros, positifs ou nuls, avec au maximum deux décimales. Les tarifs sont conservés dans le même fichier local que les fiches.

La colonne **Prix du séjour** dans les réservations et la vue d’ensemble indique le total, le nombre de jours facturables et le tarif appliqué. Pour une ligne familiale, le tarif est le montant total par jour pour le nombre de chiens de cette ligne, sans multiplication supplémentaire par ce nombre. Un tarif non défini est signalé par **Tarif à renseigner** ; aucun tarif de remplacement n’est inventé. Dans la fiche individuelle d’un chien, un montant familial est explicitement indiqué comme total de la famille.

Les demi-journées d’arrivée et de départ sont incluses : une présence sur une seule demi-journée vaut 0,5 jour ; vendredi soir → dimanche soir vaut 5 demi-journées, soit 2,5 jours. Le calcul utilise les jours civils et reste identique lors des changements d’heure. Les calculs monétaires sont effectués en centimes, avec un unique arrondi au centime sur le total.

Les prix sont calculés avec les **dates prévues** et les **tarifs actuels**, pour les réservations existantes comme nouvelles, y compris celles terminées. Modifier les tarifs recalcule donc les montants affichés ; confirmer ou annuler une arrivée ou un départ ne change pas les dates prévues ni le prix. Ce montant n’est pas une facture figée.

## Prestations supplémentaires

Depuis la colonne du prix du séjour, cliquer sur **Prestations**. Ajouter un libellé, un tarif unitaire en euros et une quantité entière positive. Chaque montant est calculé en centimes : tarif × quantité. Les lignes peuvent être modifiées ou supprimées depuis cette page, même si le séjour a commencé ou est terminé.

Le total affiché sur la réservation inclut **pension + prestations** et précise les deux sous-totaux. Pour une famille, chaque prestation est comptée une seule fois sur la réservation regroupée ; augmenter la quantité si le service est facturé pour plusieurs chiens. Les prestations sont rattachées à un séjour et restent conservées si les dates ou le commentaire sont modifiés ; si la famille est séparée en plusieurs lignes de réservation, la prestation suit le séjour auquel elle est rattachée.

Les prestations sont enregistrées dans le même fichier local que les réservations. Les prix saisis acceptent une virgule ou un point, au maximum deux décimales ; une prestation gratuite est possible. Si le tarif de pension n’est pas renseigné, le sous-total des prestations reste visible mais le total complet est signalé comme incomplet.

## Modifier toute une réservation familiale

Dans la colonne **Confirmations**, cliquer sur **Modifier la réservation familiale**, même après les confirmations d’arrivée. Le formulaire affiche tous les chiens concernés et permet de changer ensemble les dates, demi-journées et le commentaire. Dès qu’un chien est arrivé, l’arrivée prévue de toute la famille est figée ; le départ prévu et le commentaire restent modifiables. Les instants d’arrivée et de départ déjà confirmés sont toujours conservés, même après le départ. Les prestations sont conservées et le montant du séjour est recalculé.

La case **Appliquer ce commentaire à tous les chiens** permet de remplacer les commentaires en commun. Si les commentaires actuels diffèrent, ils sont affichés et la case est décochée par défaut pour les conserver lors d’un simple changement de dates.

Les changements sont enregistrés en une seule écriture : un conflit pour un chien refuse la modification entière. Un formulaire devenu obsolète (réservation modifiée ou confirmation effectuée entre-temps) est également refusé ; recharger la page avant de réessayer. La modification individuelle reste accessible dans **Gérer les chiens séparément**.

## Photo du chien et appareil photo mobile

La fiche du chien comporte une photo avec **Choisir une photo**, **Prendre une photo** et **Enregistrer la photo**. Le deuxième bouton demande l’appareil photo arrière sur les navigateurs mobiles compatibles ; certains navigateurs proposent plutôt leur sélecteur habituel. Un aperçu apparaît avant l’enregistrement. La photo peut être remplacée ou supprimée sans changer la fiche.

Formats acceptés : JPEG, PNG et WebP, maximum 12 Mo / 24 millions de pixels. Le navigateur réduit la photo lorsque possible avant son envoi. Le serveur valide et réencode l’image en JPEG, corrige son orientation et limite son côté le plus long à 1600 pixels. Les noms des fichiers d’origine ne sont pas utilisés. Les formats HEIC non lisibles par le navigateur doivent être exportés en JPEG.

Les photos sont conservées dans **`var/data/pension.json.photos/`**, hors du dossier public. Sauvegarder ce dossier avec `var/data/pension.json`. Un autre `DATA_FILE` utilise son propre dossier `<DATA_FILE>.photos`. Les photos restent conservées lorsqu’on modifie les informations du chien.

Pour accepter des photos originales plus volumineuses en cas d’absence de compression du navigateur, démarrer avec :

```sh
php -d upload_max_filesize=12M -d post_max_size=16M -S 127.0.0.1:8000 -t public public/router.php
```

Pour ouvrir le CRM sur un téléphone, placer le téléphone et le PC sur le même Wi-Fi, arrêter le serveur local puis utiliser :

```sh
php -d upload_max_filesize=12M -d post_max_size=16M -S 0.0.0.0:8000 -t public public/router.php
```

Sur le téléphone, ouvrir `http://ADRESSE_IP_LOCALE_DU_PC:8000` (l’adresse du PC peut être consultée avec `hostname -I`). Cette écoute donne accès au CRM depuis le réseau local ; la connexion personnelle reste obligatoire. Le HTTP local est réservé aux tests sur un réseau de confiance ; en ligne, utiliser HTTPS. Revenir à `127.0.0.1` pour des tests uniquement sur le PC.

## Installations, personnel et suivi

Le menu **Personnel** permet de créer les membres de l’équipe (prénom, nom, fonction), de modifier leur fiche et de les archiver ou réactiver. Seules les personnes actives peuvent être choisies lors d’une nouvelle intervention. Le nom de la personne au moment de l’intervention est conservé dans l’historique, même si sa fiche change ensuite.

Le menu **Installations** permet de créer et modifier les installations avec un nom et des consignes, sans type ni catégorie. La liste affiche le dernier nettoyage et les dernières mesures de température et d’humidité, avec leurs dates, heures et intervenants.

Sur une installation, choisir **Nettoyage**, **Température**, **Humidité** ou **Température et humidité**, sélectionner la personne, puis renseigner la date et l’heure de réalisation (heure de Paris). Les mesures utilisent °C et %. Les virgules et points sont acceptés, avec deux décimales maximum ; l’humidité doit être entre 0 et 100 %, la température entre −100 et 100 °C. Il est possible de saisir une intervention passée ; une date future est refusée. Un commentaire permet d’indiquer le produit de nettoyage ou les observations.

L’historique est trié par date de réalisation et peut être filtré par type d’intervention et par personne. Il affiche 25 entrées par page. **Annuler la saisie** permet de corriger une erreur sans effacer l’entrée : elle reste signalée comme annulée et n’est plus utilisée comme dernière mesure / dernier nettoyage.

Archiver une installation conserve son historique et bloque les nouvelles interventions jusqu’à sa réactivation. Les installations, le personnel et les suivis sont enregistrés dans le même fichier JSON local ; les données antérieures restent compatibles, sans réinitialisation du CRM.

### Compléments issus du livret ACACED

- **Clients** : contact mandaté (identité, adresse, téléphone/e-mail), personnes autorisées au retrait.
- **Chiens** : naissance, sexe, stérilisation, robe et signes particuliers, puce ou tatouage, catégorie, poids/taille, chaleurs/gestation, vétérinaire, allergies, maladies, antécédents et précautions. Consignes structurées de repas, compatibilité, peurs, fugue, manipulation et sorties.
- **Carnet de santé** : prochaine échéance renseignée d’après le carnet / vétérinaire et référence du justificatif ; modification possible des anciens soins. Les alertes retiennent le soin le plus récent pour un même type et nom de produit.
- **Réservations → Suivi du chien** : conditions de garde, provenance/destinataire, contact mandaté, vétérinaire convenu, alimentation/hébergement, affaires confiées, installation affectée et état général à l’entrée/sortie. Le préremplissage vient du client et du chien, puis est enregistré pour ce séjour. Il est conservé lors des modifications individuelles ou familiales.
- **Journal individuel** : observations, soins, incidents, appels/interventions vétérinaires, isolement sanitaire, hébergement individuel pour comportement et décès. Date/heure et personnel sont obligatoires ; les trois derniers événements exigent un motif / une cause. Impression du suivi individuel ou enregistrement en PDF depuis le navigateur.
- **Traitements** : médicament, posologie prescrite, horaires, période, ordonnance ou référence. Chaque prise réellement effectuée conserve dose, date/heure et personne. Une administration doit correspondre à la période prescrite et à la présence réelle du chien. Aucune posologie n’est calculée. Les erreurs sont annulées avec un motif, tout en restant visibles.
- **Documents** : PDF/JPEG/PNG/WebP, contrôlés par leur contenu, maximum 12 Mo ; stockage privé à côté du JSON dans `var/data/pension.json.documents`. Cartes d’identification, carnets, ordonnances, évaluations comportementales, permis, assurances, contrats signés, règlement et visites sanitaires. Un contrat signé global est rattaché au chien, indépendamment de ses séjours. Les nouvelles pièces sont ajoutées sans écraser les anciennes.
- **Contrats signés** : depuis la fiche du chien, ajouter le contrat global signé à la première garde, avec libellé, date de signature et référence / informations modifiées. Ajouter un nouveau document lors d’un changement : les anciennes versions restent accessibles. La génération et l’impression de contrats par réservation ont été supprimées. Les anciennes pièces signées déjà enregistrées restent consultables sur les chiens concernés.
- **Paramètres** : coordonnées/SIRET de la pension, vétérinaire sanitaire, capacité et procédures d’hygiène, nettoyage/désinfection, isolement et urgence. Les documents de la pension peuvent contenir le règlement validé, les comptes rendus signés et leurs versions.
- **Installations** : surface utile, capacité et protocole (toujours sans type d’installation) ; interventions distinctes de nettoyage, désinfection, lutte contre les nuisibles et vide sanitaire, avec produit/protocole, anomalie et action corrective. Les limites de température/humidité sont des validations de saisie, pas des seuils sanitaires prescrits.
- **Personnel** : date et référence ACACED/diplôme, échéance d’actualisation renseignée depuis les justificatifs.
- **Registres et alertes** : échéances proches/dépassées, échéances pendant les séjours, pièces à vérifier, contrat global signé manquant sur le chien, traitements en cours et dépassement de la capacité déclarée. L’absence d’alerte ne certifie pas la validité des documents ; l’ajout d’un nouveau justificatif ne signifie pas que son contenu a été vérifié.

### Historique, archives et sauvegardes

Chaque modification réalisée dans l’application conserve les valeurs avant/après et son horodatage dans un historique accessible depuis **Registres et alertes**. Le personnel déclaré pour les événements et prises est enregistré avec son nom au moment de la saisie. Pour les nouvelles saisies web, l’historique conserve aussi le compte et le nom de la personne connectée, indépendamment de l’intervenant sélectionné. Les anciennes saisies et les opérations depuis le terminal n’ont pas d’auteur connecté. Les hachages des mots de passe sont exclus de cet historique et les comptes sont exclus des archives destinées au contrôle.

Le bouton **Créer une archive du CRM** crée un JSON numéroté, daté, avec une empreinte SHA-256, contenant les données et leur historique. Le fichier est protégé en écriture au niveau du système de fichiers et ne peut pas être modifié depuis l’interface. Une alerte apparaît lorsqu’aucune archive n’existe ou que la dernière a plus de six mois. Une commande permet d’automatiser cette opération avec un planificateur local :

```bash
php bin/console app:archive --if-due
```

Exemple de tâche cron quotidienne à adapter à votre installation (la commande ne crée une archive que lorsqu’elle est due) :

```cron
0 3 * * * cd /home/benoit/PhpstormProjects/CMR_pension && /usr/bin/php bin/console app:archive --if-due
```

Le projet n’installe pas cette tâche cron. Pour sauvegarder réellement le CRM, copier **le JSON, son dossier `.documents` et son dossier `.photos`**, idéalement vers un autre support. Les archives JSON référencent les pièces jointes avec leurs empreintes mais n’incorporent pas leurs fichiers. Les anciennes données restent compatibles ; les modifications antérieures à cette évolution n’ont pas d’historique rétrospectif.

L’export CSV des mouvements est une aide à la préparation des déclarations, **sans transmission automatique à I-CAD** et sans remplacement de son registre officiel. Les fonctions locales d’historique/archivage ne constituent pas une certification de conformité réglementaire. Les contrats signés sont à conserver au moins cinq ans après le départ ; les volumes du suivi sanitaire au moins trois ans après leur dernière inscription (arrêté du 19 juin 2025, articles 27 et 9).

Vérification des nouveaux parcours : `php tests/welfare.php` (données isolées dans `/tmp`, sans modification des fiches réelles).

### Dossier de contrôle : PDF et justificatifs réunis

Le menu **Dossier de contrôle** prépare les documents à présenter à partir du livret ACACED fourni (pages PDF 8, 44–46, 49–51) et du [vade-mecum d’inspection DGAL du 04/03/2026](https://agriculture.gouv.fr/telecharger/129301). Il couvre la pension canine ; les pièces spécifiques à d’autres activités (élevage, vente, transport, etc.) ne sont pas présumées applicables.

- Laisser les dates vides pour inclure tout l’historique, y compris les chiens sans séjour. Une période sélectionne les séjours dont la présence réelle (ou les dates prévues pour une réservation non commencée) chevauche la période, ainsi que les séjours concernés par un soin / événement dans cette période. Les interventions et journaux sont filtrés par date de réalisation. Les documents permanents de la pension et de tout le personnel restent inclus, ainsi que toutes les versions des justificatifs des chiens sélectionnés. Les archives antérieures jointes sont intégrales et peuvent donc contenir des données hors de la période sélectionnée.
- **Télécharger tout le dossier (ZIP)** réunit neuf catégories de rapports PDF : sommaire / pièces à vérifier, établissement / procédures, personnel / qualifications, installations / nettoyage / ambiance, mouvements, suivi sanitaire, fiches chiens / contrats signés, inventaire des pièces, historique des corrections. Les grands rapports du ZIP sont répartis en parties de 100 entrées.
- Le ZIP contient aussi les fichiers PDF/images originaux, les archives JSON disponibles, un état JSON des données sélectionnées, les mouvements et les points à compléter en CSV, et un inventaire des fichiers avec empreintes SHA-256. Les contrats restent les contrats signés globaux des chiens ; aucune génération de contrat n’est réintroduite.
- Chaque rapport se télécharge séparément en PDF ou se consulte / s’imprime depuis le navigateur. Les PDF sont générés localement avec Dompdf, sans appel extérieur. PHP doit disposer de l’extension `zip` pour le téléchargement complet (disponible sur ce poste).
- Les fichiers absents, illisibles ou dont l’empreinte a changé sont signalés et exclus du ZIP. Le dossier reste exportable lorsqu’il manque des pièces, avec leur liste explicite. Il n’invente pas de justificatifs ni de signatures et n’atteste pas de la conformité de l’établissement.

Depuis **Documents de la pension**, ajouter les pièces délivrées / signées : immatriculation, déclaration d’activité / récépissé, BNO / capacité, désignation vétérinaire, règlement sanitaire et visites, exports du registre des mouvements et de transmission sanitaire I-CAD lorsqu’ils sont disponibles. Les autres catégories permettent de joindre plans des installations, dossier ICPE si concerné, plans de nettoyage / nuisibles, procédures de déchets, urgence / évacuation, maintenance sécurité et anciens rapports de contrôle. **Personnel → Justificatifs / ACACED** permet de joindre les attestations et actualisations de chaque personne, y compris celles du personnel archivé. « Autre document » accueille toute pièce complémentaire demandée.

Le registre des entrées / sorties est déjà obligatoire, sur papier ou support informatique offrant des garanties équivalentes de contrôle. Sa tenue dans I-CAD est prévue à partir du 1er janvier 2029 ; la transmission sanitaire à I-CAD à partir du 1er janvier 2027 (article 34 de l’arrêté du 19 juin 2025). Une copie / preuve I-CAD peut être jointe au CRM pour faciliter le contrôle, sans obligation distincte de pièce jointe. Les informations de santé, relevés et procédures exportés reflètent les saisies disponibles ; il reste nécessaire de fournir les justificatifs externes et de vérifier leur validité et leur applicabilité.

Tests du dossier : `php tests/inspection.php`. Les données fictives sont isolées dans `/tmp`. Les exports temporaires sont supprimés après téléchargement ; la préparation d’un dossier ne modifie pas les fiches ni les archives existantes.

Le guide d’inspection prévoit des pièces contractuelles pour chaque garde. Les contrats globaux des chiens sont exportés avec leurs versions ; vérifier que les documents signés couvrent les dates et conditions de chaque séjour. La catégorie **Accord / annexe signé(e) du séjour** permet de joindre les pièces correspondantes depuis les documents de la réservation, sans génération de contrat.

### Utilisation sur téléphone

L’interface adapte les listes en fiches à partir de 760 px, avec un menu repliable, des champs lisibles sans zoom automatique et des boutons adaptés au toucher. Le planning affiche le jour sélectionné sur téléphone et la semaine complète sur ordinateur. Les recherches de clients/chiens, les confirmations familiales, les pièces jointes et la prise de photo restent accessibles. Sans JavaScript, le menu reste visible et les tableaux peuvent être parcourus horizontalement.

Dans « Paramètres », renseignez le nom de la pension et ajoutez son logo dans la section dédiée. Le nom et le logo apparaissent sur toutes les pages du CRM, sur ordinateur et téléphone. Les logos PNG, JPEG et WebP sont réencodés en PNG (transparence conservée), limités à 12 Mo et redimensionnés à 800 px maximum. Ils sont stockés dans le dossier privé `.photos`, à inclure dans les sauvegardes. Remplacer ou retirer le logo conserve les coordonnées et procédures de la pension.


## Connexion et accès du personnel

Aucun compte ou mot de passe par défaut n’est créé. Le premier administrateur se crée depuis un terminal interactif :

```sh
php bin/console app:user:create-admin
```

La commande demande l’e-mail, le prénom, le nom et un mot de passe provisoire saisi sans affichage. Pour utiliser une fiche personnel existante : `php bin/console app:user:create-admin --staff=IDENTIFIANT`. Les identifiants figurent dans le lien de modification des fiches. La première connexion impose le remplacement du mot de passe provisoire ; reconnectez-vous ensuite.

Dans **Personnel → Gérer l’accès**, un administrateur crée le compte de chaque personne, choisit son e-mail de connexion, un mot de passe provisoire, son autorisation d’accès et son rôle. Un administrateur gère les comptes, les fiches du personnel et les paramètres ; les membres du personnel utilisent les fonctions quotidiennes du CRM. Chaque personne active peut avoir un seul compte. Archiver sa fiche ou désactiver son accès bloque la connexion et révoque ses sessions. Le dernier administrateur actif est protégé contre la désactivation, l’archivage et le retrait de son rôle.

Le personnel connecté est présélectionné pour les interventions d’installation, les événements sanitaires et les administrations de médicaments. On peut sélectionner un autre intervenant lorsqu’on retranscrit son travail ; l’auteur connecté de la saisie reste conservé dans l’historique. Une sélection renvoyée avec une erreur de formulaire est conservée.

**Mon mot de passe** permet de modifier son mot de passe après vérification du mot de passe actuel. Les mots de passe comportent au moins 12 caractères et sont hachés par Symfony (algorithme `auto`). Les connexions sont limitées à 5 tentatives sur 15 minutes par identifiant et adresse IP, avec un plafond supplémentaire par IP et un verrou contre les requêtes simultanées. Le changement de mot de passe est également limité. Les formulaires de connexion, déconnexion et modification sont protégés par CSRF. La session est renouvelée à la connexion, expire après 30 minutes sans activité et au plus après 8 heures. Un changement des droits ou du mot de passe révoque les sessions existantes. Aucun cookie de connexion permanente n’est utilisé.

Un responsable réinitialise un mot de passe depuis **Gérer l’accès**. En cas de perte du mot de passe administrateur, utiliser le terminal du serveur :

```sh
php bin/console app:user:reset-password votre@email.fr
```

Le nouveau mot de passe provisoire est demandé sans affichage et devra être changé à la connexion. Il n’y a pas de réinitialisation publique par e-mail ni d’inscription libre.

### Configuration lors de la mise en ligne

Utiliser un serveur web configuré pour le domaine et HTTPS (rediriger HTTP vers HTTPS avant PHP), avec `public/` comme racine documentaire. Ne pas exposer `var/`, `config/`, `vendor/` ou les sauvegardes. Le serveur intégré `php -S` sert uniquement aux tests. Définir `APP_ENV=prod`, `DATA_FILE` vers un fichier privé accessible au compte PHP, et `APP_SECRET` avec une valeur aléatoire privée générée par exemple avec `php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;'`. Le mode production refuse de démarrer sans secret configuré et impose les cookies HTTPS et la redirection HTTPS. Le mode local génère un secret privé persistant dans `var/.app-secret` ; ne pas le publier.

Installer les dépendances, préparer le cache et exécuter les contrôles avant publication :

```sh
composer install --no-dev --optimize-autoloader
APP_ENV=prod php bin/console cache:clear
APP_ENV=prod php bin/console cache:warmup
```

Ces commandes supposent que `APP_SECRET` et `DATA_FILE` sont déjà définis dans l’environnement. Derrière un proxy HTTPS, renseigner `TRUSTED_PROXIES` uniquement avec ses adresses connues (séparées par des virgules), pour que Symfony reconnaisse le protocole HTTPS et l’adresse du client sans accepter les en-têtes envoyés par n’importe qui. Conserver les caches de limitation des tentatives et les sessions hors du dossier public. Les cookies sont `HttpOnly`, `SameSite=Lax` et `Secure` en HTTPS ; les réponses privées ne sont pas mises en cache. La protection suppose un hébergement configuré et maintenu ; les comptes n’ajoutent pas d’authentification à deux facteurs.

Les tests `tests/security.php` vérifient le blocage des accès anonymes (y compris pièces et exports), les connexions réelles, CSRF, les droits, la révocation/expiration des sessions, les mots de passe provisoires, la limitation des tentatives, la présélection des personnes et l’absence de hachages dans les archives de contrôle. Les anciennes suites se connectent avec un compte temporaire isolé, sans désactiver le pare-feu de l’application.
