7.2 KiB
7.2 KiB
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 clientintl— formatage € et dates (localefr_FR)
- Icône via
flutter_launcher_icons(source :assets/icon/).
Backend PocketBase
- URL serveur :
https://db.tailb756e1.ts.net(définie danslib/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 enhttp://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 champis_staff = truedans la collectionusers. - 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.pointsest 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-passwordpuis/api/collections).
Architecture de l'app
- Singletons
.instancepour les services/repositories. - Repositories
ChangeNotifier= source de vérité, écoutés viaAnimatedBuilder. - 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écoutepb.authStore.onChange. Session persistée viaAsyncAuthStore(SharedPreferences) → on reste connecté.
Structure
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())oùrecordest unRecordModelPocketBase. Les relations sont des id (string) ;createdest une date ISO (string).
Commandes
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.