Files
app_fideliter/.claude/CLAUDE.md
T
2026-07-18 11:51:32 +02:00

141 lines
7.2 KiB
Markdown

# CLAUDE.md — App Fidélité (tablette magasin)
Guide pour travailler sur ce projet. À lire avant toute modification.
## Vue d'ensemble
Application **Flutter** de **programme de fidélité** pour un magasin, sur
**tablette Android en portrait** (caisse). Chaque client a une carte = un **QR
code** ; le staff scanne, ajoute des **factures** (montant € → **points**), et le
client échange ses points contre des **récompenses**.
Fait partie d'un écosystème de **deux apps** partageant le même backend :
- **`app_fideliter`** (CE repo) : app **tablette / staff**.
- **`app_fideliter_client`** (repo séparé) : app **client** (compte, points, QR).
App sœur `gestion_prix_produit` (autre projet) : même stack et mêmes conventions,
reprises ici. Thème identique mais **accent vert émeraude** (au lieu du bleu) pour
distinguer les apps sur une même tablette.
## Stack technique
- **Flutter** (Material 3), Dart. Cible : Android tablette, portrait verrouillé.
- **PocketBase** comme backend (auto-hébergé sur un **Raspberry Pi**) :
auth + base SQLite + API REST + **temps réel (SSE)**.
- Packages clés :
- `pocketbase` — client backend (auth, CRUD, réaltime)
- `shared_preferences` — persistance de la session (rester connecté)
- `mobile_scanner` — scan du QR client (caméra ; scanette externe à venir)
- `qr_flutter` — affichage/génération du QR d'un client
- `intl` — formatage € et dates (locale `fr_FR`)
- Icône via `flutter_launcher_icons` (source : `assets/icon/`).
## Backend PocketBase
- **URL serveur** : `https://db.tailb756e1.ts.net` (définie dans
[`lib/pocketbase_config.dart`](../lib/pocketbase_config.dart)). Le Raspberry est
exposé **publiquement en HTTPS via Tailscale Funnel** (gratuit) → accessible de
partout (4G, autre WiFi…), pas seulement sur le LAN. En local, le serveur reste
aussi joignable en `http://192.168.1.32:8090`. Tailscale est installé **sur le
Pi uniquement** ; les clients n'installent que l'app. Le Pi doit rester allumé
+ connecté à Internet.
- **Compte staff** : `mathew.simon2004@gmail.com` (mot de passe défini au setup).
Un compte est « staff » si son champ `is_staff = true` dans la collection `users`.
- **Superuser** (admin PocketBase) : géré via l'UI `http://192.168.1.32:8090/_/`.
### Collections
| Collection | Type | Champs principaux | Accès (règles) |
|---|---|---|---|
| `users` | auth | `is_staff` (bool) + email/password | chacun voit son compte ; `is_staff` non modifiable par l'utilisateur |
| `clients` | base | `user` (relation, option.), `code` (unique), `nom`, `prenom`, `telephone`, `points` | staff = tout ; client = sa fiche uniquement |
| `mouvements` | base | `client` (relation), `type` (facture/recompense/ajustement), `montant_euros`, `points`, `libelle`, `created` | staff écrit ; client lit les siens (anti-triche) |
| `recompenses` | base | `nom`, `cout_points`, `actif` | tout connecté lit ; staff modifie |
| `reglages` | base | `euros_par_point`, `nom_magasin` (1 seule ligne) | tout connecté lit ; staff modifie |
### Points de vigilance backend
- **Pas de trigger PocketBase** : le solde `clients.points` est maintenu **par
l'app** (`ClientRepository._ecrireSolde`), qui crée le mouvement PUIS met à jour
le solde. Comme seul le staff écrit, pas de souci de concurrence.
- **Code client** (`FID-XXXXXX`) généré **côté app** (`ClientRepository._genererCode`),
unicité garantie par un index unique + réessais.
- Créer/modifier des collections : voir l'admin UI, ou l'API superuser
(`POST /api/collections/_superusers/auth-with-password` puis `/api/collections`).
## Architecture de l'app
- **Singletons** `.instance` pour les services/repositories.
- **Repositories** `ChangeNotifier` = source de vérité, écoutés via `AnimatedBuilder`.
- **Temps réel** : chaque repository s'abonne (`pb.collection(...).subscribe`) et
met à jour sa liste en mémoire → l'UI se synchronise entre tous les appareils.
- **Auth** : `AuthGate` écoute `pb.authStore.onChange`. Session persistée via
`AsyncAuthStore` (SharedPreferences) → on reste connecté.
### Structure
```text
lib/
├── main.dart # init (locale fr, PocketBase) + thème
├── config.dart # constantes (ratio défaut, préfixe code)
├── pocketbase_config.dart # URL serveur, client `pb`, helper `estStaff`
├── theme.dart # thème noir & blanc + accent vert émeraude
├── models/ # client, mouvement, recompense (fromMap PocketBase)
├── services/
│ ├── reglages.dart # ratio €/point + nom magasin (+ temps réel)
│ ├── client_repository.dart # clients + factures + récompenses + points (+ temps réel)
│ └── recompense_repository.dart# catalogue (+ temps réel)
├── screens/
│ ├── auth_gate.dart # login staff ↔ app selon la session
│ ├── staff_login_screen.dart
│ ├── home_shell.dart # 4 onglets
│ ├── clients_screen.dart # liste + recherche + création
│ ├── client_edit_screen.dart # Nom / Prénom / Téléphone
│ ├── client_detail_screen.dart # solde, facture, récompense, historique (édit/suppr, temps réel)
│ ├── scan_screen.dart # scan QR → fiche client (_ouvrirParCode)
│ ├── recompenses_screen.dart
│ └── reglages_screen.dart # ratio, nom magasin, état serveur, déconnexion
├── widgets/ # pastille_points, qr_client
└── utils/format.dart # euro(), points(), dateHeure() (fr_FR)
```
## Règles métier
- **Points gagnés = montant facture ÷ `euros_par_point`** (fractionnaires, ex.
12 € à 10 €/pt = 1,2 pt).
- Changer le ratio n'affecte **que les nouvelles factures** (pas de recalcul
rétroactif — volontaire).
- Éditer une facture recalcule ses points au **ratio courant** et réajuste le solde.
- Récompenses : gagner **et** dépenser des points (débit à l'utilisation).
## Conventions de code
- **Tout en français** : noms de variables, méthodes, commentaires, libellés UI.
- Reprendre le style existant (singletons, `ChangeNotifier`, `copyWith`, `fromMap`).
- Modèles : `fromMap(record.toJson())``record` est un `RecordModel` PocketBase.
Les relations sont des **id** (string) ; `created` est une date ISO (string).
## Commandes
```bash
flutter pub get
flutter analyze # doit rester à 0 problème
flutter test
flutter build apk --release
# Installer en gardant les données (session conservée) :
adb -s <DEVICE_ID> install -r build/app/outputs/flutter-apk/app-release.apk
adb devices # lister les appareils branchés
```
Icône : `dart run flutter_launcher_icons` après modif de `assets/icon/`.
## Roadmap / à faire
- **Scanette USB externe** : se comporte comme un clavier → brancher un champ
caché qui appelle `scan_screen.dart``_ouvrirParCode` (déjà factorisé).
- **Accès externe** au serveur (hors LAN) : IP/domaine public + **HTTPS** (puis
retirer `usesCleartextTraffic`).
- Impression du QR (carte physique), bonus de bienvenue / paliers, statistiques.
- App client (`app_fideliter_client`) : à aligner sur ce backend PocketBase.