using MaBibli.Shared.Isbn; using MaBibli.Shared.Textes; namespace MaBibli.Shared.Entites; /// /// Une revue — ou un numéro précis — que l'on aimerait acquérir. Personnelle, comme /// . /// /// /// Pourquoi une table sœur plutôt que des colonnes de plus sur ? /// Une envie de livre est identifiée par un couple (œuvre, auteur) : c'est ce que porte son index /// unique, et c'est ce qui permet de la rapprocher du catalogue. Un numéro de revue n'a /// pas d'auteur et se distingue par son numéro — deux notions que la clé de /// LivreSouhaite ne sait pas exprimer sans devenir fausse pour tout le monde. Y loger les /// revues aurait obligé à écrire « et qui n'est pas une revue » à chaque rapprochement, à chaque /// export et à chaque compteur : c'est exactement le raisonnement qui a séparé /// de . /// /// ⚠️ Le coût est assumé et il est réel : deux tables se fusionnent à l'affichage, dans /// les deux exports et dans l'instantané hors-ligne. C'est là que le sujet se rate s'il est /// bâclé — une envie de revue invisible en librairie ne sert à rien. /// /// /// est une frontière, pas une trace : aucun point d'entrée /// n'accepte de nom d'utilisateur, et l'envie d'un autre répond 404, jamais 403. /// /// public class RevueSouhaitee { public int Id { get; set; } /// /// YNH_USER du propriétaire de l'envie. Frontière : toute lecture filtre dessus. /// public string Utilisateur { get; set; } = string.Empty; /// Titre de la revue (« Médor »), sans son numéro. public string Titre { get; set; } = string.Empty; /// Titre mis à plat. Sert au dédoublonnage. /// /// ⚠️ et non CleOeuvre : le titre d'une revue n'a ni /// mention de responsabilité ni sous-titre à couper, et « Médor : les yeux ouverts » est une /// accroche qu'on ne tape pas. C'est la même règle que . /// public string TitreNormalise { get; set; } = string.Empty; /// /// Numéro souhaité (« 43 », « hors-série 7 »), ou null pour la revue entière. /// /// /// Facultatif, et c'est la moitié du besoin : on souhaite parfois un numéro précis vu en /// kiosque, parfois l'abonnement à un titre qu'on ne possède pas encore. /// public string? Numero { get; set; } /// /// Numéro mis à plat. Jamais null — chaîne vide pour « la revue entière ». /// /// /// ⚠️ Même piège que : SQLite tient deux /// NULL pour distincts, et l'unicité laisserait alors passer autant de fois « Médor, /// pas de numéro » qu'on voudrait. /// public string NumeroNormalise { get; set; } = string.Empty; /// /// ISSN, facultatif, canonisé avec son tiret (« 2466-6718 »). /// /// /// C'est la forme rangée en base pour tout le projet — seule exception à « la valeur stockée /// reste nue », l'ISSN étant produit à tiret par le code-barres et interrogé à tiret par la /// BnF. Sans canonisation, un ISSN tapé « 24666718 » ne se rapprocherait de rien. /// public string? Issn { get; set; } /// Note libre : « vu chez le dentiste », « demander au kiosque ». public string? Note { get; set; } public DateTime DateAjout { get; set; } /// /// Rang dans la section « revues » de la liste, du plus désiré au moins désiré. /// /// /// ⚠️ Cette numérotation est indépendante de celle de : /// chaque table repart de zéro par utilisateur. C'est précisément pourquoi la liste affiche /// deux sections plutôt qu'un seul classement entrelacé — deux suites de rangs sans rapport /// mélangées produiraient un ordre que personne n'a choisi. /// public int Rang { get; set; } public void RecalculerFormes() { Titre = Titre.Trim(); TitreNormalise = NormalisationTexte.Normaliser(Titre); Numero = string.IsNullOrWhiteSpace(Numero) ? null : Numero.Trim(); NumeroNormalise = Numero is null ? string.Empty : NormalisationTexte.Normaliser(Numero); // Canonique() rend la saisie élaguée quand ce n'est pas un ISSN : un code mal recopié // est conservé tel quel plutôt que jeté — le voir mal formé vaut mieux que le voir // disparaître. Issn = FormatageIssn.Canonique(Issn); } }