Files
Omika/docs/MOBILE_FUNCTIONAL_STATUS.md
2026-10-23 18:46:50 +02:00

702 lines
40 KiB
Markdown

# MOBILE FUNCTIONAL STATUS — État fonctionnel actuel de l'application mobile
> **Bilan fonctionnel global** de l'application mobile Omika, tel qu'il est
> réellement implémenté et validé aujourd'hui. Ce document décrit **ce que
> l'utilisateur peut réellement faire**, ce qui est en lecture seule, ce qui sait
> écrire offline, ce qui manque, ce qui est bloqué backend et ce qui reste à
> développer.
>
> Ce document est **orienté fonctionnel** : il ne remplace pas
> `docs/LOT_MOBILE_0.md` (architecture technique, décisions, principes
> offline-first, SQLite, Sync, identités, Design System). Il ne duplique pas son
> contenu.
>
> Aucun code de production n'est modifié par ce document.
---
## 1. Vue d'ensemble — ce que l'utilisateur peut faire aujourd'hui
L'application mobile Omika est aujourd'hui une application **de consultation
offline** du dernier snapshot métier synchronisé. L'utilisateur peut :
- **Naviguer** entre les écrans (Agenda, Interventions, Dossiers, Équipe,
Clients, Sites, Paramètres) via une barre latérale (desktop/tablette) ou une
barre basse + menu « Plus » (smartphone).
- **Consulter** l'agenda du jour et le détail d'un rendez-vous.
- **Consulter** la liste des interventions, les rechercher localement, filtrer
les archivées, et ouvrir la fiche d'une intervention.
- **Consulter** les dossiers, clients, sites et collaborateurs (liste + détail).
- **Consulter** les contacts liés à une intervention et le détail d'un contact.
- **Consulter** les documents / photos d'une intervention, les **télécharger à
la demande**, les **ouvrir offline** s'ils sont en cache, et **en ajouter**
(photo ou document) **offline-first**.
- **Créer, modifier et archiver une intervention** **offline-first** (formulaire
partagé Create/Edit, bouton Nouvelle Intervention, action Modifier, action
Archiver avec confirmation, aucun DELETE).
- **Synchroniser** manuellement via « Synchroniser maintenant ».
- **Personnaliser** le thème (clair / sombre / système) et la couleur principale.
L'utilisateur **ne peut pas** aujourd'hui : créer/modifier un rendez-vous, un
dossier, un client, un site ou un collaborateur ; saisir des temps ; rédiger des
notes ; créer un compte rendu ou un rapport ; envoyer un email ; utiliser le
GPS ; réinitialiser l'application ; purger le cache depuis l'UI.
> **Note importante (vérification Sync)** : le **backend + Sync supportent déjà
> réellement** la création, la modification et l'archivage d'une **Intervention**
> (handler `InterventionSyncOperationHandler`, contrat `InterventionSyncPayload`,
> pipeline SUCCESS/REJECTED). L'**UI + mutation locale** Intervention est
> **réalisée** (formulaire Create/Edit, bouton Nouvelle Intervention, action
> Modifier, action Archiver avec confirmation, aucun DELETE, protection double
> Save, navigation contextuelle, états Pending/Rejected affichés). Les autres
> entités (RendezVous, Dossier, Client, Site, Collaborateur) ne disposent
> **d'aucun** handler Sync ni payload : elles restent bloquées backend.
---
## 2. Matrice fonctionnelle synthétique
Légende des états :
| État | Signification |
|------|---------------|
| **REAL** | Fonctionnalité réellement implémentée et utilisable |
| **PARTIAL** | Partiellement implémentée (une partie seulement du périmètre maquette) |
| **READ-ONLY** | Consultation seule, aucune écriture |
| **BLOCKED** | Bloqué par un prérequis backend / snapshot / sync manquant |
| **NOT IMPLEMENTED** | Non implémenté (aucun parcours utilisateur) |
| **PLACEHOLDER** | Écran d'attente « Fonctionnalité à venir » |
| Domaine | Fonctionnalité | État | Offline | Écriture | Réseau requis | Backend requis | Commentaire |
|---------|----------------|------|---------|----------|---------------|----------------|-------------|
| Shell / navigation | Navigation entre écrans | **REAL** | Oui | Non | Non | Non | Barre latérale + barre basse + menu « Plus » |
| Shell / navigation | Thème clair / sombre / système | **REAL** | Oui | Non | Non | Non | Persisté localement |
| Shell / navigation | Couleur principale | **REAL** | Oui | Non | Non | Non | Presets + personnalisée, persistée |
| Agenda | Liste des rendez-vous du jour | **REAL** | Oui | Non | Non | Non | Lecture SQLite |
| Agenda | Navigation jour précédent / suivant | **REAL** | Oui | Non | Non | Non | — |
| RendezVous | Détail d'un rendez-vous | **REAL** | Oui | Non | Non | Non | Lecture seule |
| RendezVous | Saisie des temps (accès) | **REAL** (UI CREATE) | Oui | **Oui** | Non (offline-first) | Oui | Accès contextuel RendezVous → liste filtrée → Nouvelle saisie (RendezVousId prérempli) — voir section 7 |
| RendezVous | Annulation / modification | **NOT IMPLEMENTED** | — | — | — | Oui | Aucun handler Sync RendezVous |
| Interventions | Liste des interventions | **REAL** | Oui | Non | Non | Non | Lecture SQLite |
| Interventions | Recherche locale | **REAL** | Oui | Non | Non | Non | Reference, Titre, ClientNom, SiteNom, DossierReference |
| Interventions | Filtre Actif / Archivé | **REAL** | Oui | Non | Non | Non | Basé sur `EstArchive` |
| Interventions | Fiche intervention | **REAL** | Oui | Non | Non | Non | Client/Site/Dossier/RendezVous/Documents |
| Interventions | Rendez-vous liés | **REAL** | Oui | Non | Non | Non | Liste + tri chronologique |
| Interventions | Création / modification Intervention | **REAL** (UI + mutation locale) | Oui | **Oui** | Non (offline-first) | Oui | **Backend + Sync SUPPORTED** ; UI Create/Edit + mutation locale + Outbox implémentées (CREATE/UPDATE/ARCHIVE), aucun DELETE |
| Interventions | Archiver une intervention | **REAL** (UI + mutation locale) | Oui | **Oui** | Non (offline-first) | Oui | Action Archiver + confirmation, `ArchiveAsync` via `IInterventionLocalService`, aucun DELETE |
| Interventions | État Sync (Pending / Rejected) | **REAL** | Oui | Non | Non | Non | Affiché sur la fiche à partir des vraies données Outbox (`IsPendingSync`, `HasSyncError`, `SyncErrorMessage`) |
| Documents / Photos | Liste des documents d'une intervention | **REAL** | Oui | Non | Non | Non | Métadonnées SQLite |
| Documents / Photos | Téléchargement à la demande | **REAL** | Non | Non | **Oui** | Oui | HTTP `GET /api/documents/{guid}` |
| Documents / Photos | Ouverture offline | **REAL** | Oui | Non | Non | Non | Si présent en cache |
| Documents / Photos | Ajout photo / document | **REAL** | Oui | **Oui** | Non (offline-first) | Oui | Outbox + SyncEngine |
| Contacts | Contacts liés à une intervention | **REAL** | Oui | Non | Non | Non | Relation `ContactIds` explicite |
| Contacts | Détail d'un contact | **REAL** | Oui | Non | Non | Non | Téléphone / email plateforme |
| Dossiers | Liste des dossiers | **REAL** | Oui | Non | Non | Non | Lecture SQLite |
| Dossiers | Détail d'un dossier | **REAL** | Oui | Non | Non | Non | Interventions liées |
| Dossiers | Création / modification Dossier | **NOT IMPLEMENTED** | — | — | — | Oui | Aucun handler Sync Dossier |
| Clients | Liste des clients | **REAL** | Oui | Non | Non | Non | Lecture SQLite |
| Clients | Détail d'un client | **REAL** | Oui | Non | Non | Non | Sites, contacts, dossiers, interventions |
| Clients | Création / modification Client | **NOT IMPLEMENTED** | — | — | — | Oui | Aucun handler Sync Client |
| Sites / Lieux | Liste des sites | **REAL** | Oui | Non | Non | Non | Lecture SQLite |
| Sites / Lieux | Détail d'un site | **REAL** | Oui | Non | Non | Non | Client, contacts, interventions |
| Sites / Lieux | GPS / itinéraire | **NOT IMPLEMENTED** | — | — | — | Non | — |
| Équipe / Collaborateurs | Liste des collaborateurs | **REAL** | Oui | Non | Non | Non | Lecture SQLite |
| Équipe / Collaborateurs | Détail d'un collaborateur | **REAL** | Oui | Non | Non | Non | Rendez-vous + interventions liés |
| Paramètres | Thème / couleur / version | **REAL** | Oui | Non | Non | Non | — |
| Paramètres | Synchroniser maintenant | **REAL** | Non | Non | **Oui** | Oui | Via SyncEngine |
| Paramètres | Purge cache | **NOT IMPLEMENTED** (UI) | — | — | — | — | `PurgeAsync` existe ; problème technique mobile uniquement |
| Paramètres | Reset application | **NOT IMPLEMENTED** (UI) | — | — | — | — | Primitives locales existent ; dépendance protection Outbox |
| Paramètres | Licence / activation | **REAL** | Oui | Non | Non | Oui | Activation par code client + n° licence ; session + token sécurisé ; identité affichée |
| Synchronisation | Synchronisation manuelle | **REAL** | Non | Non | **Oui** | Oui | SyncEngine + Outbox |
| Synchronisation | État de synchronisation | **REAL** | Oui | Non | Non | Non | Idle / Syncing / Synced / Offline / Error |
| Notes Intervention | Lecture / écriture notes | **NOT IMPLEMENTED** | — | — | — | Oui | CAS E — voir section 6 |
| SuiviTemps | Consultation des temps | **REAL** (UI LIST) | Oui | Non | Non | Non | Liste offline via `ISuiviTempsLocalService` (PHASE 3) — voir section 7 |
| SuiviTemps | Saisie des temps | **REAL** (UI CREATE) | Oui | **Oui** | Non (offline-first) | Oui | **UI CREATE READY** — formulaire offline-first via `ISuiviTempsLocalService.CreateAsync` ; TypeTemps depuis `IParametreMetierLocalService.GetTypeTempsAsync()` (Key = PA_CLE) ; aucun UPDATE/ARCHIVE/DELETE — voir section 7 |
| Rapports / CompteRendu | Création / consultation | **NOT IMPLEMENTED** | — | — | — | Oui | Backend only — voir section 8 |
---
## 3. Écritures offline réellement opérationnelles
La seule écriture offline réellement opérationnelle aujourd'hui est
**l'ajout de documents / photos** à une intervention.
### 3.1 Ajout d'une photo ou d'un document (offline-first)
Ce que fait l'utilisateur :
1. Depuis la fiche intervention, il ouvre l'écran **Documents / Photos**.
2. Il choisit **Ajouter une photo** ou **Ajouter un document** (sélecteur de
fichier plateforme).
3. Le fichier est immédiatement visible dans la liste, marqué comme en attente
de synchronisation.
Ce qui se passe techniquement (chaîne complète vérifiée) :
| Étape | Détail |
|-------|--------|
| **Mutation SQLite** | Copie du fichier dans l'espace contrôlé de l'application + création de la métadonnée locale `LocalDocumentMetadata` (payload `DocumentSummaryDto`) |
| **Outbox** | Création d'une opération `OutboxOperation` (`EntityType = Document`, `OperationType = Create`, état `Pending`) |
| **OperationId** | `Guid.NewGuid()` — distinct du GUID métier |
| **GUID métier** | `DocumentGuid` (identité publique du document) |
| **SyncEngine** | À la synchronisation, l'opération Document est envoyée via `POST /api/documents` (multipart) |
| **SUCCESS** | L'opération passe à l'état `Succeeded` ; le document est marqué synchronisé |
| **REJECTED** | L'opération passe à l'état `Rejected` ; l'erreur est affichée dans l'UI |
Le `DocumentGuid` est **distinct** de l'`OperationId` : le GUID métier identifie
le document, l'OperationId identifie la transaction de synchronisation.
> **Important** : la capacité générique de l'Outbox n'est pas une fonctionnalité
> utilisateur en soi. Seule l'écriture **Documents / Photos** est réellement
> branchée de bout en bout (UI → SQLite → Outbox → SyncEngine → backend).
---
## 4. Domaines actuellement en lecture seule
Les domaines suivants sont **strictement en lecture seule** : aucune écriture,
aucune Outbox, aucun CREATE / UPDATE / DELETE / ARCHIVE depuis le mobile.
| Domaine | État réel | Détail |
|---------|-----------|--------|
| **RendezVous** | READ-ONLY | Consultation du détail uniquement |
| **Intervention** | WRITE (mutation locale) | Liste, recherche, filtre, fiche, relations. **Backend + Sync SUPPORTED** pour CREATE/UPDATE/ARCHIVE ; mutation locale + Outbox implémentées (service `InterventionLocalService`) |
| **Dossier** | READ-ONLY | Liste + détail |
| **Client** | READ-ONLY | Liste + détail |
| **Site / Lieu** | READ-ONLY | Liste + détail |
| **Collaborateur** | READ-ONLY | Liste + détail |
| **Contact** | READ-ONLY | Liste + détail (téléphone / email plateforme) |
Aucune de ces entités ne dispose d'une écriture mobile **branchée à l'UI**. Les
données proviennent exclusivement du snapshot synchronisé.
> **Capacité Sync réelle** : seul **Intervention** dispose d'un handler Sync
> (`InterventionSyncOperationHandler`) et d'un contrat public
> (`InterventionSyncPayload`). Les autres entités (RendezVous, Dossier, Client,
> Site, Collaborateur) n'ont **aucun** handler ni payload : toute opération Sync
> sur ces types est rejetée (`NO_HANDLER` → REJECTED).
---
## 5. Synchronisation, Offline, SQLite, Outbox, cache Documents
### 5.1 Synchronisation
- **Déclenchement** : manuel via « Synchroniser maintenant » (Paramètres ou
Shell). Aucune synchronisation automatique périodique.
- **Moteur** : `SyncEngine` (unique système de mise à jour).
- **États UI** : `Idle`, `Syncing`, `Synced`, `Offline`, `Error`.
- **Hors ligne** : aucun appel réseau, état non intrusif (« Hors ligne »).
- **Erreurs** : nombre d'opérations rejetées / anomalies affiché.
### 5.2 Offline / SQLite
- Toutes les lectures (liste, fiche, contacts, documents) se font depuis SQLite
(dernier snapshot).
- Aucun appel HTTP obligatoire à l'ouverture d'un écran.
- Le snapshot est reconstruit atomiquement depuis le backend.
### 5.3 Outbox
- L'Outbox est opérationnelle pour **Documents / Photos** uniquement.
- Les opérations sont envoyées à la synchronisation, avec états
`Pending` / `Sending` / `Accepted` / `WaitingResult` / `Succeeded` / `Rejected`.
- Le mécanisme d'identité `RequestedEntityGuid` / `CanonicalEntityGuid` est
opérationnel : après un CREATE réussi, le backend retourne le GUID canonique
(GUID physique Omika ou GUID synthétique réversible Batik), réconcilié côté
mobile pour les opérations suivantes (UPDATE/ARCHIVE).
### 5.4 Cache Documents
- Les documents téléchargés sont stockés dans un cache fichier local
(`DocumentCacheService`).
- L'ouverture offline est possible si le fichier est présent en cache.
- **Purge du cache** : la capacité technique `PurgeAsync` existe dans
`DocumentCacheService`, mais **n'est pas branchée à une interface utilisateur**.
Aucun bouton « Purger le cache » n'est accessible à l'utilisateur.
---
## 6. Notes Intervention — état démontré (CAS E)
État **déjà démontré**, aucune décision n'est réouverte :
- **Aucune Note Intervention exploitable** dans le snapshot / local.
- **Aucune table SQLite Note**.
- **Aucune UI** Notes.
- `CompteRendu` existe côté backend mais **n'est pas peuplé** dans le snapshot.
- `MessageInterne` / `IN_NoteInterne` restent **backend** (non exposés au mobile).
- `DocumentNote` est un **concept différent** (document, pas note d'intervention).
**État fonctionnel** : `NOT IMPLEMENTED` (bloqué backend — données absentes du
snapshot).
**Classification** : **C — BACKEND + MOBILE** (snapshot / SQLite / Sync manquant)
+ **D** uniquement pour la décision de représentation métier exacte (Note vs
DocumentNote). `DocumentNote` reste distinct.
---
## 7. Suivi des temps — état démontré (CAS C)
État **déjà démontré**, aucune décision n'est réouverte :
- L'entité métier `SuiviTemps` est **réelle** côté backend.
- Les relations Intervention / RendezVous / Collaborateur existent.
- **Présent dans le snapshot** mobile (PHASE 1B) : le `MobileSnapshotResponse`
expose désormais `SuivisTemps` (liste de `SuiviTempsDto`, lecture seule,
réutilisant le DTO métier public — aucune colonne HFSQL exposée). Le GUID
exposé est le **GUID physique existant** (Batik et Omika), aucun GUID
synthétique n'est créé.
- **Sync CREATE** (PHASE 1C) : le pipeline Sync serveur moderne supporte la
création d'une saisie de temps via `SuiviTempsSyncOperationHandler` →
`ISuiviTempsService` → repository adaptatif. EntityType `SuiviTemps`,
payload `SuiviTempsSyncPayload` (CollaborateurId, InterventionId,
RendezVousId, Date, DureeMinutes, TypeTempsKey, Description). Le champ
`Valide` n'est pas positionnable depuis le mobile (état de validation métier
géré côté backend). UPDATE / ARCHIVE / DELETE → `PERMANENT_FAILURE`.
- **SQLite local** (PHASE 2) : la table `SuiviTemps` est présente dans le
schéma SQLite local, importée atomiquement par le `SnapshotApplier` (avec les
références typées Collaborateur / Intervention / RendezVous pour la
réconciliation d'identité A → B). Le service local `SuiviTempsLocalService`
permet la lecture (liste, par GUID, par intervention) et la création
offline-first (écriture locale + Outbox CREATE, `Valide` toujours false côté
mobile).
- **UI LIST** (PHASE 3) : la vue `SuiviTempsView` + `SuiviTempsViewModel`
permettent la **consultation offline** des saisies de temps via
`ISuiviTempsLocalService.GetAllAsync` (et `GetByInterventionAsync` /
`GetByRendezVousAsync` pour les filtres par intervention / rendez-vous). La
liste affiche les données métier utiles (date, durée, type de temps,
description, collaborateur, intervention, rendez-vous, validation, état Sync
Pending/Rejected/ErrorMessage). Aucun UPDATE / ARCHIVE / DELETE. Accès global
via la navigation + accès contextuel depuis une Intervention (liste filtrée)
et depuis un RendezVous (liste filtrée).
- **Référentiel TypeTemps local** (micro-lot) : **READY** — le référentiel
`ParametreMetier` est désormais disponible **offline** au mobile. Le pipeline
est : HFSQL `ParametreMetier` → repository adaptatif (`AdaptiveParametreMetierRepository`)
→ `MobileBootstrapService` (chargé au bootstrap) → SQLite local → service
local `IParametreMetierLocalService.GetTypeTempsAsync()`. Ce service retourne
une liste ordonnée d'options `ReferenceValueDto` (Key = clé métier `PA_CLE`,
Label = libellé métier) **sans réseau, sans API, sans HFSQL**, uniquement à
partir des données snapshot déjà présentes localement. Le filtrage suit la
convention générique du projet (Actif / Invisible) et l'ordre métier
(`PA_Ordre`). Aucune valeur métier n'est codée en dur ; les options proviennent
des vraies données `ParametreMetier`. L'ancienne règle Batik (exclusion
`PA_Valeur` 0/1/99, cas particulier 2) n'est **pas** reprise faute de règle
moderne explicite.
- **UI CREATE** (PHASE 3) : **READY** — le formulaire `SuiviTempsCreateView` +
`SuiviTempsCreateViewModel` permettent la **saisie offline-first** d'un temps
via `ISuiviTempsLocalService.CreateAsync` (écriture locale + Outbox CREATE,
`Valide` toujours false côté mobile). Le formulaire contient uniquement
CollaborateurId, InterventionId, RendezVousId, Date, DureeMinutes,
TypeTempsKey, Description. Le champ `Valide` est **absent**. Le TypeTemps est
chargé depuis `IParametreMetierLocalService.GetTypeTempsAsync()` (affichage
`ReferenceValueDto.Label`, valeur envoyée `ReferenceValueDto.Key` == PA_CLE,
aucune valeur hardcodée). `DureeMinutes` est saisie manuellement (aucun calcul
automatique depuis RendezVous/Intervention). Aucune écriture directe SQLite /
Outbox / API / SyncEngine depuis le ViewModel (le service local reste
responsable). Protection double clic : `SaveCommand.CanExecute == false`
pendant l'exécution (binding Avalonia) + garde interne `IsBusy`. Accès global
(liste SuiviTemps → Nouvelle saisie) et contextuel depuis une Intervention
(liste filtrée → Nouvelle saisie → InterventionId prérempli) et depuis un
RendezVous (liste filtrée → Nouvelle saisie → RendezVousId prérempli, sans
déduction automatique d'Intervention / durée / date).
**État fonctionnel** : `REAL` — **UI LIST READY**, **TYPE TEMPS LOCAL
REFERENCE READY**, **UI CREATE READY**, **RENDEZVOUS ENTRY POINT READY**.
Backend métier, snapshot, Sync CREATE, SQLite local, référentiel TypeTemps
local, consultation et saisie offline-first sont opérationnels. UPDATE /
ARCHIVE / DELETE : NOT SUPPORTED.
**Classification** : **C — BACKEND + MOBILE** (inchangée, CAS C non rouvert).
Les PHASE 1B (snapshot), 1C (Sync CREATE), 2 (SQLite local), 3 (UI LIST), le
micro-lot référentiel TypeTemps local et l'UI CREATE sont réalisés.
**Statut détaillé SuiviTemps** (après correctif snapshot — persistance des
colonnes de référence) :
| Capacité | Statut |
|----------|--------|
| BACKEND METIER | **READY** |
| SNAPSHOT | **READY** |
| SYNC CREATE | **READY** |
| LOCAL SQLITE | **READY** |
| SNAPSHOT LOCAL | **READY** |
| LOCAL CREATE | **READY** |
| OUTBOX CREATE | **READY** |
| RECONCILIATION | **READY** |
| UI LIST | **READY** |
| TYPE TEMPS LOCAL REFERENCE | **READY** |
| UI CREATE | **READY** |
| UPDATE | **NOT SUPPORTED** |
| ARCHIVE | **NOT SUPPORTED** |
| DELETE | **NOT SUPPORTED** |
| RENDEZVOUS ENTRY POINT | **READY** |
Le correctif snapshot garantit que le refresh persiste les colonnes structurées
(`CollaborateurId`, `InterventionId`, `RendezVousId`) de manière **générique**
(mécanisme partagé avec `LocalRepository`), sans fix spécifique SuiviTemps, et
préserve l'atomicité de la transaction snapshot ainsi que les données techniques
(Outbox, Sync state, profil, droits, licence, configuration, cache documents).
---
## 8. Rapports / CompteRendu — état réel
Il ne faut pas confondre les concepts suivants :
| Concept | Nature | État mobile |
|---------|--------|-------------|
| **CompteRendu** | Entité backend (`CompteRenduRow`, `CompteRenduDto`) | **BACKEND ONLY** — non peuplé dans le snapshot, aucune UI |
| **Rapport** | Modèles de rapports (`ModeleRapport`, `ModeleRapportMob`) | **BACKEND ONLY** — aucune UI mobile |
| **Note** | Note d'intervention | **NOT IMPLEMENTED** (CAS E) |
| **Document** | Fichier / métadonnée | **REAL** (liste, téléchargement, ajout offline) |
**État fonctionnel** :
- CompteRendu : `BACKEND ONLY` (aucune UI, non peuplé dans le snapshot).
- Rapports : `BACKEND ONLY` (aucune UI mobile).
- Aucune création / consultation de compte rendu ou rapport depuis le mobile.
---
## 9. Paramètres — ce qui est réellement accessible
La page Paramètres expose réellement à l'utilisateur :
| Réglage | État | Détail |
|---------|------|--------|
| **Thème** (Light / Dark / System) | **REAL** | ComboBox + bouton « Appliquer le thème » |
| **Couleur principale** | **REAL** | Presets + couleur personnalisée |
| **Synchroniser maintenant** | **REAL** | Bouton + état + erreur |
| **État de synchronisation** | **REAL** | Texte d'état |
| **Version** | **REAL** | Affichage « À propos » |
**Non accessibles à l'utilisateur** (capacité technique ≠ fonction UI) :
| Réglage | État | Détail |
|---------|------|--------|
| **Purge cache** | **NOT IMPLEMENTED** (UI) | `PurgeAsync` existe mais non branché à l'UI |
| **Reset application** | **NOT IMPLEMENTED** (UI) | Aucun parcours utilisateur |
---
## 10. Reset application
**État** : `NOT IMPLEMENTED` (UI).
Il n'existe **aucun parcours utilisateur** de réinitialisation de l'application.
Même si certaines primitives techniques de suppression existent (ex. purge du
cache), il n'y a pas de bouton ni d'écran « Reset » accessible à l'utilisateur.
**Classification** : **A — MOBILE UNIQUEMENT**. Le comportement fonctionnel est
déjà défini (réinitialisation des données locales / cache / outbox / licence) et
**aucune évolution backend n'est nécessaire**. La seule dépendance est
**technique** : la protection des fichiers référencés par des opérations Outbox
en attente (Pending / Rejected) avant suppression. Aucune décision métier non
résolue ne bloque ce lot.
---
## 11. Purge cache
**État** : `NOT IMPLEMENTED` (UI).
Le bouton de purge du cache Documents **n'a pas été branché** : la protection
des fichiers nécessaires aux opérations Outbox en attente n'était pas
suffisamment fiable. La capacité technique `DocumentCacheService.PurgeAsync`
existe mais n'est exposée à aucune interface utilisateur.
**Classification** : **A — MOBILE UNIQUEMENT**. Le problème est **uniquement
technique côté mobile** (fiabiliser la protection des fichiers référencés par
l'Outbox Pending / Rejected avant suppression). Aucune décision métier réelle ne
manque. Dépendance : protection fichiers Outbox.
---
## 12. Licence / Bootstrap — état fonctionnel utilisateur
- Le **backend** licence / bootstrap est réel : `POST /api/license/activate`,
`POST /api/license/validate`, `GET /api/mobile/bootstrap`, token signé,
identité/profil/droits.
- **Émission du token de session** : `activate` et `validate` retournent une
réponse de session `MobileLicenseSessionResponse` contenant le **Bearer token**
(`AccessToken`) et son **expiration exacte** (`AccessTokenExpiresAtUtc`,
24 h par défaut via `MobileAuth:TokenLifetimeHours`). Le token n'est émis
**que** si la licence est valide ; en cas d'échec, la réponse contient le code
d'erreur métier et **aucun token**. Le token est stateless (HMAC-SHA256),
jamais loggé, jamais stocké en HFSQL.
- **Côté application** : l'UI d'activation est **réellement branchée**. Au
premier démarrage sans session valide, l'utilisateur voit l'écran
**Activation** (code client + numéro de licence). Après activation réussie, le
token est stocké en **secure storage** (jamais en SQLite en clair), le
bootstrap (profil, droits, licence, configuration) est persisté en SQLite, et
l'application bascule vers le **Shell**.
- **Redémarrage** : si une session valide existe (licence valide ou période
offline autorisée), l'application démarre directement sur le **Shell** sans
repasser par l'activation.
- **Offline** : session déjà validée + période offline autorisée → accès aux
données SQLite sans connexion obligatoire. Premier démarrage sans session
valide + offline → activation impossible (message réseau).
- **Identité** : le nom de l'utilisateur (et ses initiales) est affiché dans le
**Shell** à partir du bootstrap local. Une licence expirée/révoquée ne
supprime **jamais** automatiquement les données locales (SQLite métier, cache,
Documents, Outbox).
**État fonctionnel** : `REAL` — activation, validation, bootstrap, token sécurisé
et identité utilisateur sont opérationnels côté mobile.
**Classification** : **A — MOBILE UNIQUEMENT** — réalisé. Le backend activation,
validation et bootstrap **existaient déjà** ; l'UI mobile d'activation / identité
a été ajoutée. **Risque sécurité : moyen/élevé** (parcours d'activation, token,
identité utilisateur) — traité par le secure storage et l'absence de token en
clair.
---
## 13. Comparaison avec la maquette `maquetteOmika.html`
La maquette reste une **référence UX uniquement**. Comparaison des grandes
fonctions :
| Fonction maquette | Application actuelle | État | Écart restant |
|-------------------|----------------------|------|---------------|
| Agenda (Mes rendez-vous) | Agenda du jour réel | **PARTIAL** | Pas de vue semaine/équipe, pas de saisie temps |
| RendezVous (détail) | Détail réel | **PARTIAL** | Pas de saisie temps, pas d'annulation |
| Interventions (recherche) | Liste + recherche locale | **PARTIAL** | Filtres avancés (relation RDV, date, collaborateur) absents |
| Interventions (fiche) | Fiche réelle | **PARTIAL** | Pas de GPS, création, planification RDV, compte rendu, rapports, emails |
| Dossiers | Liste + détail réels | **PARTIAL** | Pas de création / ajout d'intervention |
| Clients | Liste + détail réels | **PARTIAL** | Pas de création / modification |
| Sites / Lieux | Liste + détail réels | **PARTIAL** | Pas de GPS / itinéraire |
| Équipe | Liste + détail réels | **PARTIAL** | Pas d'agenda d'équipe |
| Documents | Liste + téléchargement + ajout offline | **REAL** | — |
| Photos | Ajout photo offline | **REAL** | — |
| Contacts | Liste + détail réels | **REAL** | — |
| Notes | — | **NOT IMPLEMENTED** | CAS E |
| Temps | — | **NOT IMPLEMENTED** | CAS C |
| Rapports | — | **NOT IMPLEMENTED** | Backend only |
| CompteRendu | — | **NOT IMPLEMENTED** | Backend only |
| Paramètres | Thème / couleur / sync / version / licence | **PARTIAL** | Pas de purge cache, reset |
| Synchronisation | Manuelle via SyncEngine | **REAL** | Pas d'automatique |
| Reset | — | **NOT IMPLEMENTED** | — |
---
## 14. Backlog — classification des restes
### A — MOBILE UNIQUEMENT
Données / backend déjà disponibles ; manque UI / service mobile.
| Élément | Détail |
|---------|--------|
| Filtres de recherche avancés Interventions | Données disponibles (RDV, collaborateur) ; manque l'UI de filtrage |
| GPS / itinéraire | Données adresse disponibles ; manque l'UI + lanceur carte |
| Email / appel depuis la fiche intervention | Launcher plateforme existe (contact) ; manque l'UI fiche |
| **Reset application** | Primitives locales existent ; comportement fonctionnel défini ; aucune évolution backend nécessaire. Dépendance technique : protection fichiers Outbox |
| **Purge cache** | `DocumentCacheService.PurgeAsync` existe ; problème uniquement technique mobile (protection fichiers Outbox Pending/Rejected). Dépendance : protection fichiers Outbox |
### B — MOBILE WRITE
Backend / sync réellement capables de l'écriture ; UI + mutation locale réalisées.
| Élément | Détail |
|---------|--------|
| **Modification Intervention mobile** ✅ RÉALISÉ | **Backend + Sync SUPPORTED** : handler `InterventionSyncOperationHandler`, contrat `InterventionSyncPayload`, CREATE/UPDATE/ARCHIVE réels, pipeline SUCCESS/REJECTED. **UI + mutation locale réalisées** : formulaire Create/Edit, bouton Nouvelle Intervention, action Modifier, action Archiver avec confirmation, aucun DELETE, protection double Save, navigation contextuelle, états Pending/Rejected affichés. |
> **Vérification** : CREATE, UPDATE et ARCHIVE Intervention sont réellement
> supportés de bout en bout par le backend + Sync (contrat public exploitable,
> EntityType reconnu, handler réel, traitement métier réel via
> `IInterventionService`, résultat SUCCESS/REJECTED possible). DELETE n'est pas
> supporté (archivage logique uniquement → PERMANENT_FAILURE). Le lot
> **Intervention Write UI** est **COMPLET** : CREATE UI COMPLETE, UPDATE UI
> COMPLETE, ARCHIVE UI COMPLETE, OFFLINE-FIRST, OUTBOX, IDENTITY
> RECONCILIATION. DELETE : NOT SUPPORTED.
### C — BACKEND + MOBILE
Contrat / snapshot / sync manquant avant l'UI.
| Élément | Détail |
|---------|--------|
| Notes Intervention | Absentes du snapshot / SQLite / Sync (CAS E) |
| Suivi des temps | **Snapshot (PHASE 1B) + Sync CREATE (PHASE 1C) + SQLite local (PHASE 2) faits** ; **UI LIST (PHASE 3) faite** ; **TYPE TEMPS LOCAL REFERENCE READY** ; **UI CREATE (PHASE 3) faite** (offline-first, TypeTemps Key=PA_CLE, aucun UPDATE/ARCHIVE/DELETE) ; **RENDEZVOUS ENTRY POINT RÉALISÉ** (accès contextuel RendezVous → liste filtrée → Nouvelle saisie, RendezVousId prérempli) |
| CompteRendu | Non peuplé dans le snapshot |
| Rapports | Modèles backend, aucune exposition mobile |
| **RendezVous (création / modification / annulation)** | **Aucun handler Sync RendezVous, aucun payload** ; toute opération Sync est rejetée (`NO_HANDLER`) |
| **Dossier (création / modification)** | **Aucun handler Sync Dossier, aucun payload** |
| **Client (création / modification)** | **Aucun handler Sync Client, aucun payload** |
| **Site (création / modification)** | **Aucun handler Sync Site, aucun payload** |
| **Collaborateur (création / modification)** | **Aucun handler Sync Collaborateur, aucun payload** |
### D — DÉCISION MÉTIER
Sémantique à décider avant développement.
| Élément | Détail |
|---------|--------|
| Sémantique Note vs DocumentNote | Distinction à confirmer avant développement Notes (décision de représentation métier exacte) |
> **Note** : Purge cache et Reset ne sont **plus** en D : ce sont des problèmes
> **techniques mobiles** (protection fichiers Outbox), sans décision métier non
> résolue. Ils sont classés en **A — MOBILE UNIQUEMENT** avec dépendance
> technique sur la protection Outbox.
---
## 15. Prochains lots recommandés
À partir de la cartographie réelle, **5 prochains lots** recommandés (aucun code
n'est écrit ici). Priorité forte donnée à l'activation/licence/identité, aux
écritures métier terrain réellement supportées par le Sync, au SuiviTemps et aux
Notes/CompteRendu, avant les améliorations purement cosmétiques ou les filtres
avancés.
### Lot 1 — Licence / activation + identité utilisateur ✅ RÉALISÉ
- **Objectif** : brancher l'UI d'activation / validation licence et afficher
l'identité utilisateur (nom, profil, droits).
- **Catégorie** : A (MOBILE UNIQUEMENT) — backend activation/validation/bootstrap
**déjà existants**.
- **Backend nécessaire** : Non (déjà existant).
- **Dépendances** : Bootstrap / token / profil.
- **Risque** : **Moyen/élevé** (sécurité, parcours d'activation, identité).
- **Raison de la priorité** : **Prérequis de mise en production réelle** — sans
activation/licence/identité, l'application n'est pas exploitable en production.
- **Statut** : **RÉALISÉ** — écran d'activation, session + token sécurisé
(secure storage), bootstrap local, identité Shell, offline grace. Voir
section 12.
### Lot 2 — Modification Intervention mobile (B — MOBILE WRITE) ✅ RÉALISÉ
- **Objectif** : brancher l'UI + mutation locale (Outbox) pour créer, modifier et
archiver une intervention, en s'appuyant sur le handler Sync **déjà
opérationnel**.
- **Catégorie** : B (MOBILE WRITE) — backend + Sync **déjà supportés**.
- **Backend nécessaire** : Non (handler `InterventionSyncOperationHandler` et
contrat `InterventionSyncPayload` existants).
- **Dépendances** : UI fiche + mutation locale + Outbox + réconciliation
RequestedEntityGuid → CanonicalEntityGuid.
- **Risque** : Moyen (mutation locale, identité, ordre CREATE → UPDATE).
- **Raison de la priorité** : **Écriture métier terrain réellement déjà
supportée par le Sync** — valeur terrain immédiate, sans évolution backend.
- **Statut** : **RÉALISÉ** — formulaire Create/Edit partagé, bouton Nouvelle
Intervention, action Modifier, action Archiver avec confirmation, aucun
DELETE, protection double Save, navigation contextuelle, états
Pending/Rejected affichés. Voir section 14 B.
### Lot 3 — Suivi des temps (CAS C)
- **Objectif** : exposer le SuiviTemps dans le snapshot / SQLite / Sync, puis
UI de saisie.
- **Catégorie** : C (BACKEND + MOBILE).
- **Backend nécessaire** : Oui (sync + handler ; snapshot **fait**).
- **Dépendances** : SQLite, Sync, UI.
- **Risque** : Moyen (contrat + sync).
- **Raison de la priorité** : **Fonction terrain très attendue** (saisie des
temps), nécessite le backend.
- **Statut** : **PHASE 1B (snapshot) RÉALISÉE** — `SuivisTemps` exposé dans le
`MobileSnapshotResponse` (lecture seule, GUID physique, aucun GUID
synthétique). **PHASE 1C (Sync CREATE) RÉALISÉE** — handler
`SuiviTempsSyncOperationHandler` + payload `SuiviTempsSyncPayload`, CREATE
supporté, UPDATE/ARCHIVE/DELETE non supportés. **PHASE 2 (SQLite local)
RÉALISÉE** — table `SuiviTemps` dans le schéma SQLite, import snapshot
(`SnapshotApplier`), service local `SuiviTempsLocalService` (lecture +
création offline-first via Outbox CREATE, `Valide` toujours false côté
mobile), réconciliation d'identité A → B. **PHASE 3 (UI LIST) RÉALISÉE** —
`SuiviTempsView` + `SuiviTempsViewModel` (consultation offline, états
Pending/Rejected/ErrorMessage, accès global + depuis Intervention).
**RÉFÉRENTIEL TYPE TEMPS LOCAL RÉALISÉ** — `IParametreMetierLocalService`
+ `GetTypeTempsAsync()` (options `ReferenceValueDto` Key/Label depuis les
vraies données `ParametreMetier`, offline). **UI CREATE RÉALISÉE** —
`SuiviTempsCreateView` + `SuiviTempsCreateViewModel` (saisie offline-first,
TypeTemps Key=PA_CLE, aucun UPDATE/ARCHIVE/DELETE, protection double clic
CanExecute). **RENDEZVOUS ENTRY POINT RÉALISÉ** — accès contextuel
RendezVous → liste filtrée → Nouvelle saisie (RendezVousId prérempli, sans
déduction automatique d'Intervention / durée / date).
### Lot 4 — Notes / CompteRendu (CAS E)
- **Objectif** : définir la représentation Note, l'exposer dans le snapshot /
SQLite / Sync, puis UI.
- **Catégorie** : C (BACKEND + MOBILE) + D (décision métier Note vs DocumentNote).
- **Backend nécessaire** : Oui (snapshot + sync + handler).
- **Dépendances** : Décision métier, snapshot, SQLite, Sync, UI.
- **Risque** : Moyen (sémantique + contrat).
- **Raison de la priorité** : **Fonction terrain attendue** (notes / compte
rendu), nécessite une décision métier préalable.
### Lot 5 — Reset + Purge cache (A — MOBILE UNIQUEMENT)
- **Objectif** : brancher l'UI Reset application et Purge cache, avec protection
des fichiers référencés par l'Outbox en attente.
- **Catégorie** : A (MOBILE UNIQUEMENT) — primitives locales existantes, aucune
évolution backend.
- **Backend nécessaire** : Non.
- **Dépendances** : Protection fichiers Outbox (Pending / Rejected) avant purge /
reset.
- **Risque** : Faible à moyen (intégrité des opérations en attente).
- **Raison de la priorité** : **Hygiène de production** (réinitialisation /
libération d'espace), sans backend.
---
## 16. Baseline de validation
| Vérification | Résultat | Commande |
|--------------|----------|----------|
| Tests Mobile | **587/587** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName~Omika.Tests.Mobile"` |
| Tests ViewModel SuiviTemps (List) | **11/11** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName~SuiviTempsViewModelTests"` |
| Tests ViewModel SuiviTemps (Create) | **18/18** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName~SuiviTempsCreateViewModelTests"` |
| Tests ViewModel RendezVous (Detail) | **1/1** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName~RendezVousDetailViewModelTests"` |
| Tests ViewModel Intervention (Edit/List/Detail) | **30/30** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName~InterventionEditViewModelTests\|FullyQualifiedName~InterventionsViewModelTests\|FullyQualifiedName~InterventionDetailViewModelTests"` |
| Tests Référentiel TypeTemps local | **6/6** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName~ParametreMetierLocalServiceTests"` |
| Suite unitaire (hors intégration HFSQL) | **1853/1853** | `dotnet test tests/Omika.Tests/Omika.Tests.csproj -c Debug --filter "FullyQualifiedName!~Omika.Tests.Connection.HfSqlOleDb"` |
| Build `Omika.slnx` | **VERT** (projets .NET) | `dotnet build Omika.slnx -c Debug` |
| Build `Omika.App.Local` | **VERT** | `dotnet build src/Omika.App.Local/Omika.App.Local.csproj -c Debug` |
| Build `Omika.App` | **VERT** | `dotnet build src/Omika.App/Omika.App.csproj -c Debug` |
| Build Android | **VERT** | `dotnet build src/Omika.App.Android/Omika.App.Android.csproj -c Debug` |
> **Note — Lot serveur Licence (connexion physique base HFSQL)** : le lot est en
> état de **STOP** (voir `docs/LOT_SERVEUR_LICENCE.md`). Les probes de lecture
> OLEDB ont révélé que la base Licence réelle (`BASE_LICENCES`) ne contient pas
> les tables `ClientBase`, `Licence`, `LicenceHisto` attendues par le repository
> (seulement 2 tables de backup système). Aucune écriture n'a été effectuée.
> La suite complète de tests affiche **1779 réussis / 10 échecs** : les 10 échecs
> sont des tests d'intégration HFSQL environnementaux (`HfSqlOleDb*IntegrationTests`)
> qui échouent faute de base d'intégration configurée (connexion `<:0>`), hors
> périmètre de la baseline unitaire. Les 34 tests de configuration Licence
> (`HfSqlTestConfigurationLoaderTests`, `E2EOptionsTests`,
> `LicenseConnectionOptionsValidatorTests`, `LicenseOleDbConnectionTests`)
> passent **34/34**.
---
*Document de bilan fonctionnel global — ne modifie aucun code de production.*