{{ 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éNomClé nativeUne ligne parConversion → dorians_product_idÉtat
1BDPP — Base de données de produits pharmaceutiques (Santé Canada)DRUG_CODEproduitvaleur numérique du DIN (préfixe 00/01/02)actif
2BDPSNH — Produits de santé naturels homologués (Santé Canada)NPN (licence_number)produitvaleur numérique du NPN (97 % préfixe 80)actif
3RAMQ — Liste des médicamentsCOD_FACT_PRODUformatvaleur numérique (DIN réel ou pseudo-code 99xxxxxx)actif
4AQPP — liste des produitsDINformatvaleur numérique du DINactif
5McKesson — catalogue grossisteitem_numberformatdin, sinon NPN (mckesson_item_number_vs_npn, scraping), sinon gtin_unit ≥ 10⁹, sinon gtin_case ≥ 10⁹, sinon 66 + item sur 6 chiffresactif
6PharmaPlus — catalogue grossiste (Distribution Pharmaplus)codeformatdin/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

PlageFamille
< 03 000 000DIN 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 999PharmaPlus sans identifiant : frappe pharmaplus_code_vs_id (65 + auto-increment)
66 000 000 – 66 999 999McKesson sans identifiant : 66 + item_number sur 6 chiffres
80 xxx xxxNPN
88 000 001 +produits personnels variés : 88 + auto-increment sur 6 chiffres (à venir)
99 xxx xxxpseudo-DIN RAMQ (codes de facturation)
≥ 10¹¹UPC/GTIN (produits sans autre identifiant — McKesson gtin, PharmaPlus upc)

Structure de produits

ColonneTypeNote
dorians_product_idBIGINT PKidentifiant unique numérique du projet
dinINT NULL, UNIQUEDIN bdpp, NPN bdpsnh ou pseudo-DIN ramq ; pour les jointures Santé Canada, filtrer din < 99000000
nomVARCHAR(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
marqueVARCHAR(300)composante « marque » du nom (cascade AQPP → RAMQ → source) ; mck/pp : la description verbatim
teneurVARCHAR(100)composante « teneur/force » du nom (ex. 2MG) ; NULL pour mck/pp
formeVARCHAR(60)composante « forme » du nom (ex. CO.) ; NULL pour mck/pp
dorians_formeENUM NULLunité 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_generiqueBIGINT NULLniveau 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_confianceENUM NULLcolonne 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
formatsJSON NULLVOIR TABLEAU DÉTAILLÉ
classe_legale_bdppJSON NULLvaleurs SCHEDULE verbatim de BDPP (ex. ["PRESCRIPTION","NARCOTICS (CDSA I)"])
classe_legale_dorianENUM NULLla 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_fournitureTINYINT(1) défaut 01 = 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_qcTINYINT(1) NULLAnnexe 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
actifTINYINT(1)BDPP MARKETED · BDPSNH licence active · RAMQ inscrit · AQPP publié · McKesson/PharmaPlus au catalogue
actif_detailVARCHAR(100)« Commercialisé », statut_f, « Retiré de la liste AQPP (…) », « Disparu de la source (…) », …
sourceVARCHAR(10)bdpp / bdpsnh / ramq / aqpp / mck / pp
updated_atDATETIMEposé 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).

ColonneTypeNote
format_idINT NOT NULLidentifiant 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_formatNUMBER NULLquantité du format (RAMQ QUANT_FORMAT · AQPP Format · McKesson unit_quantity_case · PharmaPlus : quantité extraite de « Format », ex. 30 de « 30UN »)
unit_formatSTRING NULLunité du format (RAMQ UNIT_FORMAT · AQPP UniteFormat · McKesson product_format · PharmaPlus : unité extraite de « Format », ex. UN)
prix_unitaireNUMBER NULLprix unitaire en $ (RAMQ PRIX_UNITAIRE · AQPP Coutantuni · McKesson regular_unit_price · PharmaPlus : prix ÷ quantité)
ramq_couvertureARRAYcodes 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_ppbNUMBER NULLprix le plus bas unitaire RAMQ (PRIX_UNIT_PPB) — plafond de remboursement des interchangeables ; null hors RAMQ
couverture_aqppBOOLEANce 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

TableRôle
produitsversion finale unifiée
produits_stage_$date_$istaging avant confirmation ($i auto-incrémenté si plusieurs le même jour)
bdpp / bdpsnh / ramq / aqpp / mckesson / pharmaplusdernière version de chaque source, avec updated_at, vu_le, disparu
<source>_$date_brutsnapshot brut de chaque téléchargement (archives, jamais modifiées après exclusions)
aqpp_2026_08_23_fichieramorçage AQPP : copie verbatim du fichier source (grain DIN+Format), lue par aqpp/3_extraire.js --table=…
groupes_generiquesgroupes frappés 77xxxxxx pour les produits AQPP hors RAMQ (durable, jamais purgée)
mckesson_item_number_vs_npncorrespondance item McKesson → NPN (scraping, durable, checkpoint)
pharmaplus_code_vs_idids frappés 65xxxxxx pour les codes PharmaPlus sans din/npn/upc (durable, jamais purgée)
majs / majs_diffstraç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(', ') }}
{{ fmt(dt.total) }} ligne(s){{ filtresActifs ? ' (filtrées)' : '' }} page {{ dt.page }} / {{ Math.max(1, Math.ceil(dt.total/dt.taille)) }} filtres : taper puis Entrée · tri : cliquer un en-tête · id : cliquer pour la fiche
{{ c.nom }}{{ dir==='asc' ? ' ▲' : ' ▼' }}

Aucun résultat.

Chargement…

Recherche « {{ recherche.q }} » — {{ fmt(recherche.rows.length) }} résultat(s){{ recherche.rows.length===200 ? ' (limité à 200, préciser la recherche)' : '' }}

dorians_product_iddinNomGroupe génériqueActifDétailSource
{{ pad(r.dorians_product_id) }} {{ r.din==null ? '' : pad(r.din) }} {{ r.nom }} {{ r.groupe_generique ?? '' }} {{ r.groupe_generique_confiance }} {{ r.actif ? 'actif' : 'inactif' }} {{ r.actif_detail }} {{ r.source }}

Aucun produit trouvé.

Historique des comparaisons (majs)

#SourceBrut → cibleAjoutsModifsDisparitionsCrééePromue
{{ m.id }}{{ m.source }} {{ m.table_brut }} → {{ m.table_cible }} {{ fmt(res(m).n_ajouts) }}{{ fmt(res(m).n_modifs) }}{{ fmt(res(m).n_disparitions) }} {{ (''+m.created_at).slice(0,16).replace('T',' ') }} {{ m.promu ? 'promue' : 'non promue' }}

Cliquer sur une ligne pour voir le datatable complet de ses changements.