Clôt la décision du premier lot, qui écartait les périodiques faute de modèle. Douze numéros d'un magazine ne font plus douze fiches identiques. Table à part, pour la troisième fois et pour le même motif que la liste d'envies et les séries : logée dans Livres, une revue entrerait dans le catalogue, ses compteurs, les doublons et les bibliographies, et il faudrait écrire « et qui n'est pas une revue » à chaque lecture. Un numéro est recensé, rien de plus — ni prêt ni statut, tous deux liés à Livre par clé étrangère. Réversible. « Créer ou retrouver » et non « créer » : scanner le numéro suivant du même magazine retombe forcément sur la même revue. L'unicité de l'ISSN est un index partiel, sans quoi les revues sans ISSN se bloqueraient entre elles. Le flux 977 mène désormais à la fiche de la revue, et ne pré-remplit plus le formulaire d'un livre. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
104 lines
4.1 KiB
C#
104 lines
4.1 KiB
C#
using MaBibli.Shared.Textes;
|
|
|
|
namespace MaBibli.Shared.Entites;
|
|
|
|
/// <summary>
|
|
/// Une revue — magazine, périodique —, et non l'un de ses numéros.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <b>Pourquoi une table à part et non une ligne de <c>Livres</c> ?</b> Même raisonnement que
|
|
/// pour <see cref="LivreSouhaite"/> et <see cref="Serie"/>, et il vaut d'être répété : logée
|
|
/// dans <c>Livres</c>, une revue entrerait <b>mécaniquement</b> dans le catalogue, ses
|
|
/// compteurs, la détection de doublons, les séries et les bibliographies par auteur — et il
|
|
/// faudrait écrire « et qui n'est pas une revue » à chaque lecture. Un invariant qu'on réécrit
|
|
/// partout finit par être oublié quelque part.
|
|
/// <para>
|
|
/// <b>Une fiche par revue, les numéros à l'intérieur.</b> C'est ce qui évite le défaut redouté
|
|
/// dès le premier lot : douze numéros d'un même magazine ne font pas douze fiches identiques,
|
|
/// puisqu'ils partagent la fiche de leur revue.
|
|
/// </para>
|
|
/// <para>
|
|
/// ⚠️ L'<see cref="Issn"/> est un identifiant de <b>revue</b>, jamais un ISBN. Ranger un code
|
|
/// <c>977</c> dans <c>Livre.Isbn</c> ferait échouer tout lookup ultérieur sur cette fiche —
|
|
/// c'est précisément ce que cette table évite.
|
|
/// </para>
|
|
/// </remarks>
|
|
public class Revue
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public string Titre { get; set; } = string.Empty;
|
|
|
|
/// <summary>Titre mis à plat. Index <b>unique</b> : une revue, une fiche.</summary>
|
|
public string TitreNormalise { get; set; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// ISSN sous sa forme canonique à tiret (<c>2466-6718</c>), quand on le connaît.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Facultatif : une revue se catalogue à la main sans que personne n'ait scanné son
|
|
/// code-barres. Son unicité est portée par un index <b>partiel</b> — deux <c>NULL</c> sont
|
|
/// distincts pour SQLite, une unicité simple laisserait donc passer autant de revues sans
|
|
/// ISSN qu'on veut, ce qui est justement le comportement voulu.
|
|
/// </remarks>
|
|
public string? Issn { get; set; }
|
|
|
|
public string? Editeur { get; set; }
|
|
|
|
public DateTime DateAjout { get; set; }
|
|
|
|
/// <summary>Trace de saisie. <b>Pas une frontière</b> : les revues sont communes au foyer.</summary>
|
|
public string? AjoutePar { get; set; }
|
|
|
|
public List<NumeroRevue> Numeros { get; set; } = [];
|
|
|
|
public void RecalculerFormes()
|
|
{
|
|
Titre = Titre.Trim();
|
|
TitreNormalise = NormalisationTexte.Normaliser(Titre);
|
|
Issn = string.IsNullOrWhiteSpace(Issn) ? null : Issn.Trim();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Un numéro possédé d'une revue.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <b>Décidé le 2026-08-19 : un numéro est recensé, rien de plus.</b> Ni prêt, ni statut de
|
|
/// lecture — ces deux mécanismes sont attachés à <see cref="Livre"/> par clé étrangère, et les
|
|
/// rouvrir demanderait une seconde table de prêts ou une parenté commune, pour un usage non
|
|
/// confirmé. Le choix est <b>réversible</b> : les ajouter plus tard ne détruit rien de ce qui
|
|
/// est enregistré ici.
|
|
/// </remarks>
|
|
public class NumeroRevue
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public int RevueId { get; set; }
|
|
|
|
public Revue? Revue { get; set; }
|
|
|
|
/// <summary>
|
|
/// Le numéro tel qu'il est imprimé (« 43 », « hors-série 7 »). <b>Obligatoire</b> :
|
|
/// sans lui, deux numéros de la même revue seraient indiscernables.
|
|
/// </summary>
|
|
public string Numero { get; set; } = string.Empty;
|
|
|
|
/// <summary>Forme mise à plat, porteuse de l'unicité au sein de la revue.</summary>
|
|
public string NumeroNormalise { get; set; } = string.Empty;
|
|
|
|
/// <summary>Date de parution, quand elle est connue. En UTC comme toutes les dates.</summary>
|
|
public DateTime? DateParution { get; set; }
|
|
|
|
/// <summary>Note libre : « dossier sur l'eau », « prêté à Paul », « acheté en gare ».</summary>
|
|
public string? Note { get; set; }
|
|
|
|
public DateTime DateAjout { get; set; }
|
|
|
|
public void RecalculerFormes()
|
|
{
|
|
Numero = Numero.Trim();
|
|
NumeroNormalise = NormalisationTexte.Normaliser(Numero);
|
|
}
|
|
}
|