Un code-barre inconnu scanné propose désormais le même formulaire de création que /materiels au lieu d'un simple message d'erreur. La quantité peut être ajustée d'un delta choisi librement (pas figé à ±1) ou définie directement à une valeur absolue après un inventaire. La racine "/" sert directement la liste des matériels sans redirection. La page Matériels gagne un filtre par catégorie, un filtre stock bas, et un tri incluant un regroupement par catégorie.
119 lines
6.3 KiB
Markdown
119 lines
6.3 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Projet
|
|
|
|
Application web interne pour le département IT d'une clinique privée : gestion
|
|
du stock de matériel informatique (arborescence de catégories + matériels),
|
|
avec recherche de matériel par scan de code-barres et alertes email quand le
|
|
stock passe sous un seuil défini.
|
|
|
|
Contraintes du domaine, volontairement strictes :
|
|
- **Pas d'authentification dans l'app** : l'accès est restreint par un
|
|
mécanisme externe (réseau interne, proxy...). Ne pas ajouter de login/session.
|
|
- **Pas de suivi d'exemplaires individuels.** Un `Materiel` est un *type*
|
|
d'objet (ex: "PC Dell XPS 13 modèle 9310", "Câble RJ45 2m") avec juste une
|
|
quantité en stock. Pas de numéro de série, pas d'historique par unité.
|
|
- **Douchette code-barres = clavier.** Les douchettes USB/Bluetooth émulent un
|
|
clavier (elles tapent le code + Entrée). Aucune intégration matérielle
|
|
n'est nécessaire côté app : un simple `<input>` seul dans un `<form>`
|
|
suffit, le navigateur soumet au Entrée.
|
|
- **Catégories à profondeur illimitée.** `Categorie` est auto-référencée
|
|
(`parent_id`), pas un modèle Catégorie/Sous-catégorie à deux niveaux fixes :
|
|
une catégorie peut avoir des sous-catégories qui en ont elles-mêmes, sans
|
|
limite (ex: Réseau > Câbles > RJ45 > 2m). Voir `Categorie.chemin()`.
|
|
- **Listes potentiellement longues.** Les pages Matériels/Catégories/
|
|
Destinataires sont conçues pour rester utilisables avec beaucoup
|
|
d'éléments : recherche texte côté serveur (`?q=`), lignes repliées par
|
|
défaut (`<details>`), jamais de tableau avec un `<input>` par colonne
|
|
affiché sur toutes les lignes à la fois (ça déborde horizontalement).
|
|
|
|
## Stack
|
|
|
|
Python + FastAPI + SQLModel (ORM) + SQLite (fichier `data/stock.db`) +
|
|
Jinja2 (rendu HTML côté serveur, formulaires classiques, pas de bundler JS).
|
|
|
|
Tout vit dans ce dossier : pas de service externe requis pour développer et
|
|
tester en local. Les emails d'alerte sont simulés dans la console tant
|
|
qu'aucun SMTP n'est configuré (voir `app/email_alerts.py` et `.env.example`).
|
|
|
|
## Commandes
|
|
|
|
```bash
|
|
source .venv/bin/activate # créer avec: python3 -m venv .venv
|
|
pip install -r requirements-dev.txt
|
|
uvicorn app.main:app --reload # lancer le serveur de dev (http://127.0.0.1:8000)
|
|
pytest # lancer les tests
|
|
ruff check . # linter
|
|
```
|
|
|
|
Les tests utilisent une base SQLite **en mémoire** (voir `tests/conftest.py`),
|
|
jamais le fichier `data/stock.db` du développement.
|
|
|
|
## Convention : commentaires détaillés
|
|
|
|
Contrairement à la préférence par défaut (commentaires minimaux), ce projet
|
|
doit rester compréhensible par d'autres membres du département IT qui n'ont
|
|
pas suivi son développement. **Commenter en français, de façon détaillée**,
|
|
en particulier le "pourquoi" des décisions non évidentes (ex: pourquoi la
|
|
douchette ne nécessite pas d'intégration, pourquoi les emails sont simulés en
|
|
l'absence de SMTP_HOST). Ne pas se limiter au commentaire minimal habituel.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
app/
|
|
main.py point d'entrée FastAPI, montage des routers
|
|
config.py lecture des variables d'environnement (SMTP...)
|
|
database.py connexion SQLite + dependency get_session()
|
|
models.py modèles SQLModel : Categorie (arborescence auto-référencée), Materiel, DestinataireAlerte
|
|
categories_arbre.py parcours de l'arborescence (racines, aplatissement pour un <select> indenté)
|
|
email_alerts.py vérifie le seuil d'alerte et envoie/simule l'email
|
|
templates_engine.py instance Jinja2Templates partagée par tous les routers
|
|
routers/ une route FastAPI par ressource (categories, materiels, scan, destinataires)
|
|
templates/ pages HTML (Jinja2, formulaires HTML classiques)
|
|
static/ CSS
|
|
tests/ pytest + TestClient FastAPI + base SQLite en mémoire
|
|
data/ base SQLite locale, ignorée par git
|
|
```
|
|
|
|
Point d'attention Starlette : `Jinja2Templates.TemplateResponse()` prend
|
|
`request` en premier argument positionnel (`templates.TemplateResponse(request,
|
|
"page.html", {...})`), **pas** `{"request": request, ...}` dans le contexte —
|
|
c'est l'API de la version de Starlette installée ici.
|
|
|
|
Les listings (`materiels.html`, `categories.html`) utilisent `<details>`/
|
|
`<summary>` pour replier les lignes/branches par défaut plutôt qu'un
|
|
tableau tout-en-input : c'est ce qui évite le débordement horizontal et
|
|
garde la page utilisable avec beaucoup d'éléments. Le formulaire d'édition
|
|
complet d'un matériel ne s'affiche que dans la ligne dépliée (voir
|
|
`.formulaire-edition` dans `style.css`), pas sur toutes les lignes en
|
|
permanence.
|
|
|
|
`/materiels` (aussi servie sur `/`, voir `main.py`) combine recherche
|
|
(`?q=`), filtre par catégorie — elle et toutes ses sous-catégories, via
|
|
`ids_sous_arbre()` — filtre stock bas (`?stock_bas=1`) et tri (`?tri=`),
|
|
tous combinables. `tri=categorie` trie par chemin de catégorie puis nom,
|
|
ce qui rend les matériels d'une même catégorie consécutifs : le
|
|
regroupement visuel (en-têtes `.groupe-categorie-entete`) vient de ce tri,
|
|
pas d'une logique de regroupement séparée à maintenir.
|
|
|
|
L'écran de scan (`/scan`) gère 3 cas après un scan : matériel trouvé
|
|
(étiquette + contrôles de stock), code inconnu (formulaire de création
|
|
identique à celui de `/materiels`, avec le code-barre pré-rempli, pour
|
|
créer sans changer de page), ou matériel venant d'être créé (même rendu
|
|
que "trouvé"). Deux façons distinctes d'agir sur le stock, volontairement
|
|
séparées pour ne jamais laisser d'ambiguïté sur ce qu'un nombre représente :
|
|
`/scan/{id}/ajuster` applique un delta choisi (pas fixé à ±1) via un champ
|
|
« sens » (ajouter/retirer), `/scan/{id}/definir` fixe la quantité absolue
|
|
(utile après un inventaire physique).
|
|
|
|
## Emails d'alerte
|
|
|
|
Seuil configurable par matériel (`Materiel.seuil_alerte`), liste globale de
|
|
destinataires (`DestinataireAlerte`, gérée sur `/destinataires`). La logique
|
|
d'envoi est dans `app/email_alerts.py` : si `SMTP_HOST` n'est pas défini, rien
|
|
n'est envoyé, l'email est juste affiché dans la console du serveur — pratique
|
|
pour tester le déclenchement des alertes sans serveur mail réel.
|