28 KiB
Omika — Analyse technique de l'application (mobile offline-first + desktop)
Source :
D:\Avalonia\Omika— projetssrc/Omika.App,src/Omika.App.Local,src/Omika.App.Desktop,src/Omika.App.Android,src/Omika.App.iOS. Analyse factuelle destinée à la rédaction d'un site web marketing pour vendre le logiciel de gestion d'interventions (field-service management).
Vue d'ensemble de la pile technique
| Élément | Détail | Preuve |
|---|---|---|
| Langage / runtime | C# sur .NET 10 (net10.0) |
src/Omika.App/Omika.App.csproj |
| UI | Avalonia UI (thème Fluent, police Inter), rendu identique sur desktop + mobile | Omika.App.csproj, App.axaml |
| MVVM | CommunityToolkit.Mvvm ([ObservableProperty], [RelayCommand]) |
ViewModelBase.cs |
| Base locale | SQLite (Microsoft.Data.Sqlite) — fichier omika.db |
LocalDatabase.cs |
| Cartes | Mapsui.Avalonia12 (vue lieu/intervention) | Omika.App.csproj |
| QR | QRCoder (génération) + ZXing.Net (scan Android) | Omika.App.csproj, Omika.App.Android.csproj |
| Images | SkiaSharp (optimisation poids photos) + MetadataExtractor (date EXIF) | Omika.App.csproj |
| Hôtes | Desktop (Avalonia.Desktop, WinExe), Android (net10.0-android, min SDK 23, com.applilogik.omika.app), iOS (net10.0-ios, min 14.0) |
les 3 .csproj hôtes |
| Injection | Pas de conteneur DI — composition racine manuelle unique | AppServices.cs |
Point notable : l'application est un monolithe UI partagé. Omika.App contient
toute la logique d'interface (Vues + ViewModels + Navigation + Services UI) ; les
trois hôtes ne contiennent que le strict minimum natif (lancement + implémentations
plateforme : caméra, QR, téléphone, email, carte, secure storage, connectivité).
1. Les écrans de l'application
Toutes les vues sont dans src/Omika.App/Views/*.axaml, les ViewModels dans
src/Omika.App/ViewModels/*.cs, et le routage page → ViewModel dans
src/Omika.App/Navigation/PageViewModelFactory.cs (enum AppPage).
1.1 Activation / licence
LicenseActivationView.axaml+LicenseActivationViewModel.cs— écran d'inscription au premier démarrage. Deux entrées vers la même procédure : saisie manuelle d'un code d'inscription ou scan d'un QR (avec aperçu caméra en direct, annulable). Affiche aussi les états « Connexion requise pour vérifier votre licence » et « Licence expirée ou révoquée ». Aucune URL codée en dur.
1.2 Accueil / Shell
ShellView.axaml+ShellViewModel.cs— coquille applicative : rail latéral (desktop/tablette), barre de navigation basse mobile (5 accès directs max, le reste dans un menu « Plus »), topbar avec titre, carte profil, bouton flottant de synchronisation avec badge du nombre d'opérations en attente. Mode « immersif » plein écran pour l'éditeur d'image.
1.3 Agenda / Planning / Rendez-vous
AgendaView.axaml+AgendaViewModel.cs— agenda personnel : vues Jour / Semaine / Mois, bandeau 7 jours avec compteurs, sélecteur de date. Lecture 100 % SQLite locale (aucun appel API au chargement).AgendaEquipeView.axaml+AgendaEquipeViewModel.cs— agenda d'équipe : filtres par équipe/collaborateur, modes liste/calendrier.RendezVousDetailView.axaml+RendezVousDetailViewModel.cs— fiche rendez-vous : contacts, saisie de temps inline (brouillons), annulation (origine + motif + note), finalisation (« Fait » après validation des temps), accès au compte rendu.
1.4 Interventions
InterventionsView.axaml+InterventionsViewModel.cs— recherche d'interventions (filtres référence/client/site/collaborateur/état/type/relation aux RDV) avec onglets « Récentes » (dernière recherche persistée) et « Épinglées » (favoris). Recherche à la demande (API), affichage offline du cache.InterventionDetailView.axaml+InterventionDetailViewModel.cs— fiche intervention : hero (référence, client, site, dossier), contacts, documents/ images, notes internes, courriels, brouillons de rapport, notes administratives, actions Modifier / Archiver (offline-first via Outbox).InterventionEditView.axaml+InterventionEditViewModel.cs— formulaire partagé de création/édition d'intervention (offline-first).- Sous-écrans d'une intervention :
InterventionContactsView+ContactDetailView— contacts liés + fiche contact.InterventionDocumentsView+PhotoViewerView+ImageEditorView— documents/ photos, carrousel, annotation d'image plein écran (édition + signature).InterventionNotesView+InterventionNotesAdministrativesView— notes internes et administratives (droitA.NOTE.ADMIN).InterventionEmailsView+NouveauEmailView— courriels d'intervention + composition d'email (depuis modèle, rapport ou contact).RapportChampsView— saisie des champs/rubriques d'un modèle de rapport.InterventionResourcesView— vue regroupant notes / images / documents.
1.5 Dossiers / Clients / Sites
DossiersView+DossierDetailView— liste (recherche + épinglage local) et fiche dossier.ClientsView+ClientDetailView— liste (recherche + épinglage) et fiche client.SitesView+SiteDetailView— liste (recherche + épinglage) et fiche site, avec carte géographique (contrôleControls/LieuMap.axaml, Mapsui) et lancement d'itinéraire.
1.6 Équipe / Collaborateurs
EquipesView.axaml+EquipesViewModel.cs— menu « Équipe » : liste des équipes de l'utilisateur.EquipeDetailView+EquipeDetailViewModel.cs— membres d'une équipe.CollaborateurDetailView+CollaborateurDetailViewModel.cs— fiche collaborateur (dont attribution/révocation de licence mobile).CollaborateursView.axaml/CollaborateursViewModel.cs— liste de collaborateurs existante mais non câblée dans la factory de pages (voir §8).
1.7 Suivi des temps / Compte rendu
SuiviTempsCreateView.axaml+SuiviTempsCreateViewModel.cs— création d'une saisie de temps (offline-first : brouillons SQLite + Outbox). Prise en compte de la pause déjeuner (préférence locale par utilisateur).CompteRenduCreateView.axaml+CompteRenduCreateViewModel.cs— saisie du compte rendu d'un rendez-vous (individuel ou groupe/journée, anti-doublon).
1.8 Synchronisation / Paramètres
SynchronisationView.axaml+SynchronisationViewModel.cs— file de synchronisation : liste des opérations Outbox en attente (temps, annulations, photos/documents, interventions…) + lancement manuel.ParametresView.axaml+ParametresViewModel.cs— réglages : thème clair/sombre/système, couleur principale (presets + curseurs RVB), section Compte/Licence (n° masqué, validité, expiration, société), limite de poids des photos (politique client, lecture seule), horaires de pause déjeuner, maintenance des données (purge cache documents, suppression données, réinitialisation complète), email au support (export des logs en pièce jointe), déconnexion, version.
1.9 Composants réutilisables (Controls/)
Cartes (ClientCard, SiteCard, ContactCard, DossierCard, InterventionCard,
RendezVousCard, EquipeCard), boutons d'action (CallButton, EmailButton,
OpenButton), champs (DateField, TimeField, SignaturePad pour signature,
ImageEditorControl), LieuMap, aperçu HTML d'email
(EmailHtmlPreviewControl), templates d'édition de champs
(Views/ChampEditionTemplates.axaml).
2. Navigation et MVVM
ViewLocator (src/Omika.App/ViewLocator.cs)
IDataTemplate standard d'Avalonia : résout la vue à partir du type du ViewModel en
remplaçant le suffixe ViewModel par View (réflexion). Cas particulier pour
PlaceholderViewModel → PlaceholderPage. Retour Match sur ViewModelBase.
NavigationService (Navigation/NavigationService.cs, INavigationService.cs)
- Liste d'items
NavigationItem: Agenda (Home), Interventions, Dossiers, Équipe, Clients, Sites, Paramètres. Les 5 premiers sont « primaires » sur mobile, les autres passent par le menu « Plus ». - Pile de retour (
_backStackdeNavigationEntry) pourGoBack. - Événements
PageChanged/ParameterChanged(le Shell écoute pour recharger le VM). - Blocage en amont par droits via
PermissionGuard.IsPageAllowed— impossible de naviguer vers une page non autorisée (pas seulement masquée). - Le changement de page est purement visuel : aucun appel API.
ShellViewModel (ViewModels/ShellViewModel.cs)
Orchestre : page courante + ViewModel courant, titre, état de synchronisation
(SyncUiState), identité utilisateur (nom/initiales), mode immersif, badge de
synchronisation en attente (PendingSyncCount via Outbox), commande
SyncNowCommand (avec verrou anti-double-clic), filtrage du menu par droits,
RefreshCurrentPage() après sync.
RootViewModel (ViewModels/RootViewModel.cs)
Décide au démarrage entre écran d'activation et Shell. Règles :
- rejet terminal serveur (licence inactive/expirée/révoquée/device mismatch) → activation avec QR ;
- session valide (token valide, renouvelé, ou période offline autorisée) → Shell ;
- session présente mais hors grace ET serveur injoignable → activation sans QR (« Connexion requise ») ;
- aucune session → inscription QR.
PageViewModelFactory (Navigation/PageViewModelFactory.cs)
Fabrique les ViewModels métier par page, avec routage des paramètres (Guid,
AgendaItem, DetailNavigation, InterventionEditNavigation, EmailEditionNavigation,
NouveauEmailRapportNavigation, NouveauEmailContactNavigation, ImageEditorNavigation…).
Réutilise les instances Agenda / Agenda-équipe pour conserver date/filtres/mode au
retour. Câble SetShell pour résoudre la dépendance circulaire Shell ↔ Factory.
CommunityToolkit.Mvvm (ViewModels/ViewModelBase.cs)
ObservableObject de base, [ObservableProperty] (code généré), [RelayCommand],
helpers LogError/TrackFeature (télémétrie d'usage/erreurs best-effort).
Droits (Services/PermissionService.cs, Navigation/PermissionGuard.cs)
Droits centralisés par clés canoniques (MobilePermissionKeys : VoirClients,
VoirSites, VoirDossiers, PeutEnvoyerEmail, PeutCreerRapport,
PeutVoirAgendaEquipe, LicenceMobile, A.TEMP.COLB…). Le menu et les pages sont
filtrés/recalculés dès que le bootstrap (droits serveur) est rafraîchi.
3. La couche offline-first
3.1 Base SQLite (Sqlite/LocalDatabase.cs, Sqlite/SqliteSchema.cs)
- Fichier
omika.dbdans%LocalAppData%\Omika(desktop) / dossier app (mobile). - Schéma versionné (
CurrentVersion = 1), créé idempotent (CREATE TABLE IF NOT EXISTS) + migrations de colonnes viaPRAGMA table_info(ajout de colonnes sans détruire les lignes). - Séparation stricte :
- Tables métier (remplacées par snapshot) :
Client,Personne,Site,Intervention,Dossier,RendezVous,Collaborateur,DocumentMetadata,SuiviTemps— chacune =Id(PK GUID) +PayloadJson(DTO complet) + colonnes de référence typées (ClientId,SiteId,DossierId,InterventionId…). - Tables techniques (jamais purgées) :
Outbox,IdentityMapping,SyncState,DocumentCache,LocalBootstrap,MobileEnrollment,UiPreferences,PreferencesUtilisateur,SuiviTempsDraft,RendezVousTempsValidation,RendezVousDraftReference,InterventionCache,DossierLocalState,DossierInterventionCache,ContextInterventionCache(State),RapportBrouillon,NoteInterne, + caches de dernière recherche (DossierLastSearch,ClientLastSearch,SiteLastSearch).
- Tables métier (remplacées par snapshot) :
LocalRepository<TEntity>(Repositories/LocalRepository.cs) : repository génériqueUpsert/GetById/GetAll/Delete/Clear/Countsur ce schéma JSON + colonnes de référence (mapperReferenceColumnMapper).
3.2 L'Outbox (Services/OutboxService.cs, Models/OutboxOperation.cs, Models/OutboxState.cs, Repositories/OutboxRepository.cs)
- Chaque mutation métier locale produit une opération Outbox (idempotente).
OutboxOperationporte :OperationId(GUID de transaction immuable, créé une fois et conservé à travers les retries),EntityType,OperationType(CREATE/UPDATE/DELETE/ARCHIVE),RequestedEntityGuid(GUID local temporaire),CanonicalEntityGuid(GUID serveur après réconciliation A→B),Payload(JSON),DependencyEntityType/DependencyRequestedEntityGuid(ordonnancement),State,RetryCount,LastError.OutboxState:Pending → Sending → Accepted → WaitingResult → Succeeded | Rejected | StatusNotAvailable(7 états, récupérables après crash).
3.3 Le SyncEngine (Services/SyncEngine.cs) — cycle complet
Protégé par SemaphoreSlim (une seule sync à la fois). Cycle :
- Vérifier la connectivité (
INetworkConnectivity) — hors ligne → retour « Hors ligne. » sans aucune tentative HTTP. - Envoyer les opérations prêtes (
GetReadyAsync) en différant celles dont la dépendance (ex. note → intervention créée) est encore active ; marquageSendingavant envoi ;DeviceIdréel transmis (cohérence serveur token/opération). Erreur réseau → retourPending+RetryCount++. Documents : upload synchrone multipart (jamais de Base64) ; résultat connu immédiatement, sans polling. - Interroger les statuts (
GET /api/sync/status/{OperationId}) :Success(réconciliation A→B),Rejected(définitif, conservé pour diagnostic),StatusNotAvailable→ ré-émission bornée à 5 puis anomalie visible (jamais convertie artificiellement en succès). - Appliquer le snapshot atomiquement (voir ci-dessous).
Résultat structuré
SyncCycleResult(Executed, SnapshotApplied, OperationsSent/ Succeeded/Rejected/Anomaly, FailureStage, ExceptionType, Message).
3.4 SnapshotApplier (Services/SnapshotApplier.cs) — transaction atomique
- Le snapshot est entièrement téléchargé et validé AVANT toute modification.
- Transaction SQLite unique : remplace
Personne,Intervention,RendezVous(DELETE + INSERT), préserve les rendez-vous ayant une mutation locale en attente (annulation non confirmée), puis purge les tables « à la demande » (Client, Site, Dossier, Collaborateur, DocumentMetadata, SuiviTemps). - En cas d'erreur : rollback — l'ancienne base métier reste intacte.
- Les tables techniques (Outbox, IdentityMapping, DocumentCache, LocalBootstrap, favoris, brouillons…) ne sont jamais touchées.
3.5 Réconciliation d'identités (Services/MobileIdentityReconciliationService.cs)
GUID mobile temporaire (A) → GUID canonique serveur (B). Idempotente et
transactionnelle : enregistre le mapping (IdentityMapping), réécrit la PK
logique, réconcilie le cache document, les références enfants (foreign keys
locales), l'Outbox en attente, et réécrit les payloads Outbox via des
IOutboxPayloadRewriter typés (PayloadRewriters/ClientPayloadRewriter.cs,
SitePayloadRewriter.cs, DossierPayloadRewriter.cs, InterventionPayloadRewriter.cs,
RendezVousPayloadRewriter.cs, NoteInternePayloadRewriter.cs,
NoteAdministrativePayloadRewriter).
3.6 Cache document (Services/DocumentCacheService.cs, Repositories/DocumentCacheRepository.cs)
- Les binaires sont dans des fichiers locaux (
%LocalAppData%\Omika\documents) ; SQLite ne contient que les métadonnées (DocumentCache: LocalPath, NomFichier, MimeType, Size, State). - Téléchargement à la demande, survit au snapshot, purge possible depuis les Paramètres.
3.7 Détection de connectivité
- Repli portable (
Services/NetworkConnectivity.cs) : interfaceNetworkInterfaceactive non loopback. - Android (
AndroidNetworkConnectivity.cs, branché dansApplication.cs) :ConnectivityManager— évite les faux positifs « en ligne » (mode avion, hotspot sans Internet).
4. Session et activation
4.1 MobileSessionService (Services/MobileSessionService.cs)
- Token stocké en secure storage dédié, jamais en SQLite en clair
(
ISecureTokenStore) ; le bootstrap (profil/droits/licence) est, lui, en SQLite (LocalBootstrapState, singleton). - Période offline autorisée : une session déjà validée reste accessible hors
connexion pendant
OfflineGraceDaysjours, calculés strictement depuis la dernière confirmation serveur (LastLicenseValidationUtc), jamais depuis une écriture locale. - Renouvellement single-flight (
SemaphoreSlim) : un seullicense/validateà la fois ; mémorisation d'un rejet terminal pour ne pas marteler le serveur. ClearSessionAsyncsupprime seulement token + bootstrap — jamais les données métier (SQLite, cache, Documents, Outbox).RefreshBootstrapAsyncrecharge référentiels/équipes/collaborateurs/droits sans repousser la grace.
4.2 Flux d'activation côté mobile (Services/MobileEnrollmentService.cs, Services/LicenseEnrollmentClient.cs)
- Lecture du QR d'inscription (
EnrollmentQrCodec.Decode, vérification de signature parkidviaSecurity/EnrollmentVerificationKey.cs). - Validation stricte HTTPS non-local de l'URL du service de licence
(
EndpointValidator). - Appel
POST /api/license/activateavecCodeClient,NumeroLicence,DeviceId- snapshot d'appareil.
- Validation HTTPS de l'URL d'API Omika retournée par le serveur.
- Persistance de la configuration (
MobileEnrollment/MobileEnrollmentStore). ApplyEnrollmentSessionAsync: token en secure storage + bootstrap en SQLite. Aucune URL codée en dur — tout provient du QR puis du serveur de licence.
4.3 Licence de développement (Debug uniquement)
Services/DevelopmentLicenseProvider.cs+DevelopmentLicenseConfigurationLoader.cs: licence locale valide chargée depuis un fichier non sensibledevelopment-license.json, compilée sous#if DEBUGuniquement — en Release, le provider retourne toujoursnull(le serveur reste l'autorité).- Permet de tester navigation/SQLite/snapshot/offline sans base de licences HFSQL.
EnsureDevelopmentSessionAsyncobtient un vrai token signé viaPOST /api/dev/sessionpour le flux Desktop de développement.
4.4 Secure storage par plateforme
- Android :
AndroidSecureTokenStore— Keystore AES-GCM, clé non exportée, supprimée à la désinstallation (→ réinstallation = écran QR).LegacyTokenMigratormigre l'anciensession.tokenen clair vers le blob chiffré. - iOS :
IosSecureTokenStore— Keychain ;IosTokenBootstrapgère le marker d'installation (purge Keychain à la réinstallation) + migration. - Desktop/fallback :
PlatformSecureTokenStore(fichier, développement).
5. Le client HTTP (Services/MobileApiClient.cs, ~1135 lignes)
- Client HTTP typé vers l'API mobile Omika, réutilisant les contrats backend
partagés (
Omika.Core.Contracts.Mobile.*). BaseUrl:nulltant que l'appareil n'est pas inscrit, puis fixée depuis la configuration d'inscription (jamais de localhost/fallback codé en dur).RequireBaseUrl()lève si aucune URL.- Bearer token :
AccessTokenposé viaAuthorization: BearerdansCreateAuthenticatedRequest(jamais loggé). - ~50 endpoints : licence (
activate/validate/dev/session), bootstrap, sync (operations,status/{id},snapshot), recherche intervention/dossier/client/ site/collaborateur, fiches par GUID, documents (download/upload), notes, emails, modèles (mail/rapport), génération (email/rapport), compte rendu, suivi temps. - Deux
DelegatingHandlerbranchés dansAppServices:TraceIdPropagationHandler— propagation TraceId/SessionId (corrélation).SessionRefreshHandler— renouvellement 401 single-flight (réémission transparente), anti-récursion via optionSkipAuthRefreshsur les appels licence.
6. Plateformes
Desktop (src/Omika.App.Desktop/Program.cs)
WinExe,[STAThread],IClassicDesktopStyleApplicationLifetime.- Capture globale des exceptions non gérées →
crash.log+ télémétrie (ExceptionTelemetry). Rendu Windows/macOS/Linux viaAvalonia.Desktop.
Android (src/Omika.App.Android/)
MainActivity(AvaloniaMainActivity) : demande de permission caméra runtime, sélecteur de fichiers SAF (ACTION_OPEN_DOCUMENT, multi-sélection), capture photo (ACTION_IMAGE_CAPTURE+ FileProvider), gestion des résultats, handlers d'exceptions natives (AndroidEnvironment.UnhandledExceptionRaiser).Application.cs(CustomizeAppBuilder) : installe tous les services natifs AVANT l'init Avalonia —AndroidEnrollmentQrScanner(Camera2 + ZXing), launcherstel:/mailto:/geo:,AndroidFilePicker,AndroidPhotoCapture,AndroidFileLauncher,AndroidSecureTokenStore(Keystore),AndroidDeviceInfoProvider,AndroidNetworkConnectivity(ConnectivityManager), migration du token.
iOS (src/Omika.App.iOS/)
AppDelegate.cs(CustomizeAppBuilder) :IosEnrollmentQrScanner, launcherstel:/mailto:/Apple Maps,IosFilePicker(UIDocumentPicker + PHPicker photos),IosPhotoCapture(UIImagePickerController),IosFileLauncher(UIDocumentInteractionController),IosSecureTokenStore(Keychain) +IosTokenBootstrap(marker d'installation),IosDeviceInfoProvider.Main.cs: entréeUIApplication.Main.
Points spécifiques transverse
- QR code : génération (
QrCodeToImageConverter.cs) + scan natif (aperçu caméra en direct viaIQrScannerPreviewSink/LicenseActivationViewModel.OnFrame). - Caméra/photo : capture native + optimisation du poids (
SkiaImageOptimizer)- date EXIF (
MetadataDocumentDateResolver) avant enregistrement.
- date EXIF (
- Téléphone/email/carte : abstractions
IPhoneLauncher/IEmailLauncher/IMapLauncheravec implémentations natives par plateforme. - Secure storage : Keystore / Keychain / fichier (voir §4.4).
7. Extraction marketing (fonctionnalités concrètes, avec preuve technique)
| Argument marketing | Réalité technique (fichier) |
|---|---|
| 100 % hors-ligne | Agenda et fiches lisent SQLite locale sans appel API (AgendaViewModel.cs « Ne déclenche aucun appel API », InterventionDetailViewModel.cs). Les mutations sont écrites localement puis envoyées plus tard (Outbox). |
| Synchronisation automatique | Sync au lancement (App.axaml.cs StartInitialSyncAsync) + après inscription + manuelle (ShellViewModel.SyncNowAsync). |
| Aucune perte de données | Snapshot atomique avec rollback (SnapshotApplier.cs), préservation des RDV en attente, réconciliation d'identités idempotente (MobileIdentityReconciliationService.cs). |
| File d'attente hors-ligne fiable | Outbox avec états récupérables après crash, ré-émission bornée, jamais de faux succès (SyncEngine.cs, OutboxState.cs). |
| Multi-plateforme, une seule base de code | ~165 fichiers UI partagés (Omika.App), 3 hôtes fins (Desktop/Android/iOS). |
| Interface native | Avalonia Fluent + Inter, adaptative (rail desktop vs barre basse mobile, ShellView.axaml), thème clair/sombre/système + couleur personnalisable (ThemeService, ParametresViewModel). |
| Activation sécurisée par QR | QR signé (kid), validation HTTPS stricte, token en Keystore/Keychain (MobileEnrollmentService, AndroidSecureTokenStore, IosSecureTokenStore). |
| Photos/documents optimisés | Compression poids (SkiaSharp), date réelle EXIF, cache fichier, upload multipart sans Base64 (DocumentLocalService, SkiaImageOptimizer, MetadataDocumentDateResolver). |
| Signature + annotation d'image | Controls/SignaturePad.axaml, ImageEditorView/ImageEditorControl + SignatureStrokeTracker. |
| Emails & rapports depuis modèles | NouveauEmailView, RapportChampsView, génération serveur (/generation/email, /generation/rapport). |
| Suivi des temps intelligent | Pré-remplissage, pause déjeuner, saisie pour autrui (A.TEMP.COLB), brouillons offline (RendezVousDetailViewModel, SuiviTempsLocalService). |
| Sécurité par droits | Menu/pages filtrés par droits serveur, rechargés à chaque sync (PermissionGuard, PermissionService). |
| Diagnostic & support intégrés | Télémétrie locale (JSONL), export logs en email au support, capture de crash (AppServices, SupportLogExporter, ExceptionTelemetry). |
| Gestion de licence & licences collaborateurs | Activation/validation/grace offline, attribution/révocation de licence mobile (MobileSessionService, MobileApiClient.AssignMobileLicenceAsync/ReleaseMobileLicenceAsync). |
| Cartographie des interventions | Controls/LieuMap.axaml (Mapsui) + itinéraire (IMapLauncher). |
8. Limites / risques
- Journalisation de diagnostic « temporaire » abondante : de nombreux
Console.WriteLinemarqués « à retirer après diagnostic » subsistent dans le code de production ([TOKEN-DIAG],[INTERVENTION-HTTP],[API-CONFIG],[STARTUP],REFRESH START/SUCCESS,SQLITE IMPORT END) —MobileSessionService.cs,MobileApiClient.cs,AppServices.cs,SyncEngine.cs. Aucun secret n'est loggé, mais c'est du bruit à nettoyer avant une démo publique. - Licence de développement (Debug) : bypass local
#if DEBUG— correctement compilé hors Release, mais à ne jamais laisser fuir en production (DevelopmentLicenseProvider.cs). - Commentaires d'énumération obsolètes :
AppPage.csqualifie encore Agenda/ Interventions/Dossiers/Clients/Sites de « placeholder pour ce lot » alors qu'ils sont entièrement implémentés (PageViewModelFactory.csroute tout vers de vrais ViewModels). Risque de confusion lors de la relecture. - Écran orphelin :
CollaborateursView.axaml+CollaborateursViewModel.csexistent mais ne sont pas référencés par la navigation (le menu « Équipe » passe parEquipesView/EquipeDetail). - Composition racine manuelle :
AppServices.cs(~840 lignes) câble tout sans conteneur DI — explicite mais long à maintenir. - Snapshot réduit : le snapshot ne fournit plus que Personne/Intervention/
RendezVous ; Client/Site/Dossier/Collaborateur/DocumentMetadata/SuiviTemps sont
« à la demande » et purgés à chaque snapshot (
SnapshotApplier.cs). La première consultation hors ligne de ces fiches dépend donc des caches/épinglages locaux (DossierLocalState,ClientLocalState,SiteLocalState,InterventionCache,ContextInterventionCache) — comportement à bien expliquer (les favoris et la dernière recherche restent disponibles offline). - Connectivité portable naïve : le repli
NetworkConnectivity(interface active ≠ internet) est corrigé sur Android parConnectivityManager, mais iOS et desktop utilisent le repli. - Stockage du token Desktop : le repli fichier (
PlatformSecureTokenStore) est moins robuste que Keystore/Keychain (documenté « développement/desktop »). - Grace offline pilotée serveur : la durée hors-ligne autorisée dépend de
OfflineGraceDaysrenvoyé par la licence ; en mode Dev elle est fixée à la durée d'expiration (DevelopmentLicenseProvider.cs).
Résumé (5 lignes)
Omika est un logiciel de gestion d'interventions offline-first réécrit en .NET 10 / Avalonia UI, avec une UI partagée unique déployée sur desktop (Windows/macOS/Linux), Android et iOS. Il couvre l'ensemble du cycle terrain — agenda/planning, interventions, dossiers, clients, sites, équipe, suivi des temps, comptes rendus, documents/emails — avec une base SQLite locale et un moteur de synchronisation robuste (Outbox + snapshot atomique + réconciliation d'identités). L'activation par QR sécurisée et le token en Keystore/Keychain assurent un onboarding et une session maîtrisés, avec période offline autorisée. Les points de vigilance avant commercialisation sont la journalisation de diagnostic temporaire à nettoyer, quelques commentaires/écrans obsolètes, et le repli fichier du token desktop.