(Feat) Migration PocketBase
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# 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())` où `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.
|
||||
Reference in New Issue
Block a user