{{ 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 |
| 6 | PharmaPlus — catalogue grossiste (Distribution Pharmaplus) | code | format | din/npn ≤ 8 chiffres, sinon upc ≥ 10¹¹, sinon 65 + frappe (pharmaplus_code_vs_id) | 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) |
| 65 000 000 – 65 999 999 | PharmaPlus sans identifiant : frappe pharmaplus_code_vs_id (65 + auto-increment) |
| 66 000 000 – 66 999 999 | McKesson sans identifiant : 66 + item_number sur 6 chiffres |
| 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 — McKesson gtin, PharmaPlus upc) |
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) | marque + teneur + forme (ex. « MAR-RANITIDINE 150MG CO. »), cascade par champ AQPP → RAMQ → repli source (bdpp active_ingredients/form, bdpsnh dosage_form) ; mck et pp gardent leur description verbatim |
| marque | VARCHAR(300) | composante « marque » du nom (cascade AQPP → RAMQ → source) ; mck/pp : la description verbatim |
| teneur | VARCHAR(100) | composante « teneur/force » du nom (ex. 2MG) ; NULL pour mck/pp |
| forme | VARCHAR(60) | composante « forme » du nom (ex. CO.) ; NULL pour mck/pp |
| dorians_forme | ENUM NULL | unité de prise déduite de la forme : COMPRIME ou CAPSULE ; NULL sinon (injectables, crèmes, trousses, formes composées hétérogènes, mck/pp). Prescription l'écrit dans la posologie (« 1 COMPRIME AU DEJEUNER ») |
| 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), sinon pont BDPP (comprimés/capsules hors RAMQ/AQPP : hérite du NO_SEQ_MED désigné sans ambiguïté par la clé BDPP molécule+teneur+forme — ex. Lasix 20 mg → 544) |
| groupe_generique_confiance | ENUM NULL | colonne interne — absente de /exporter. Voie qui a posé le groupe : ramq_din · ramq_triplet_aqpp · frappe_aqpp · bdpp_pont (équivalence dérivée de la BDPP, 99,6 % validée) · bdpp_ambigu (groupe NULL : la clé BDPP désigne ≥ 2 groupes RAMQ, à trancher) ; NULL = hors périmètre |
| formats | JSON NULL | VOIR TABLEAU DÉTAILLÉ |
| classe_legale_bdpp | JSON NULL | valeurs SCHEDULE verbatim de BDPP (ex. ["PRESCRIPTION","NARCOTICS (CDSA I)"]) |
| classe_legale_dorian | ENUM NULL | la plus restrictive : narcotique > drogue_controlee > substance_ciblee > ordonnance > sans_ordonnance ; acte (service pharmaceutique AQPP, hors cascade) ; NULL = inconnu. La matrice de règles d'ordonnance vit dans Prescription |
| est_fourniture | TINYINT(1) défaut 0 | 1 = fourniture / matériel / matériel diabète (seringues, aiguilles, chambres d'espacement, bandelettes, lancettes, capteurs), déduite de RAMQ (COD_NAT_MED FHO/FSH/ADJ, AHFS) et AQPP (AHFS 94 / 36:26, forme) ; services AQPP (Nature S) et pansements (AHFS 94:32) exclus ; 0 hors RAMQ/AQPP |
| annexe_qc | TINYINT(1) NULL | Annexe du règlement québécois sur la vente, verbatim AQPP (Annexe) : 0 non annexé, 1 annexe I, 2 annexe II, 3 annexe III ; NULL hors AQPP |
| actif | TINYINT(1) | BDPP MARKETED · BDPSNH licence active · RAMQ inscrit · AQPP publié · McKesson/PharmaPlus au catalogue |
| actif_detail | VARCHAR(100) | « Commercialisé », statut_f, « Retiré de la liste AQPP (…) », « Disparu de la source (…) », … |
| source | VARCHAR(10) | bdpp / bdpsnh / ramq / aqpp / mck / pp |
| updated_at | DATETIME | posé uniquement quand la ligne change réellement |
Structure de formats
JSON : [{ format_id, quant_format, unit_format, prix_unitaire, ramq_couverture, ramq_ppb, couverture_aqpp }] —
un objet par format d'acquisition. Enrichis par dorians_product_id, peu importe la source de la
ligne produits ; premier enrichisseur gagnant : RAMQ > AQPP > McKesson > PharmaPlus
(formats RAMQ « 9 » établissements exclus).
| Colonne | Type | Note |
| format_id | INT NOT NULL | identifiant du format, unique DANS son produit et stable d'une mise à jour à l'autre (RAMQ COD_FORMAT_ACQ, 0..4 · McKesson item_number · PharmaPlus id_magento, sinon ordinal frappé par code · AQPP : ordinal frappé par quantité|Cup — table durable formats_ids, 1, 2, 3… dans le produit) |
| quant_format | NUMBER NULL | quantité du format (RAMQ QUANT_FORMAT · AQPP Format · McKesson unit_quantity_case · PharmaPlus : quantité extraite de « Format », ex. 30 de « 30UN ») |
| unit_format | STRING NULL | unité du format (RAMQ UNIT_FORMAT · AQPP UniteFormat · McKesson product_format · PharmaPlus : unité extraite de « Format », ex. UN) |
| prix_unitaire | NUMBER NULL | prix unitaire en $ (RAMQ PRIX_UNITAIRE · AQPP Coutantuni · McKesson regular_unit_price · PharmaPlus : prix ÷ quantité) |
| ramq_couverture | ARRAY | codes de programme RAMQ (COD_PGM bruts, sans perte — un format cumule souvent plusieurs programmes, ex. [1,3]) : 1 ITSS · 2 tuberculose · 3 régime général · 6 pandémie · 7 vaccin · 8 naloxone · 9 IVG · 40 patient d'exception · 41 médicament d'exception. [] si non assuré (NAS) ou hors RAMQ |
| ramq_ppb | NUMBER NULL | prix le plus bas unitaire RAMQ (PRIX_UNIT_PPB) — plafond de remboursement des interchangeables ; null hors RAMQ |
| couverture_aqpp | BOOLEAN | ce format est présent à la liste AQPP du produit (apparié par quantité, l'AQPP n'ayant pas de format_id) ; true pour les formats venant de l'AQPP, false pour McKesson/PharmaPlus (hors AQPP par priorité) |
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 / pharmaplus | dernière version de chaque source, avec updated_at, vu_le, disparu |
| <source>_$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) |
| mckesson_item_number_vs_npn | correspondance item McKesson → NPN (scraping, durable, checkpoint) |
| pharmaplus_code_vs_id | ids frappés 65xxxxxx pour les codes PharmaPlus sans din/npn/upc (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/, mckesson/, pharmaplus/ — 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(', ') }}