Files
Mathieu LimonierandClaude Opus 5 6a6d745af4 MaBibli 1.0.0
Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts,
scan de code-barres, consultation hors-ligne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:36:16 +02:00

110 lines
4.6 KiB
C#

using MaBibli.Shared.Textes;
namespace MaBibli.Shared.Entites;
/// <summary>
/// Un regroupement ordonné de livres : trilogie, série, cycle, intégrale.
/// </summary>
/// <remarks>
/// C'est la première notion du projet qui porte sur une <b>relation entre livres</b> plutôt que
/// sur un livre. Trois choses en découlent, et un simple champ texte « série » sur
/// <see cref="Livre"/> n'en couvrirait aucune.
/// <para>
/// <b>1. Deux niveaux, au moins.</b> <i>La Légende de Drizzt</i> regroupe plusieurs trilogies.
/// D'où <see cref="SerieParenteId"/>, une auto-référence : un cycle <b>est</b> une série qui
/// contient des séries. Un champ « cycle » séparé figerait la profondeur à deux et obligerait à
/// tout refaire au troisième niveau.
/// </para>
/// <para>
/// <b>2. L'ordre de lecture n'est pas l'ordre de publication.</b> <i>L'Elfe noir</i> est une
/// préquelle écrite après. C'est précisément l'information qu'on vient chercher : elle est
/// <b>stockée</b> (<see cref="ElementSerie.Position"/>), jamais déduite d'une année.
/// </para>
/// <para>
/// <b>3. La portée est COMMUNE au foyer</b>, comme le catalogue et les prêts, contrairement au
/// statut de lecture et à la liste d'envies. L'ordre de lecture d'une saga est une propriété de
/// l'œuvre : il ne change pas selon qui regarde. <see cref="AjoutePar"/> est donc une trace,
/// pas une frontière — <b>ne jamais filtrer dessus</b>, comme <see cref="Livre.AjoutePar"/>.
/// </para>
/// </remarks>
public class Serie
{
public int Id { get; set; }
public string Titre { get; set; } = string.Empty;
/// <summary>
/// Titre mis à plat. Porte un index <b>unique</b> : une série, une fiche.
/// </summary>
/// <remarks>
/// L'unicité est globale et non « par série parente », parce que
/// <see cref="SerieParenteId"/> est nullable et que SQLite tient deux <c>NULL</c> pour
/// distincts : une unicité incluant le parent laisserait passer autant de doublons qu'on
/// veut à la racine — exactement le piège documenté sur <c>LivreSouhaite.AuteurNormalise</c>.
/// </remarks>
public string TitreNormalise { get; set; } = string.Empty;
/// <summary>La série qui contient celle-ci, quand c'en est une partie d'un cycle.</summary>
public int? SerieParenteId { get; set; }
public Serie? SerieParente { get; set; }
public List<Serie> SousSeries { get; set; } = [];
/// <summary>Rang de cette série dans sa série parente. Sans objet à la racine.</summary>
public int Position { get; set; }
public List<ElementSerie> Elements { get; set; } = [];
public DateTime DateAjout { get; set; }
/// <summary>Qui a créé la série. <b>Trace, pas frontière</b> : la série est commune.</summary>
public string? AjoutePar { get; set; }
public void RecalculerFormes()
{
Titre = Titre.Trim();
TitreNormalise = NormalisationTexte.Normaliser(Titre);
}
}
/// <summary>
/// Une <b>position</b> dans une série : le tome n, qu'on le possède ou non.
/// </summary>
/// <remarks>
/// ⚠️ <b>Ce n'est pas « un livre de la série » mais une place dans l'ordre de lecture.</b> La
/// nuance est tout l'intérêt de l'écran : sans position sans livre, on ne pourrait pas montrer
/// les trous — « il vous manque le tome 3 » —, qui est la demande d'origine. Aucune source ne
/// donne l'ordre de lecture d'une saga (la BnF ne l'expose pas, et l'ordre de publication est
/// justement faux pour une préquelle) : les tomes absents n'existent que si on les saisit.
/// <para>
/// <see cref="Titre"/> est donc <b>toujours renseigné</b>, y compris quand
/// <see cref="LivreId"/> l'est : c'est ce qui permet à la suppression d'un livre de laisser un
/// trou nommé plutôt qu'une ligne muette. La clé étrangère est en <c>SetNull</c> pour cette
/// raison précise — supprimer un livre ne doit pas trouer la structure de la saga.
/// </para>
/// </remarks>
public class ElementSerie
{
public int Id { get; set; }
public int SerieId { get; set; }
public Serie? Serie { get; set; }
/// <summary>Rang dans l'ordre de <b>lecture</b>, à partir de 0.</summary>
public int Position { get; set; }
/// <summary>Le livre du catalogue, s'il est possédé. <c>null</c> = un trou.</summary>
public int? LivreId { get; set; }
public Livre? Livre { get; set; }
/// <summary>
/// Titre du tome. Renseigné même quand le livre est là, pour survivre à sa suppression.
/// </summary>
public string Titre { get; set; } = string.Empty;
public void RecalculerFormes() => Titre = Titre.Trim();
}