Signaler les doublons à l'ajout d'un livre, sans jamais les refuser
Rien n'empêchait de rescanner un livre déjà catalogué. Plutôt qu'un index unique — qui interdirait le second exemplaire, cas parfaitement légitime — la création répond 409 avec les fiches semblables, et « confirmerDoublon » enregistre la même saisie. Deux critères, dont aucun n'est une clé : ISBN identique, ou clé d'œuvre et auteur communs. La clé d'œuvre étant un préfixe de TitreNormalise, SQL dégrossit sur la colonne indexée et l'égalité exacte se vérifie ensuite en mémoire. Vérifié en exécution : avertissement, retour au formulaire intact, et ajout confirmé créant bien un second exemplaire. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -835,6 +835,65 @@ livre sorti depuis six mois qu'on avait oublié, pas celui prêté hier.
|
||||
(`ServiceLivresApi.EnUtc`). Sans cela le prêt se décalerait d'un jour pour la moitié du globe.
|
||||
Vérifié en exécution : saisie du 12/08 → `2026-08-11 22:00` en base (CEST).
|
||||
|
||||
## Doublons du catalogue — avertir, jamais refuser (2026-08-19)
|
||||
|
||||
Défaut remonté en usage : rien n'empêchait de rescanner un livre déjà catalogué, et
|
||||
`ServiceCatalogue.CreerAsync` ne faisait aucun contrôle.
|
||||
|
||||
**Décidé : un avertissement confirmable**, pas un index unique. `HasIndex(l => l.Isbn)` reste
|
||||
délibérément **non unique**, et il ne faut pas le rendre unique « pour bien faire » :
|
||||
|
||||
- **posséder deux exemplaires est légitime** — on garde le sien et on prête l'autre. C'est
|
||||
précisément le cas qu'un index unique rendrait impossible, sans recours ;
|
||||
- **l'ISBN est facultatif** (livres anciens, tirages sans ISBN) : il ne peut pas porter à lui
|
||||
seul l'unicité, et SQLite tient deux `NULL` pour distincts de toute façon.
|
||||
|
||||
⚠️ Le contraste avec la liste d'envies est voulu : `LivreSouhaite` **refuse** le doublon par
|
||||
index unique. Une envie décrit une œuvre, dont on ne souhaite pas deux exemplaires ; un livre
|
||||
décrit un **objet**, qu'on peut posséder en double. Ne pas uniformiser les deux.
|
||||
|
||||
### Deux critères, dont aucun n'est une clé
|
||||
|
||||
| Critère | Attrape | Rate, ou signale à tort |
|
||||
|---|---|---|
|
||||
| **ISBN identique** | le rescan du même livre, cas visé | rien quand l'ISBN manque |
|
||||
| **`CleOeuvre` + un auteur commun** | la saisie manuelle sans ISBN, le sous-titre, l'ordre `Nom, Prénom` | le poche à côté du grand format — deux exemplaires bien réels |
|
||||
|
||||
L'auteur est **exigé en plus du titre** (au sens de `RapprochementAuteurs`) : sans lui, deux
|
||||
recueils « Nouvelles » sans rapport se signaleraient l'un l'autre. Deux fiches **sans aucun**
|
||||
auteur concordent, le titre étant alors tout ce dont on dispose.
|
||||
|
||||
⚠️ **La clé d'œuvre est un préfixe de `TitreNormalise`** — la troncature du sous-titre précède
|
||||
la normalisation, qui préserve l'ordre des caractères. C'est ce qui permet de dégrossir en SQL
|
||||
sur la colonne indexée (`== cle` ou `StartsWith(cle + " ")`) puis de vérifier l'égalité exacte
|
||||
des clés en mémoire. Sans cette seconde passe, « Germinal les années noires » serait signalé
|
||||
comme doublon de « Germinal » ; sans la première, il faudrait charger tout le catalogue à
|
||||
chaque ajout.
|
||||
|
||||
### 409, et surtout pas 400
|
||||
|
||||
`POST /api/livres` répond **409 Conflict** avec `DoublonsLivre` (message + les fiches en cause),
|
||||
et `?confirmerDoublon=true` enregistre la même saisie sans plus rien demander.
|
||||
|
||||
⚠️ **Un doublon n'est pas une erreur**, et le code de statut n'est pas un détail : la saisie est
|
||||
valide, c'est le catalogue qui contient déjà quelque chose. D'où un **troisième état** dans
|
||||
`ResultatEcriture` et dans `ResultatCreation` côté client, au lieu d'un message logé dans
|
||||
`Erreur` — un texte rouge dirait « c'est raté » là où l'écran doit proposer « ajouter quand
|
||||
même ». Le 409 traverse donc `EnvoyerAsync` sans passer par le traitement d'erreur générique,
|
||||
qui l'aurait aplati en « L'enregistrement a échoué (409) ».
|
||||
|
||||
### L'écran montre les fiches, il ne les décrit pas
|
||||
|
||||
`AvertissementDoublon` remplace le formulaire tant que la question n'est pas tranchée (la saisie
|
||||
attend dans `_saisie` et revient intacte si l'on renonce), et affiche chaque livre en cause
|
||||
**en lien vers sa fiche** : la vraie question est « est-ce bien le même livre ? », et on n'y
|
||||
répond qu'en regardant celui qui est déjà là. Le prêt en cours y figure — un exemplaire dehors
|
||||
est justement une raison d'en vouloir un second.
|
||||
|
||||
**Ce qui n'est délibérément pas fait** : le contrôle ne porte que sur la **création**. Une
|
||||
édition qui rendrait deux fiches identiques est un geste délibéré sur une fiche existante, pas
|
||||
un scan répété par mégarde.
|
||||
|
||||
## Liste d'envies — implémentée le 2026-08-18
|
||||
|
||||
### Une table à part, et non un statut de plus
|
||||
|
||||
Reference in New Issue
Block a user