// Sonde LECTURE SEULE : rendez-vous créés « aujourd'hui » dans OMIKA_TEST. // Rejoue, étape par étape, les filtres du snapshot mobile pour déterminer lequel // écarte un rendez-vous (groupe, plusieurs jours, plusieurs collaborateurs). // // Aucune écriture. Aucune donnée personnelle loggée : seuls les identifiants // techniques (PKID, GUID), dates, drapeaux et clés métier sont affichés. using System.Globalization; using Microsoft.Extensions.Configuration; using Omika.Data.Configuration; using Omika.Data.Connection; using Omika.Data.HfSql.Connection; var configPath = Path.GetFullPath(Path.Combine( Directory.GetCurrentDirectory(), "config", "hfsql-test.local.json")); var config = new ConfigurationBuilder() .AddJsonFile(configPath, optional: false, reloadOnChange: false) .Build(); var hfsqlOptions = new HfsqlOptions(); config.GetSection(HfsqlOptions.SectionName).Bind(hfsqlOptions); DateTime today = DateTime.Today; // Fenêtre d'agenda du snapshot mobile : [-14 j, +28 j], borne SQL exclusive sur +1 jour. DateTime sqlDebutMin = today.AddDays(-14); DateTime sqlDebutMax = today.AddDays(29); // Collaborateur de test (ASA) : GUID canonique + PK numérique. string asaGuid = "62224eca-4565-4f47-9197-5c7439400000"; long asaPk = 281474976710675; // Formes physiques réellement stockées dans les rubriques GUID Omika (la // rubrique RVC_GUID_Colb fait 32 caractères : la forme WinDev « D » tronquée // des quatre derniers hexadécimaux y est fréquente). IReadOnlyList asaCandidats = Omika.Data.Mapping.Common.PhysicalGuidParser .GetStorageCandidates(Guid.Parse(asaGuid)); bool EstAsa(string valeur) => asaCandidats.Any(c => string.Equals(c, valeur, StringComparison.OrdinalIgnoreCase)); Console.WriteLine($"=== Sonde rendez-vous (base {hfsqlOptions.DatabaseName}) ==="); Console.WriteLine($"aujourd'hui = {today:yyyy-MM-dd} ({today.DayOfWeek})"); Console.WriteLine($"fenêtre agenda = {sqlDebutMin:yyyy-MM-dd} .. {sqlDebutMax:yyyy-MM-dd} (SQL exclusif)"); Console.WriteLine(); var factory = new HfSqlOleDbConnectionFactory(); await using var business = new BusinessOleDbConnection(hfsqlOptions, factory); await business.OpenAsync(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); CancellationToken ct = cts.Token; // ----------------------------------------------------------------------------- // 1. Tous les rendez-vous créés aujourd'hui (date de création = aujourd'hui). // ----------------------------------------------------------------------------- Console.WriteLine("--- 1. RDV_Planning créés aujourd'hui ---"); var rvDuJour = await business.QueryAsync( "SELECT RV_PKID, GUID, RV_Objet, RV_CleType, RV_CleStatut, RV_Date, RV_Groupe, " + "RV_PlusieursJours, RV_IdInter, RV_IdDossier, RV_IdSociete, Archive, CreationDate " + "FROM RDV_Planning WHERE CreationDate >= ? AND CreationDate < ? ORDER BY RV_PKID", [today, today.AddDays(1)], ct); Console.WriteLine($"nombre de rendez-vous créés aujourd'hui = {rvDuJour.Count}"); foreach (var row in rvDuJour) { Console.WriteLine( $" RV_PKID={Str(row, "RV_PKID")} GUID={Str(row, "GUID")} groupe={Str(row, "RV_Groupe")} " + $"plusieursJours={Str(row, "RV_PlusieursJours")} archive={Str(row, "Archive")} " + $"type={Str(row, "RV_CleType")} statut={Str(row, "RV_CleStatut")} date={Str(row, "RV_Date")} " + $"idInter={Str(row, "RV_IdInter")} idDossier={Str(row, "RV_IdDossier")} idSociete={Str(row, "RV_IdSociete")} " + $"cree={Str(row, "CreationDate")} objet={Trunc(Str(row, "RV_Objet"), 60)}"); } Console.WriteLine(); // ----------------------------------------------------------------------------- // 2. Affectations (RDV_Collaborateur) de ces rendez-vous. // ----------------------------------------------------------------------------- Console.WriteLine("--- 2. Affectations RDV_Collaborateur des RDV ci-dessus ---"); var guidList = rvDuJour .Select(r => Str(r, "GUID")) .Where(g => !string.IsNullOrWhiteSpace(g)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); var affectations = new List>(); if (guidList.Count > 0) { string placeholders = string.Join(", ", guidList.Select(_ => "?")); affectations = (await business.QueryAsync( "SELECT RVC_PKID, RVC_GUID_RDV, RVC_GUID_Colb, RVC_DateDeb, RVC_DateFin, RVC_CleStatut, " + "RVC_Responsable, RVC_TempsValide, Archive, RVC_CR " + $"FROM RDV_Collaborateur WHERE RVC_GUID_RDV IN ({placeholders}) ORDER BY RVC_GUID_RDV, RVC_PKID", guidList.Cast().ToList(), ct)).ToList(); } Console.WriteLine($"nombre d'affectations = {affectations.Count}"); foreach (var rdv in guidList) { var lignes = affectations .Where(a => string.Equals(Str(a, "RVC_GUID_RDV"), rdv, StringComparison.OrdinalIgnoreCase)) .ToList(); Console.WriteLine($" RDV {rdv} : {lignes.Count} affectation(s)"); foreach (var a in lignes) { DateTime deb = ToDateTime(a, "RVC_DateDeb"); DateTime fin = ToDateTime(a, "RVC_DateFin"); bool debDansFenetre = deb >= sqlDebutMin && deb < sqlDebutMax; Console.WriteLine( $" RVC_PKID={Str(a, "RVC_PKID")} collab={Str(a, "RVC_GUID_Colb")} " + $"deb={deb:yyyy-MM-dd HH:mm} fin={fin:yyyy-MM-dd HH:mm} " + $"statut={Str(a, "RVC_CleStatut")} archive={Str(a, "Archive")} " + $"resp={Str(a, "RVC_Responsable")} cr={Str(a, "RVC_CR")} " + $"debDansFenetreSQL={debDansFenetre}"); } } Console.WriteLine(); // ----------------------------------------------------------------------------- // 3. Collaborateurs référencés (GUID → PK / trigramme / actif / auPlanning). // ----------------------------------------------------------------------------- Console.WriteLine("--- 3. Collaborateurs référencés par ces affectations ---"); var collabGuids = affectations .Select(a => Str(a, "RVC_GUID_Colb")) .Where(g => !string.IsNullOrWhiteSpace(g)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); foreach (var guid in collabGuids) { var rows = await business.QueryAsync( "SELECT CO_PKID, CO_Nom, CO_Prenom, CO_Trigramme, CO_Actif, CO_AuPlanning, GUID FROM Collaborateur WHERE GUID = ?", [guid], ct); if (rows.Count == 0) { Console.WriteLine($" GUID {guid} → AUCUN collaborateur (forme physique inventoriée : {Omika.Data.Mapping.Common.PhysicalGuidParser.ParseOrNull(guid)?.ToString("D") ?? "-"})"); continue; } var c = rows[0]; Console.WriteLine( $" GUID {guid} → CO_PKID={Str(c, "CO_PKID")} trigramme={Str(c, "CO_Trigramme")} " + $"actif={Str(c, "CO_Actif")} auPlanning={Str(c, "CO_AuPlanning")} " + $"nom={Trunc(Str(c, "CO_Nom"), 20)} prenom={Trunc(Str(c, "CO_Prenom"), 20)} " + $"{(EstAsa(guid) ? " <== COLLABORATEUR TEST (ASA)" : string.Empty)}"); } Console.WriteLine(); // ----------------------------------------------------------------------------- // 4. Type de rendez-vous : drapeau « Envoi_mobile » du paramétrage métier. // ----------------------------------------------------------------------------- Console.WriteLine("--- 4. Types de rendez-vous utilisés + drapeau Envoi_mobile ---"); var types = rvDuJour .Select(r => Str(r, "RV_CleType")) .Where(t => !string.IsNullOrWhiteSpace(t)) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); foreach (var type in types) { var rows = await business.QueryAsync( "SELECT PA_Code, PA_Libelle, PA_Parametre FROM ParametreMetier WHERE PA_Code = ?", [type], ct); if (rows.Count == 0) { Console.WriteLine($" type '{type}' → AUCUN ParametreMetier"); continue; } var p = rows[0]; string parametre = Str(p, "PA_Parametre"); Console.WriteLine( $" type '{type}' libelle='{Trunc(Str(p, "PA_Libelle"), 40)}' " + $"Envoi_mobile_present={parametre.Contains("Envoi_mobile", StringComparison.OrdinalIgnoreCase)} " + $"parametre={Trunc(parametre, 220)}"); } Console.WriteLine(); // ----------------------------------------------------------------------------- // 5. Simulation du snapshot pour le collaborateur de test : chaque filtre. // ----------------------------------------------------------------------------- Console.WriteLine($"--- 5. Simulation snapshot pour ASA (GUID {asaGuid} / CO_PKID {asaPk}) ---"); foreach (var row in rvDuJour) { string guid = Str(row, "GUID"); var lignes = affectations .Where(a => string.Equals(Str(a, "RVC_GUID_RDV"), guid, StringComparison.OrdinalIgnoreCase)) .ToList(); var asaLigne = lignes.FirstOrDefault(a => EstAsa(Str(a, "RVC_GUID_Colb"))); Console.WriteLine($" RV {guid} ({Trunc(Str(row, "RV_Objet"), 50)})"); Console.WriteLine($" filtre SQL collaborateur (RVC_GUID_Colb = ASA) : {(asaLigne is not null ? "PASSE" : "ÉCARTÉ")}"); if (asaLigne is null) { Console.WriteLine($" → affectations présentes : {string.Join(", ", lignes.Select(l => Str(l, "RVC_GUID_Colb")))}"); continue; } DateTime deb = ToDateTime(asaLigne, "RVC_DateDeb"); DateTime fin = ToDateTime(asaLigne, "RVC_DateFin"); bool debDansFenetre = deb >= sqlDebutMin && deb < sqlDebutMax; Console.WriteLine($" filtre SQL fenêtre (RVC_DateDeb) : {(debDansFenetre ? "PASSE" : "ÉCARTÉ")} ({deb:yyyy-MM-dd})"); bool periodeExploitable = deb != default; Console.WriteLine($" date début d'affectation exploitable : {(periodeExploitable ? "PASSE" : "ÉCARTÉ (default)")}"); bool dansFenetreMemoire = deb.Date >= today.AddDays(-14) && deb.Date <= today.AddDays(28); Console.WriteLine($" fenêtre mémoire [-14,+28] sur Debut rabattu : {(dansFenetreMemoire ? "PASSE" : "ÉCARTÉ")}"); bool archive = Str(asaLigne, "Archive") is "True" or "true" or "1"; Console.WriteLine($" affectation archivée : {(archive ? "ÉCARTÉ" : "PASSE")}"); } Console.WriteLine(); // ----------------------------------------------------------------------------- // 6. Autres rendez-vous (créés avant aujourd'hui) touchant le collaborateur test, // pour comparaison : ce sont ceux qui SONT affichés. // ----------------------------------------------------------------------------- Console.WriteLine("--- 6. Comparaison : tous les RDV affectés à ASA dans la fenêtre SQL ---"); string asaPlaceholders = string.Join(", ", asaCandidats.Select(_ => "?")); var rvAsa = await business.QueryAsync( "SELECT RDV_Planning.RV_PKID, RDV_Planning.GUID, RDV_Planning.RV_Objet, RDV_Planning.RV_Groupe, " + "RDV_Planning.RV_PlusieursJours, RDV_Planning.Archive, RDV_Planning.CreationDate, " + "RDV_Collaborateur.RVC_DateDeb, RDV_Collaborateur.RVC_DateFin, RDV_Collaborateur.Archive " + "FROM RDV_Planning INNER JOIN RDV_Collaborateur ON RDV_Collaborateur.RVC_GUID_RDV = RDV_Planning.GUID " + $"WHERE RDV_Collaborateur.RVC_GUID_Colb IN ({asaPlaceholders}) AND RDV_Collaborateur.RVC_DateDeb >= ? " + "AND RDV_Collaborateur.RVC_DateDeb < ? ORDER BY RDV_Collaborateur.RVC_DateDeb", [.. asaCandidats.Cast(), sqlDebutMin, sqlDebutMax], ct); Console.WriteLine($"nombre = {rvAsa.Count}"); foreach (var r in rvAsa) { Console.WriteLine( $" RV={Str(r, "RV_PKID")} GUID={Str(r, "GUID")} groupe={Str(r, "RV_Groupe")} " + $"plusieursJours={Str(r, "RV_PlusieursJours")} archiveRV={Str(r, "Archive")} " + $"deb={ToDateTime(r, "RVC_DateDeb"):yyyy-MM-dd} fin={ToDateTime(r, "RVC_DateFin"):yyyy-MM-dd} " + $"cree={Str(r, "CreationDate")}"); } Console.WriteLine(); Console.WriteLine("Sonde terminée (lecture seule)."); // --- Helpers --- static string Str(IReadOnlyDictionary row, string column) => row.TryGetValue(column, out var value) && value is not null ? Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty : string.Empty; static DateTime ToDateTime(IReadOnlyDictionary row, string column) { if (row.TryGetValue(column, out var value) && value is not null) { if (value is DateTime dt) return dt; if (DateTime.TryParse(Convert.ToString(value, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsed)) return parsed; } return default; } static string Trunc(string value, int max) => value.Length <= max ? value : value[..max] + "…";