Files
2026-10-23 18:46:50 +02:00

263 lines
12 KiB
C#

// 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<string> 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<IReadOnlyDictionary<string, object?>>();
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<object?>().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<object?>(), 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<string, object?> 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<string, object?> 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] + "…";