Cataloguer les revues : une fiche, ses numéros à l'intérieur

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>
This commit is contained in:
mathieu
2026-08-19 22:19:08 +02:00
co-authored by Claude Opus 5
parent 4e372ab694
commit ca0a9dc18c
20 changed files with 2110 additions and 42 deletions
+87
View File
@@ -717,6 +717,25 @@ RapprochementRefuse (mémoire des « non » de l'utilisateur)
├── AuteurAId ─┐ UNIQUE, couple toujours rangé (petit Id d'abord)
└── AuteurBId ─┘ pour qu'un refus vaille dans les deux sens
Revue (magazines — COMMUNS au foyer ; PAS des livres)
├── Id
├── Titre
├── TitreNormalise (index UNIQUE — une revue, une fiche)
├── Issn (index UNIQUE PARTIEL, WHERE Issn IS NOT NULL)
├── Editeur
├── DateAjout
└── AjoutePar (trace, pas une frontière)
NumeroRevue (recensé seulement : ni prêt, ni statut de lecture)
├── Id
├── RevueId (FK Revue, cascade — un numéro n'existe que par sa revue)
├── Numero (obligatoire : « 43 », « hors-série 7 »)
├── NumeroNormalise
├── DateParution (UTC, nullable)
├── Note
├── DateAjout
└── UNIQUE (RevueId, NumeroNormalise)
Serie (sagas et cycles — COMMUNS au foyer, comme le catalogue)
├── Id
├── Titre
@@ -1290,6 +1309,74 @@ l'œil.
piste, avec sa limite connue — les œuvres remontent en langue originale et ne se rapprochent pas
du catalogue par `CleOeuvre`.
## Revues et magazines — migration `Revues` (2026-08-19)
Rouvre — et clôt — la décision du premier lot, qui écartait le catalogage des périodiques faute
de modèle. **Une fiche par revue, les numéros à l'intérieur** : douze numéros d'un même magazine
ne font pas douze fiches identiques, puisqu'ils partagent la fiche de leur revue.
### Une table à part, pour la troisième fois — et c'est le même argument
Comme `LivreSouhaite` et comme `Serie` : logée dans `Livres`, une revue entrerait
**mécaniquement** dans le catalogue, ses compteurs, la détection de doublons, les séries et les
bibliographies par auteur — et il faudrait ajouter « et qui n'est pas une revue » à **chaque**
lecture. Un invariant qu'on réécrit partout finit par être oublié quelque part.
Vérifié en exécution : la revue *Médor* n'apparaît ni au catalogue, ni dans une recherche sur
son nom.
⚠️ L'`Issn` vit sur `Revue`, **jamais** dans `Livre.Isbn` : c'était déjà la règle du flux `977`,
elle a maintenant un endroit où se ranger.
### Un numéro est recensé, rien de plus (décidé avec l'utilisateur le 2026-08-19)
Ni prêt, ni statut de lecture. Ces deux mécanismes sont attachés à `Livre` **par clé étrangère** :
les étendre demanderait une seconde table de prêts ou une parenté commune entre livres et
numéros, pour un usage non confirmé. Le choix est **réversible** — les ajouter plus tard ne
détruit rien de ce qui aura été saisi.
Deux détails qui ne sont pas des détails :
- **`Numero` est obligatoire.** Sans lui, deux numéros de la même revue seraient indiscernables,
et l'unicité `(RevueId, NumeroNormalise)` ne voudrait rien dire.
- **Les numéros s'affichent du plus récent au plus ancien**, à l'inverse d'une série qui suit son
ordre de lecture : devant un kiosque, la question est « ai-je le dernier ? ». ⚠️ Les numéros
**sans date ferment la liste** au lieu de passer pour les plus vieux — une date inconnue n'est
pas une date ancienne.
### « Créer ou retrouver », et non « créer »
`POST /api/revues` rend la revue existante quand l'ISSN **ou** le titre la désigne. C'est ce
qu'exige le flux du code-barres : scanner le numéro suivant du même magazine retombe forcément
sur la même revue, et le geste de l'utilisateur est toujours « voici un numéro de plus ».
Refuser le doublon l'obligerait à comprendre qu'il doit aller chercher la fiche lui-même.
Corollaire testé : **une revue saisie à la main apprend son ISSN au premier scan**, ce qui permet
de la retrouver ensuite. L'unicité de l'ISSN est un index **partiel** (`WHERE Issn IS NOT NULL`),
sans quoi les revues sans ISSN se bloqueraient les unes les autres — deux `NULL` étant distincts
pour SQLite, c'est exactement le piège déjà rencontré sur `LivreSouhaite.AuteurNormalise`.
### Le flux `977` ne remplit plus un formulaire de livre
Avant, un code de périodique basculait sur la saisie manuelle d'un **livre**, titre pré-rempli.
C'était le moins mauvais choix tant que les revues n'avaient pas de modèle. L'écran propose
maintenant **« Ouvrir la fiche de cette revue »**, qui la crée au besoin et y mène.
⚠️ **Le numéro se saisit à la main, et ce n'est pas un oubli** : les deux chiffres de parution du
code-barres ne sont pas un numéro fiable (déjà acté), et l'add-on **EAN-2** qui le porterait
vraiment n'a pas pu être vérifié — il faudrait un magazine réel devant la caméra. La piste reste
ouverte dans IDEES.md.
Vérifié en exécution sur `9772466671438` : la BnF nomme « Médor », la fiche est créée avec son
ISSN, deux numéros s'y ajoutent, et **rescanner le même code retombe sur la même fiche**.
### Un sixième onglet, mesuré
`Catalogue / Auteurs / Séries / Revues / Prêts / Envies`. À 320 px — le plus étroit des
téléphones réalistes — les six libellés occupent **312 px sans troncature**, huit de marge.
⚠️ C'est la limite : un septième onglet, ou un libellé plus long que « Catalogue », imposera de
regrouper plutôt que d'ajouter.
## Sagas et cycles — migration `SagasEtCycles` (2026-08-19)
Cas donné : *La Légende de Drizzt*. C'est la première notion du projet qui porte sur une