# 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.*