{{ fmt(v.lignes) }}
{{ k }} · {{ fmt(v.disparus) }} disparus
maj {{ (''+v.derniere_maj).slice(0,10) }}
Objectif
Compiler différentes sources de « produits » en une seule table produits ayant un identifiant unique
dorians_product_id (numérique), avec la même structure. Une seule ligne par DIN, même si le produit
existe en plusieurs formats (une seule ligne pour atorvastatine 80, qu'il vienne en 30, 100 ou 500 comprimés).
Sources
| Priorité | Nom | Clé native | Une ligne par | Conversion → dorians_product_id | État |
| 1 | BDPP — Base de données de produits pharmaceutiques (Santé Canada) | DRUG_CODE | produit | valeur numérique du DIN (préfixe 00/01/02) | actif |
| 2 | BDPSNH — Produits de santé naturels homologués (Santé Canada) | NPN (licence_number) | produit | valeur numérique du NPN (97 % préfixe 80) | actif |
| 3 | RAMQ — Liste des médicaments | COD_FACT_PRODU | format | valeur numérique (DIN réel ou pseudo-code 99xxxxxx) | actif |
| 4 | AQPP — liste des produits | DIN | format | valeur numérique du DIN | actif |
| 5 | McKesson — catalogue grossiste | item_number | format | din, sinon NPN (mckesson_item_number_vs_npn, scraping), sinon gtin_unit ≥ 10⁹, sinon gtin_case ≥ 10⁹, sinon 66 + item sur 6 chiffres | actif |
| — | RAMQ pseudo-DIN — liste des produits | — | — | — | à venir |
Plages de l'identifiant numérique
| Plage | Famille |
| < 03 000 000 | DIN réels (préfixes 00/01/02, zéros de tête perdus — affichés repaddés sur 8 chiffres) |
| 66 000 000 – 66 999 999 | McKesson sans identifiant : 66 + item_number sur 6 chiffres |
| 77 000 001 + | groupes génériques frappés (table groupes_generiques — pas des produits) |
| 80 xxx xxx | NPN |
| 88 000 001 + | produits personnels variés : 88 + auto-increment sur 6 chiffres (à venir) |
| 99 xxx xxx | pseudo-DIN RAMQ (codes de facturation) |
| ≥ 10¹¹ | UPC/GTIN (produits sans autre identifiant, à venir) |
Structure de produits
| Colonne | Type | Note |
| dorians_product_id | BIGINT PK | identifiant unique numérique du projet |
| din | INT NULL, UNIQUE | DIN bdpp, NPN bdpsnh ou pseudo-DIN ramq ; pour les jointures Santé Canada, filtrer din < 99000000 |
| nom | VARCHAR(500) | BRAND_NAME / product_name / Marque |
| groupe_generique | BIGINT NULL | niveau molécule + teneur + forme (les interchangeables) : NO_SEQ_MED RAMQ par din, sinon triplet AQPP apparié à RAMQ, sinon id frappé 77xxxxxx (table groupes_generiques) |
| formats | JSON NULL | [{ format_id, quant_format, unit_format, prix_unitaire }] — enrichis depuis RAMQ puis AQPP par dorians_product_id, peu importe la source de la ligne ; McKesson à venir |
| actif | TINYINT(1) | BDPP MARKETED · BDPSNH licence active · RAMQ inscrit · AQPP publié |
| actif_detail | VARCHAR(100) | « Commercialisé », statut_f, « Retiré de la liste AQPP (…) », « Disparu de la source (…) », … |
| source | VARCHAR(10) | bdpp / bdpsnh / ramq / aqpp / mck |
| updated_at | DATETIME | posé uniquement quand la ligne change réellement |
Structure de la base
| Table | Rôle |
| produits | version finale unifiée |
| produits_stage_$date_$i | staging avant confirmation ($i auto-incrémenté si plusieurs le même jour) |
| bdpp / bdpsnh / ramq / aqpp / mckesson | dernière version de chaque source, avec updated_at, vu_le, disparu |
| bdpp_$date_brut / … / aqpp_$date_brut | snapshot brut de chaque téléchargement (archives, jamais modifiées après exclusions) |
| aqpp_2026_08_23_fichier | amorçage AQPP : copie verbatim du fichier source (grain DIN+Format), lue par aqpp/3_extraire.js --table=… |
| groupes_generiques | groupes frappés 77xxxxxx pour les produits AQPP hors RAMQ (durable, jamais purgée) |
| majs / majs_diffs | traçabilité : chaque comparaison est enregistrée, chaque promotion pointée |
Mise à jour incrémentielle
Les mises à jour sont incrémentielles, basées sur le téléchargement précédent : un DIN peut disparaître de la
source, il ne disparaît jamais des tables. Il est marqué disparu=1 dans la table source et
actif=0 / « Disparu de la source » dans produits. Aucun DELETE, jamais, et aucune
promotion sans confirmation explicite.
Chaque source suit 6 étapes (dossiers bdpp/, bdpsnh/, ramq/, aqpp/ — ls du dossier = la checklist) :
1_telecharger.js télécharge les fichiers bruts dans <source>/temp/$date/
2_verifier_structure.js structure des fichiers vs définitions — échec = stop, rien en DB
3_extraire.js extrait tout dans <source>_$date_brut
4_exclure.js supprime les exclusions manuelles (de la brute seulement)
5_comparer.js intégrité + diff complet vs table durable → majs / majs_diffs
6_promouvoir.js CONFIRMATION puis upsert → table durable (disparus marqués)
Puis generer_produits/ : 1_generer_stage.js (fusion par priorité, enrichissements croisés
formats et groupe générique) → 2_comparer.js (diff vs produits) → 3_promouvoir.js
(confirmation puis upsert).
La checklist détaillée que Claude suit est dans CLAUDE.md à la racine du projet.
Exclusions manuelles
Enregistrements exclus des sources brutes avant unification. Appliquées par 4_exclure.js sur les
tables brutes datées uniquement. Modifiables seulement dans <source>/exclusions.js, avec l'accord de Dorian.
{{ e.source.toUpperCase() }} — {{ e.raison }} ({{ e.n }})
Champ {{ e.cle }} · exclu par {{ e.exclu_par }} le {{ e.date }}
{{ e.valeurs.join(', ') }}