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

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 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). 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

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

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.