Concepts métier - PT2QE¶
Vue d'ensemble du système¶
PT2QE (Pricing Tier 2 Quote Engine) génère des recommandations de prix personnalisées à la maille client × article. Contrairement à PT1CE qui optimise des corridors de prix génériques, PT2QE produit des offres de prix spécifiques pour chaque couple client-article.
Périmètre traité : - Univers : ZOOM1 EXCLUSIF (filtre appliqué à l'extraction et tout au long du processus) - Période d'analyse : 4 derniers trimestres fiscaux COMPLETS selon SYS_MD_CALENDRIER_SYSCO - Conditions : Types ZFAP, ZFSP, ZIAP, ZISP sur séquences A305 et A565 - État : Offres valides (DT_FIN > SYSDATE) et conditions actives (FG_CND_VLD = 'X')
Prérequis obligatoires : - Tables PT1CE_OPTIMAL_ZOOM1/ZOOM2/ZOOM3 existantes (générées par PT1CE) - Tables de mapping PT0CE_TYPE_CLIENT_MAPPING et PT0CE_TYPE_RESTAURANT_MAPPING - Table SYS_MD_CALENDRIER_SYSCO avec données fiscales à jour
Période d'analyse et historique transactionnel¶
Détermination automatique de la période¶
PT2QE utilise le calendrier fiscal Sysco (format 4-4-5) pour déterminer automatiquement la période d'analyse. Le module PeriodManager interroge SYS_MD_CALENDRIER_SYSCO pour :
- Identifier la date de référence : Date d'exécution du traitement
- Trouver les 4 derniers trimestres complets :
- Un trimestre est considéré "complet" si sa dernière semaine (MAX(ID_SEM)) est passée
- Remonte dans l'historique pour obtenir exactement 4 trimestres
- Retourne les dates de début et fin au format DATE Oracle
Exemple :
Date d'exécution : 2025-11-03
Trimestres identifiés : 2024_Q04, 2025_Q01, 2025_Q02, 2025_Q03
Période d'extraction : 2024-10-28 → 2025-10-26
Scope temporel de l'historique¶
Toutes les métriques transactionnelles sont calculées sur cette période de 4 trimestres :
- MT_CAB_4Q : CA total (hors promos)
- QT_UF_4Q : Volume en unités de facturation
- QT_KG_4Q : Volume en kilogrammes
- MT_CAB_4Q_FERMES : CA sur prix fermes uniquement
- MT_CAB_4Q_INDEXES : CA sur prix indexés uniquement
Filtres transactionnels appliqués :
- ID_TYP_FAC = 'ZF2' (factures de type ZF2)
- LC_TC_INTRA IN ('Brake', 'D2 hors Brake')
- Prix promo exclus (via jointure PRP_LABELS)
- FG_PRESTA = '0' (pas de prestation)
- FG_MARCHANDISE IN ('X', '1') (marchandise uniquement)
- MT_CAB > 0, QT_UF > 0, MT_GM4 IS NOT NULL
Récupération du statut mercuriale (FG_HM)¶
Le flag FG_HM est déterminé dynamiquement depuis l'historique transactionnel :
- Source : Table SYS_FACTURE_LIGNE sur la période 4Q
- Logique : Dernière ligne de facture connue pour le couple client-article
- Valeurs :
- '1' : Hors mercuriale (prix personnalisé)
- '0' : En mercuriale (prix standard)
- Utilisation : Ce flag est ensuite utilisé pour déterminer l'UNIVERS et faire le matching avec les mappings TYPE_CLIENT
Extraction et enrichissement des offres¶
Phase 1 : Extraction des offres actuelles¶
Table source : SYS_TARIF_SIMULATION
Table de sortie : PT2QE_PRICE_OFFERS
Critères de sélection des offres :
-- Conditions valides et actives
cnd.FG_CND_VLD = 'X'
cnd.ID_ACC IS NULL
cnd.DT_FIN > SYSDATE
-- Types de conditions
cnd.ID_TYP_CND IN ('ZFAP', 'ZFSP', 'ZIAP', 'ZISP')
-- Séquences A305 ou A565
cnd.ID_SEQ IN ('A305', 'A565')
-- Séquence client ou hiérarchie
(
(c.ID_STA_CLN IN ('10', '20', '30', '50') AND cnd.LC_SEQ_CLN = 'CLIENT')
OR
(c.ID_AGC <> 'A000' AND cnd.LC_SEQ_CLN IN ('HIERARCHIE CLIENT N4', 'N5', 'N6'))
)
-- Exclusions produits
a.ID_GMM <> 'PSN'
a.LC_HI1 <> 'Qualificatif article inconnu'
a.LC_HIC_SYSCO_N2 NOT IN ('Divers', 'Freezer')
-- Exclusions clients
c.ID_TC_CG <> 'AUTRES'
Phase 2 : Enrichissement avec FG_HM¶
Sous-requête : LAST_FG_HM
Principe : Récupérer le dernier flag mercuriale connu pour chaque couple client-article
-- Classement par date de commande décroissante
ROW_NUMBER() OVER (
PARTITION BY c.ID_CLN, f.ID_ART
ORDER BY f.DT_CDE DESC, f.ID_FAC DESC
) as RN
-- Conservation du rang 1 uniquement
WHERE RN = 1
Transformation du flag :
- Si TRIM(f.FG_HM) = 'X' → '1'
- Sinon → '0'
Phase 3 : Calcul de l'UNIVERS¶
Règles métier BusinessRules :
ZOOM1 (cible principale de PT2QE)¶
RCI PI (avec ou sans KAM)
→ ID_TC_CG = 'RCI' AND ID_TC_CIBLE = 'PINDEP'
RCI GI SANS KAM
→ ID_TC_CG = 'RCI' AND ID_TC_CIBLE = 'GINDEP' AND ID_KAM = 'NO_KAM'
FG_HM = '1' pour RSI/RCI (hors RSC)
→ FG_HM = '1' AND (
(ID_TC_CG = 'RSI' AND ID_TC_CIBLE IN ('MP_AO_', 'GR_A_G', ...))
OR (ID_TC_CG = 'RCI' AND ID_TC_CIBLE IN ('PERSYS', 'PERCLI', 'AS_AUT'))
)
RSC avec ID_MERC_HM = 'HM'
→ ID_TC_CG = 'RSC' AND ID_TC_CIBLE IN ('CR_NAT', 'SRCNAT', ...)
AND TRIM(ID_MERC_HM) = 'HM'
ZOOM2¶
RCI GI AVEC KAM
→ ID_TC_CG = 'RCI' AND ID_TC_CIBLE = 'GINDEP' AND ID_KAM <> 'NO_KAM'
FG_HM = '0' pour RSI/RCI
→ FG_HM = '0' AND (
(ID_TC_CG = 'RSI' AND ID_TC_CIBLE IN ('MP_AO_', 'SRCREG', ...))
OR (ID_TC_CG = 'RCI' AND ID_TC_CIBLE = 'GC_REG')
)
RCI GC_REG avec FG_HM = '1'
→ ID_TC_CG = 'RCI' AND ID_TC_CIBLE = 'GC_REG' AND FG_HM = '1'
ZOOM3¶
RSC avec ID_MERC_HM <> 'HM'
→ ID_TC_CG = 'RSC' AND ID_TC_CIBLE IN (...)
AND TRIM(ID_MERC_HM) <> 'HM'
RCC
→ ID_TC_CG = 'RCC' AND ID_TC_CIBLE IN ('RCHNAT', 'GCN3PL', 'GCNDIR')
⚠️ Point d'attention : PT2QE applique un filtre strict WHERE UNIVERS = 'ZOOM1' après le calcul de l'UNIVERS. Les offres ZOOM2 et ZOOM3 sont exclues du traitement.
Phase 4 : Enrichissement des dimensions¶
Mapping TYPE_CLIENT¶
Table : PT0CE_TYPE_CLIENT_MAPPING
Jointure :
LEFT JOIN PT0CE_TYPE_CLIENT_MAPPING tc
ON tc.UNIVERS = ou.UNIVERS
AND tc.ID_TC_CG = ou.ID_TC_CG
AND tc.ID_TC_CIBLE = ou.ID_TC_CIBLE
AND tc.FG_HM = ou.FG_HM
Colonnes récupérées :
- TYPE_CLIENT : Libellé normalisé (ex: "RCI PI GI", "RSI HM")
- UNIVERS : Confirmation de l'univers calculé
Traitement des valeurs nulles :
Mapping TYPE_RESTAURANT¶
Table : PT0CE_TYPE_RESTAURANT_MAPPING
Jointure :
Colonne récupérée :
- Type_Restaurant : Classification de l'établissement
Traitement des valeurs nulles :
Enrichissement GEO¶
Source : SYS_MD_CLIENT.LC_ZDV_GRV
Utilisation : Zone géographique pour le matching avec les corridors MASTER
Phase 5 : Enrichissement historique 4Q¶
Sous-requête : HISTORICAL_PERFORMANCE
Agrégation : Par ID_CLN et ID_ART
Métriques calculées :
1. CA global (hors promos) :
- MT_CAB_4Q : Somme des CA sur 4 trimestres
- QT_UF_4Q : Somme des quantités UF
- QT_KG_4Q : Somme des quantités KG
- CA Prix Fermes :
MT_CAB_4Q_FERMES: CA surLC_PRP_N1 = 'Prix fermes'-
QT_UF_4Q_FERMES,QT_KG_4Q_FERMES -
CA Prix Indexés :
MT_CAB_4Q_INDEXES: CA surLC_PRP_N1 = 'Prix indexés'QT_UF_4Q_INDEXES,QT_KG_4Q_INDEXES
Valeurs par défaut :
Phase 6 : Dernière QT_UF connue¶
Sous-requête : LAST_QT_UF
Objectif : Calculer le prix unitaire actuel
-- Dernière ligne de facture avec QT_UF > 0
ROW_NUMBER() OVER (
PARTITION BY c.ID_CLN, f.ID_ART
ORDER BY f.DT_CDE DESC, f.ID_FAC DESC
)
-- Prix unitaire
PRIX_TARIF_ACTUEL as PRIX_UNITAIRE_ACTUEL
Valeur par défaut :
Filtrage final ZOOM1¶
Clause WHERE finale :
Toutes les offres qui ne sont pas : - ZOOM1 explicitement - Mappées TYPE_CLIENT - Mappées TYPE_RESTAURANT
...sont exclues du processus PT2QE.
Matching avec les corridors PT1CE¶
Principe du matching en cascade¶
PT2QE utilise une approche fallback en 2 niveaux pour maximiser la couverture des offres :
- Tentative MASTER : Matching exact sur 4 dimensions
- Fallback NATIONAL : Si échec, matching sur l'article uniquement
Niveau 1 : Matching MASTER¶
Table de sortie : MATCH_MASTER (sous-requête)
Conditions de jointure :
LEFT JOIN CORRIDORS_ZOOM1_ONLY c
ON o.ID_ART = c.ID_ART -- Article exact
AND o.TYPE_CLIENT = c.TYPE_CLIENT -- Type client exact
AND o.TYPE_RESTAURANT = c.TYPE_RESTAURANT -- Type restaurant exact
AND NVL(o.GEO, 'NULL') = NVL(c.GEO, 'NULL') -- Zone géo exacte
AND c.CUBE_TYPE = 'MASTER'
Caractéristiques : - Corridor spécifique au contexte client - 4 dimensions de segmentation - Taux de matching typiquement 70-85% - Utilisé en priorité si disponible
Niveau 2 : Fallback NATIONAL¶
Table de sortie : MATCH_NATIONAL (sous-requête)
Conditions de jointure :
JOIN CORRIDORS_ZOOM1_ONLY c
ON o.ID_ART = c.ID_ART -- Article uniquement
AND c.TYPE_CLIENT = 'NATIONAL'
AND c.TYPE_RESTAURANT = 'NATIONAL'
AND (c.GEO = 'NATIONAL' OR c.GEO IS NULL)
AND c.CUBE_TYPE = 'NATIONAL'
WHERE NOT EXISTS (
-- Seulement si pas déjà matché MASTER
SELECT 1 FROM MATCH_MASTER m
WHERE m.ID_CLN = o.ID_CLN AND m.ID_ART = o.ID_ART
)
Caractéristiques : - Corridor agrégé national (toutes transactions article confondues) - 1 seule dimension (ID_ART) - Utilisé pour nouveaux clients ou segments atypiques - Taux de matching sur le résidu : 10-20%
Niveau 3 : Offres sans corridor¶
Table de sortie : NO_MATCH (sous-requête)
Condition :
Traitement :
- Toutes les colonnes corridor = NULL
- HAS_CORRIDOR = 0
- MATCH_TYPE = 'NO_MATCH'
- Ces offres sont exclues du calcul de recommandations (filtre WHERE HAS_CORRIDOR = 1)
Colonnes récupérées depuis PT1CE¶
Anciennes bornes PT0CE (référence historique)¶
PAS_ACTIF -- Prix d'achat standard actuel
PRB_RC_ACTIF -- PRB Restauration Collective actuel
PRB_COLL_ACTIF -- PRB Collectivité actuel
PRB_TO_USE -- Flag indiquant quel PRB utiliser (1=RC, 0=COLL)
PRB_ACTIF -- PRB calculé selon PRB_TO_USE
BORNE_PL1_PL2 -- Séparation PL1/PL2 ancienne
BORNE_PL2_PL3 -- Séparation PL2/PL3 ancienne
BORNE_PL3_PL4 -- Séparation PL3/PL4 ancienne
BORNE_PL4_PL5 -- Séparation PL4/PL5 ancienne
BORNE_PL5_PL6 -- Séparation PL5/PL6 ancienne
BORNE_PL6_PLX -- Séparation PL6/PLX ancienne
Nouvelles bornes PT1CE (post-optimisation)¶
NEW_PAS -- Nouveau PAS recommandé par PT1CE
NEW_PRB_RC -- Nouveau PRB RC
NEW_PRB_COLL -- Nouveau PRB COLL
NEW_PRB -- Nouveau PRB calculé selon PRB_TO_USE
NEW_BORNE_PL1_PL2 -- Nouvelle séparation PL1/PL2
NEW_BORNE_PL2_PL3 -- ⭐ Utilisé comme PLANCHER dans CHEMIN 2
NEW_BORNE_PL3_PL4
NEW_BORNE_PL4_PL5
NEW_BORNE_PL5_PL6
NEW_BORNE_PL6_PLX
Métadonnées corridor¶
PRICE_SENSITIVITY -- Sensibilité prix (HIGH/MEDIUM/LOW)
ECART_TYPE -- Écart-type du corridor
CUBE_TYPE -- MASTER ou NATIONAL
STATUS -- OPTIMAL (filtre appliqué)
SOURCE_ZOOM -- ZOOM1/ZOOM2/ZOOM3
Calculs dérivés¶
PCT_HAUSSE_PAS -- (NEW_PAS - PAS_ACTIF) / PAS_ACTIF
HAS_CORRIDOR -- 1 si corridor trouvé, 0 sinon
MATCH_TYPE -- MASTER / NATIONAL / NO_MATCH
Les deux logiques de recommandation¶
Recommandation 1 : Repositionnement par paliers¶
Principe¶
RECO1 analyse la position actuelle du prix dans le corridor et propose un repositionnement vers les bornes supérieures. L'objectif est de faire progresser les prix vers des paliers plus rémunérateurs.
Configuration des règles¶
Les règles de repositionnement sont paramétrables via le fichier config/pt2qe_config.json :
{
"recommendations": {
"reco1_rules": [
{
"position": "ABOVE_PL1",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL1_PL2",
"action": "NO_CHANGE",
"target": "PRIX_TARIF_ACTUEL",
"comment": "Prix déjà en PL1, pas de changement"
},
{
"position": "PL1_PL2",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL2_PL3",
"action": "TO_PL1",
"target": "NEW_BORNE_PL1_PL2",
"comment": "Remonter vers PL1"
},
...
]
}
}
Structure d'une règle :
- position : Nom de la position source
- condition : Expression SQL de détection
- action : Nom de l'action à effectuer
- target : Colonne SQL cible pour le nouveau prix
- comment : Documentation de la règle
Génération SQL dynamique¶
Le code Python (PT2QEConfig.build_reco1_sql_case()) génère automatiquement le CASE SQL depuis la configuration :
CASE
WHEN ed.PRIX_TARIF_ACTUEL > ed.NEW_BORNE_PL1_PL2
THEN ed.PRIX_TARIF_ACTUEL -- Prix déjà en PL1
WHEN ed.PRIX_TARIF_ACTUEL > ed.NEW_BORNE_PL2_PL3
THEN ed.NEW_BORNE_PL1_PL2 -- Remonter vers PL1
WHEN ed.PRIX_TARIF_ACTUEL > ed.NEW_BORNE_PL3_PL4
THEN ed.NEW_BORNE_PL1_PL2 -- Remonter vers PL1
WHEN ed.PRIX_TARIF_ACTUEL > ed.NEW_BORNE_PL4_PL5
THEN ed.NEW_BORNE_PL2_PL3 -- Remonter vers PL2
WHEN ed.PRIX_TARIF_ACTUEL > ed.NEW_BORNE_PL5_PL6
THEN ed.NEW_BORNE_PL3_PL4 -- Remonter vers PL3
WHEN ed.PRIX_TARIF_ACTUEL > ed.NEW_BORNE_PL6_PLX
THEN ed.NEW_BORNE_PL5_PL6 -- Remonter vers PL5
WHEN ed.PRIX_TARIF_ACTUEL >= ed.NEW_PAS
THEN ed.NEW_BORNE_PL6_PLX -- Remonter vers PL6
ELSE ed.NEW_PAS -- Remonter au PAS minimum
END as RECO1_BASE
Modification des règles de repositionnement¶
Pour changer la logique de repositionnement :
- Éditer le fichier :
config/pt2qe_config.json - Modifier la section :
recommendations.reco1_rules - Exemples de modifications possibles :
// Exemple 1 : Repositionnement plus agressif
// Au lieu de PL2→PL3 aller vers PL2→PL1
{
"position": "PL2_PL3",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL3_PL4",
"action": "TO_PL1", // ← Au lieu de TO_PL2
"target": "NEW_BORNE_PL1_PL2",
"comment": "Remonter directement vers PL1"
}
// Exemple 2 : Ajouter une règle intermédiaire
{
"position": "PL3_MID",
"condition": "PRIX_TARIF_ACTUEL > (NEW_BORNE_PL3_PL4 + NEW_BORNE_PL2_PL3) / 2",
"action": "TO_PL2",
"target": "NEW_BORNE_PL2_PL3",
"comment": "Milieu de PL3 vers PL2"
}
// Exemple 3 : Repositionnement conservateur
// Ne remonter que d'un palier
{
"position": "PL5_PL6",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL6_PLX",
"action": "TO_PL5",
"target": "NEW_BORNE_PL5_PL6", // ← Au lieu de PL4
"comment": "Remonter seulement d'un palier"
}
- Relancer PT2QE : La nouvelle configuration sera chargée automatiquement au prochain run
⚠️ Attention : Les conditions doivent être mutuellement exclusives et triées de la plus restrictive à la moins restrictive.
Règles par défaut (sans configuration)¶
Si pt2qe_config.json est absent ou incomplet, PT2QE utilise les règles par défaut codées en dur dans PT2QEConfig._defaults.
Recommandation 2 : Hausse proportionnelle au PAS¶
Principe¶
RECO2 applique le même pourcentage de hausse que celui du PAS du corridor au prix actuel de l'offre.
Formule¶
RECO2 = PRIX_TARIF_ACTUEL × (1 + PCT_HAUSSE_PAS)
Où :
PCT_HAUSSE_PAS = (NEW_PAS - PAS_ACTIF) / PAS_ACTIF
Exemple de calcul¶
Contexte corridor : - PAS ancien : 10,00 € - PAS nouveau : 11,00 € (+10%) - PCT_HAUSSE_PAS = 0,10
Application à une offre : - Prix tarif actuel : 15,00 € - RECO2 = 15,00 × 1,10 = 16,50 € - Hausse recommandée : +1,50 € (+10%)
Caractéristiques¶
Avantages : - Suit mécaniquement l'évolution des coûts d'achat - Simple et transparent - Proportionnel au prix existant
Inconvénients : - Ne repositionne pas dans le corridor - Peut rester dans des paliers bas - Moins agressif que RECO1 en général
Arbre de décision à 3 chemins¶
Vue d'ensemble¶
PT2QE n'applique pas systématiquement la logique MAX(RECO1, RECO2). Le prix final dépend d'un arbre de décision qui analyse le contexte de chaque offre.
┌─────────────────┐
│ Offre de prix │
└────────┬────────┘
│
┌────────────▼────────────┐
│ NEW_PAS < PAS_ACTIF ? │
└────────┬───────┬────────┘
│ OUI │ NON
│ │
┌────────────▼ └───────────────┐
│ CHEMIN 1 │ │
│ GEL PRIX │ │
│ Prix = Actuel │ │
└─────────────────────┘ │
┌──────────▼──────────┐
│ Prix dans PL1 │
│ anciennes bornes ? │
└──────┬─────┬────────┘
│ OUI │ NON
│ │
┌────────────▼ └────────────┐
│ CHEMIN 2 │
│ CONSERVATION PREMIUM │
│ Prix + Plancher │
└───────────────────────────────┘
│
┌────────────▼────────────┐
│ CHEMIN 3 │
│ OPTIMISATION STANDARD │
│ MAX(RECO1, RECO2) │
└─────────────────────────┘
CHEMIN 1 : PAS_BAISSE_GEL_PRIX¶
Condition de déclenchement¶
Détection : - Le nouveau PAS recommandé par PT1CE est inférieur au PAS actif - Indique une baisse du coût d'achat
Logique de calcul¶
PRIX_RECOMMANDE = PRIX_TARIF_ACTUEL
PCT_HAUSSE_FINALE = 0
RECO_SELECTIONNEE = 'GEL_PRIX'
CAPPING_APPLIED = 'GEL_PAS'
Principe : - Le prix reste strictement inchangé - Aucune recommandation n'est calculée (RECO1 et RECO2 sont calculées mais ignorées) - Pas de hausse appliquée (0%)
Rationale métier¶
Pourquoi geler le prix en cas de baisse PAS ? - Ne pas répercuter les baisses de coût au client - Conserver la marge dégagée - Éviter un effet yo-yo tarifaire - Maintenir une perception de stabilité prix
Exemple :
Corridor avant PT1CE :
PAS_ACTIF = 12,00 €
PRIX_TARIF_ACTUEL = 18,00 €
Marge = 6,00 €
Corridor après PT1CE :
NEW_PAS = 11,00 € (baisse de 1€)
→ CHEMIN 1 déclenché
→ PRIX_RECOMMANDE = 18,00 € (inchangé)
→ Nouvelle marge = 7,00 € (+1€)
CHEMIN 2 : PL1_CONSERVATION_PREMIUM¶
Conditions de déclenchement¶
DECISION_PATH = 'PL1_CONSERVATION_PREMIUM'
WHERE NEW_PAS >= PAS_ACTIF -- Pas de baisse PAS
AND PRIX_TARIF_ACTUEL <= PRB_ACTIF -- Prix dans anciennes bornes
AND PRIX_TARIF_ACTUEL > BORNE_PL1_PL2 -- Position PL1
Détection : - Le prix actuel est dans le PL1 des anciennes bornes (bornes PT0CE) - Clients "premium" facturés au niveau le plus élevé du corridor historique - Le PAS ne baisse pas (sinon CHEMIN 1 prioritaire)
Logique de calcul¶
-- Étape 1 : Conservation du prix actuel
PRIX_BASE = PRIX_TARIF_ACTUEL
-- Étape 2 : Application plancher PL2_PL3
PRIX_AVEC_PLANCHER = GREATEST(PRIX_BASE, NEW_BORNE_PL2_PL3)
-- Étape 3 : Application capping PRB final
PRIX_RECOMMANDE = LEAST(PRIX_AVEC_PLANCHER, NEW_PRB)
-- Métadonnées
RECO_SELECTIONNEE = 'CONSERVATION_PREMIUM'
CAPPING_APPLIED = CASE
WHEN PRIX_AVEC_PLANCHER > NEW_PRB THEN 'PRB_FINAL'
WHEN PRIX_BASE < NEW_BORNE_PL2_PL3 THEN 'PLANCHER_PL2_PL3'
ELSE 'NONE'
END
Plancher de sécurité PL2_PL3¶
Objectif : Protéger contre la compression du corridor
Mécanisme :
Scénario d'activation :
Anciennes bornes :
PL1_PL2 = 22,00 €
PRIX_ACTUEL = 23,00 € (en PL1)
Nouvelles bornes (après recalibrage PT1CE) :
NEW_PL1_PL2 = 24,00 € (hausse)
NEW_PL2_PL3 = 20,00 €
Sans plancher :
PRIX_FINAL = 23,00 € (inchangé)
→ Passe de PL1 (anciennes) à PL2 (nouvelles)
→ Dégradation relative
Avec plancher :
PRIX_FINAL = MAX(23,00 ; 20,00) = 23,00 €
→ Reste à 23,00 € (aucun changement nécessaire ici)
Cas extrême :
NEW_PL2_PL3 = 25,00 € (forte compression)
→ PRIX_FINAL = MAX(23,00 ; 25,00) = 25,00 €
→ Remontée forcée pour rester dans PL2
Flag de détection :
Capping PRB final¶
Comme tous les chemins, le CHEMIN 2 applique le capping PRB en dernière étape :
Ce capping peut s'appliquer si : - Le corridor s'est fortement compressé (écart PAS-PRB réduit) - Le plancher PL2_PL3 a remonté le prix au-dessus du PRB
Rationale métier¶
Pourquoi conserver les clients premium ? - Maintenir les clients les mieux valorisés en position haute - Éviter un effet de "décalibrage" suite à l'optimisation PT1CE - Le plancher PL2_PL3 assure qu'ils ne descendent pas trop dans le nouveau corridor - Priorité à la stabilité relationnelle sur l'optimisation tarifaire agressive
Exemple complet :
Avant PT1CE :
PRB_ACTIF = 25,00 €
BORNE_PL1_PL2 = 22,00 €
PRIX_ACTUEL = 24,00 € (en PL1, bien valorisé)
Après PT1CE :
NEW_PRB = 26,00 €
NEW_BORNE_PL1_PL2 = 23,50 €
NEW_BORNE_PL2_PL3 = 21,00 €
→ CHEMIN 2 déclenché
→ PRIX_BASE = 24,00 € (conservation)
→ PLANCHER = MAX(24,00 ; 21,00) = 24,00 € (pas d'effet)
→ PRIX_RECOMMANDE = MIN(24,00 ; 26,00) = 24,00 €
→ PCT_HAUSSE_FINALE = 0% (stabilité totale)
CHEMIN 3 : OPTIMISATION_STANDARD¶
Condition de déclenchement¶
DECISION_PATH = 'OPTIMISATION_STANDARD'
WHERE NEW_PAS >= PAS_ACTIF -- Pas de baisse PAS
AND NOT (
PRIX_TARIF_ACTUEL <= PRB_ACTIF
AND PRIX_TARIF_ACTUEL > BORNE_PL1_PL2
) -- Pas en PL1 anciennes
Détection : - Tous les cas qui ne sont ni CHEMIN 1 ni CHEMIN 2 - Représente la majorité des offres (70-90%) - Cas "standard" d'optimisation tarifaire
Cascade de capping (ordre d'application)¶
Le CHEMIN 3 applique une cascade de capping en 3 étapes avant la sélection finale :
RECO1_BASE (repositionnement brut)
↓
[ÉTAPE 1] Capping sensibilité prix
↓
RECO1_APRES_CAPPING_SENSIBILITE
↓
[ÉTAPE 2] Capping basiques (50% max)
↓
RECO1_AVEC_CAPPING (finale)
↓
[SÉLECTION] MAX(RECO1_AVEC_CAPPING, RECO2)
↓
[ÉTAPE 3] Capping PRB final
↓
PRIX_RECOMMANDE
ÉTAPE 1 : Capping sensibilité prix¶
Objectif : Limiter les hausses selon la sensibilité du produit
Mécanisme :
RECO1_APRES_CAPPING_SENSIBILITE = CASE
WHEN PRICE_SENSITIVITY = 'HIGH' AND CAPPING_HIGH IS NOT NULL THEN
LEAST(RECO1_BASE, PRIX_TARIF_ACTUEL * (1 + CAPPING_HIGH))
WHEN PRICE_SENSITIVITY = 'MEDIUM' AND CAPPING_MEDIUM IS NOT NULL THEN
LEAST(RECO1_BASE, PRIX_TARIF_ACTUEL * (1 + CAPPING_MEDIUM))
WHEN PRICE_SENSITIVITY = 'LOW' AND CAPPING_LOW IS NOT NULL THEN
LEAST(RECO1_BASE, PRIX_TARIF_ACTUEL * (1 + CAPPING_LOW))
ELSE RECO1_BASE
END
Taux par défaut (fichier inputs/capping_type_client.csv) :
- CAPPING_HIGH = 0,025 (2,5% maximum)
- CAPPING_MEDIUM = 0,05 (5% maximum)
- CAPPING_LOW = 0,075 (7,5% maximum)
Exemple :
PRIX_ACTUEL = 20,00 €
RECO1_BASE = 24,00 € (+20%)
PRICE_SENSITIVITY = HIGH
CAPPING_HIGH = 0,025
→ Prix max avec capping = 20,00 × 1,025 = 20,50 €
→ RECO1_APRES_CAPPING_SENSIBILITE = MIN(24,00 ; 20,50) = 20,50 €
→ Capping actif (ramène de +20% à +2,5%)
Modification des cappings sensibilité :
Pour ajuster les taux par TYPE_CLIENT :
- Éditer :
inputs/capping_type_client.csv -
Format :
-
Relancer PT2QE : Les nouveaux taux seront appliqués
ÉTAPE 2 : Capping basiques (50% max)¶
Objectif : Limiter les hausses excessives sur les produits de base
Mécanisme :
RECO1_AVEC_CAPPING = CASE
WHEN LC_ATTRIBUT = 'Basiques' THEN
LEAST(
RECO1_APRES_CAPPING_SENSIBILITE,
PRIX_TARIF_ACTUEL * 1.50
)
ELSE RECO1_APRES_CAPPING_SENSIBILITE
END
Comportement :
- S'applique uniquement si LC_ATTRIBUT = 'Basiques'
- Vient APRÈS le capping sensibilité
- Écrase le capping sensibilité si plus restrictif
Exemple cascade complète :
PRIX_ACTUEL = 10,00 €
RECO1_BASE = 18,00 € (+80%)
PRICE_SENSITIVITY = LOW
CAPPING_LOW = 0,075 (7,5%)
LC_ATTRIBUT = 'Basiques'
→ ÉTAPE 1 : Capping sensibilité
Prix max = 10,00 × 1,075 = 10,75 €
RECO1_APRES_CAPPING = MIN(18,00 ; 10,75) = 10,75 €
→ ÉTAPE 2 : Capping basiques
Prix max basiques = 10,00 × 1,50 = 15,00 €
RECO1_AVEC_CAPPING = MIN(10,75 ; 15,00) = 10,75 €
→ Pas d'effet (sensibilité plus restrictive)
Autre exemple :
PRIX_ACTUEL = 10,00 €
RECO1_BASE = 18,00 € (+80%)
PRICE_SENSITIVITY = LOW
CAPPING_LOW = 0,20 (20%)
LC_ATTRIBUT = 'Basiques'
→ ÉTAPE 1 : Capping sensibilité
Prix max = 10,00 × 1,20 = 12,00 €
RECO1_APRES_CAPPING = MIN(18,00 ; 12,00) = 12,00 €
→ ÉTAPE 2 : Capping basiques
Prix max basiques = 10,00 × 1,50 = 15,00 €
RECO1_AVEC_CAPPING = MIN(12,00 ; 15,00) = 12,00 €
→ Pas d'effet (sensibilité plus restrictive)
Cas où basiques s'applique :
PRIX_ACTUEL = 10,00 €
RECO1_BASE = 22,00 € (+120%)
PRICE_SENSITIVITY = NULL (pas de capping sensibilité)
LC_ATTRIBUT = 'Basiques'
→ ÉTAPE 1 : Pas de capping
RECO1_APRES_CAPPING = 22,00 €
→ ÉTAPE 2 : Capping basiques
Prix max basiques = 10,00 × 1,50 = 15,00 €
RECO1_AVEC_CAPPING = MIN(22,00 ; 15,00) = 15,00 €
→ Capping actif (ramène de +120% à +50%)
Modification du seuil basiques :
Le taux de 50% est codé en dur dans la requête SQL et dans PT2QEConfig._defaults['capping']['basiques'].
Pour le modifier :
1. Éditer : config/pt2qe_config.json
2. Section :
calculate_recommandations.py :
-- Ligne à modifier (apparaît plusieurs fois)
r.PRIX_TARIF_ACTUEL * 1.50 -- Changer 1.50 en 1.60 pour 60%
SÉLECTION : MAX(RECO1_AVEC_CAPPING, RECO2)¶
Mécanisme :
PRIX_AVANT_CAPPING_PRB = GREATEST(RECO1_AVEC_CAPPING, RECO2)
RECO_TYPE = CASE
WHEN RECO1_AVEC_CAPPING >= RECO2 THEN 'REPOSITIONNEMENT_PALIERS'
ELSE 'HAUSSE_PROPORTIONNELLE_PAS'
END
RECO_SELECTIONNEE = CASE
WHEN RECO1_AVEC_CAPPING >= RECO2 THEN 'RECO1_REPOSITIONNEMENT_PALIERS'
ELSE 'RECO2_HAUSSE_PROPORTIONNELLE_PAS'
END
Principes : - RECO1 est privilégiée en cas d'égalité - RECO2 n'est pas cappée (suit mécaniquement le PAS) - La cascade de capping ne s'applique qu'à RECO1
Exemple de sélection :
Cas A : RECO1 gagne
RECO1_AVEC_CAPPING = 18,00 €
RECO2 = 17,50 €
→ PRIX_AVANT_PRB = 18,00 €
→ RECO_SELECTIONNEE = RECO1_REPOSITIONNEMENT_PALIERS
Cas B : RECO2 gagne
RECO1_AVEC_CAPPING = 16,00 €
RECO2 = 17,50 €
→ PRIX_AVANT_PRB = 17,50 €
→ RECO_SELECTIONNEE = RECO2_HAUSSE_PROPORTIONNELLE_PAS
Cas C : Égalité
RECO1_AVEC_CAPPING = 17,50 €
RECO2 = 17,50 €
→ PRIX_AVANT_PRB = 17,50 €
→ RECO_SELECTIONNEE = RECO1_REPOSITIONNEMENT_PALIERS (priorité)
ÉTAPE 3 : Capping PRB final¶
Objectif : Garantir que le prix ne dépasse jamais le PRB
Mécanisme :
PRIX_RECOMMANDE = LEAST(PRIX_AVANT_CAPPING_PRB, NEW_PRB)
CAPPING_PRB_APPLIED = CASE
WHEN PRIX_AVANT_CAPPING_PRB > NEW_PRB THEN 1
ELSE 0
END
Application : - S'applique à TOUS les chemins (1, 2, 3) - Vient en dernière étape (capping ultime) - Protège contre la compression du corridor
Exemple :
RECO1_AVEC_CAPPING = 22,00 €
RECO2 = 21,50 €
NEW_PRB = 20,00 €
→ PRIX_AVANT_PRB = MAX(22,00 ; 21,50) = 22,00 €
→ PRIX_RECOMMANDE = MIN(22,00 ; 20,00) = 20,00 €
→ CAPPING_PRB_APPLIED = 1
→ CAPPING_APPLIED = 'PRB_FINAL'
Synthèse du capping appliqué¶
Colonne : CAPPING_APPLIED
Ordre de priorité (du plus contraignant au moins) :
1. GEL_PAS : Gel total (CHEMIN 1)
2. PRB_FINAL : Plafond PRB absolu
3. PLANCHER_PL2_PL3 : Plancher de sécurité (CHEMIN 2)
4. BASIQUES_50PCT : Capping basiques à +50%
5. SENSIBILITE : Capping selon sensibilité prix
6. NONE : Aucun capping appliqué
Logique de détermination :
CAPPING_APPLIED = CASE
-- Priorité 1 : Gel total
WHEN DECISION_PATH = 'PAS_BAISSE_GEL_PRIX' THEN 'GEL_PAS'
-- Priorité 2 : Capping PRB
WHEN CAPPING_PRB_APPLIED = 1 THEN 'PRB_FINAL'
-- Priorité 3 : Plancher PL2_PL3
WHEN PLANCHER_PL2_APPLIED = 1 THEN 'PLANCHER_PL2_PL3'
-- Priorité 4 : Capping basiques
WHEN CAPPING_BASIQUES_APPLIED = 1 THEN 'BASIQUES_50PCT'
-- Priorité 5 : Capping sensibilité
WHEN CAPPING_SENSIBILITE_APPLIED = 1 THEN 'SENSIBILITE'
-- Aucun capping
ELSE 'NONE'
END
Exemple de détermination :
Cas 1 : Capping sensibilité ET capping PRB actifs
→ CAPPING_APPLIED = 'PRB_FINAL' (priorité 2 > priorité 5)
Cas 2 : Capping sensibilité ET capping basiques actifs
→ CAPPING_APPLIED = 'BASIQUES_50PCT' (priorité 4 > priorité 5)
→ Note : En pratique, si basiques s'applique, c'est qu'il est plus restrictif
que sensibilité, donc basiques a bien agi
Cas 3 : Seulement capping sensibilité actif
→ CAPPING_APPLIED = 'SENSIBILITE'
Cas 4 : Aucun capping actif
→ CAPPING_APPLIED = 'NONE'
Les 3 positions du prix¶
Philosophie¶
PT2QE calcule et expose 3 positions pour chaque offre, permettant de tracer l'évolution du prix à travers les différentes étapes du processus de pricing.
Position 1 : Prix actuel dans ANCIENNES bornes¶
Colonne : POSITION_TARIF_ACTUEL_DANS_ANCIENNES_BORNES
Référentiel : Bornes PT0CE (pré-optimisation)
Calcul :
CASE
WHEN PRIX_TARIF_ACTUEL > PRB_ACTIF THEN 'ABOVE_PRB'
WHEN PRIX_TARIF_ACTUEL >= BORNE_PL1_PL2 THEN 'PL1'
WHEN PRIX_TARIF_ACTUEL >= BORNE_PL2_PL3 THEN 'PL2'
WHEN PRIX_TARIF_ACTUEL >= BORNE_PL3_PL4 THEN 'PL3'
WHEN PRIX_TARIF_ACTUEL >= BORNE_PL4_PL5 THEN 'PL4'
WHEN PRIX_TARIF_ACTUEL >= BORNE_PL5_PL6 THEN 'PL5'
WHEN PRIX_TARIF_ACTUEL >= BORNE_PL6_PLX THEN 'PL6'
WHEN PRIX_TARIF_ACTUEL >= PAS_ACTIF THEN 'PLX'
ELSE 'BELOW_PAS'
END
Utilité : - Point de départ historique - Sert à détecter les clients PL1 (CHEMIN 2) - Permet de mesurer l'évolution post-optimisation
Position 2 : Prix actuel dans NOUVELLES bornes¶
Colonne : PALIER_TARIF_ACTUEL_VS_NOUVELLES_BORNES
Référentiel : Bornes PT1CE (post-optimisation)
Calcul :
CASE
WHEN PRIX_TARIF_ACTUEL > NEW_PRB THEN 'ABOVE_PRB'
WHEN PRIX_TARIF_ACTUEL >= NEW_BORNE_PL1_PL2 THEN 'PL1'
WHEN PRIX_TARIF_ACTUEL >= NEW_BORNE_PL2_PL3 THEN 'PL2'
WHEN PRIX_TARIF_ACTUEL >= NEW_BORNE_PL3_PL4 THEN 'PL3'
WHEN PRIX_TARIF_ACTUEL >= NEW_BORNE_PL4_PL5 THEN 'PL4'
WHEN PRIX_TARIF_ACTUEL >= NEW_BORNE_PL5_PL6 THEN 'PL5'
WHEN PRIX_TARIF_ACTUEL >= NEW_BORNE_PL6_PLX THEN 'PL6'
WHEN PRIX_TARIF_ACTUEL >= NEW_PAS THEN 'PLX'
ELSE 'BELOW_PAS'
END
Utilité : - Montre l'impact du recalibrage PT1CE - Un prix peut changer de palier sans bouger (corridor déplacé) - Base pour calculer RECO1
Position 3 : Prix recommandé dans NOUVELLES bornes¶
Colonne : POSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNES
Référentiel : Bornes PT1CE (post-optimisation)
Calcul :
CASE
WHEN PRIX_RECOMMANDE > NEW_PRB THEN 'ABOVE_PRB'
WHEN PRIX_RECOMMANDE >= NEW_BORNE_PL1_PL2 THEN 'PL1'
WHEN PRIX_RECOMMANDE >= NEW_BORNE_PL2_PL3 THEN 'PL2'
WHEN PRIX_RECOMMANDE >= NEW_BORNE_PL3_PL4 THEN 'PL3'
WHEN PRIX_RECOMMANDE >= NEW_BORNE_PL4_PL5 THEN 'PL4'
WHEN PRIX_RECOMMANDE >= NEW_BORNE_PL5_PL6 THEN 'PL5'
WHEN PRIX_RECOMMANDE >= NEW_BORNE_PL6_PLX THEN 'PL6'
WHEN PRIX_RECOMMANDE >= NEW_PAS THEN 'PLX'
ELSE 'BELOW_PAS'
END
Utilité : - Objectif atteint par PT2QE - Mesure l'efficacité du repositionnement - Permet de valider la cohérence des recommandations
Exemple de trajectoire¶
Offre client A - article X
POSITION 1 (anciennes bornes) : PL3
PRB_ACTIF = 25,00 €
BORNE_PL2_PL3 = 20,00 €
BORNE_PL3_PL4 = 15,00 €
PRIX_ACTUEL = 18,00 €
→ Entre PL3_PL4 et PL2_PL3 = PL3
POSITION 2 (nouvelles bornes, prix inchangé) : PL4
NEW_PRB = 27,00 €
NEW_BORNE_PL3_PL4 = 19,00 € (hausse de 4€)
NEW_BORNE_PL4_PL5 = 14,00 €
PRIX_ACTUEL = 18,00 € (inchangé)
→ Entre PL4_PL5 et PL3_PL4 = PL4
→ Décalibrage : passe de PL3 à PL4 sans bouger
POSITION 3 (après recommandation PT2QE) : PL2
RECO1_BASE = 22,00 € (objectif PL2_PL3 = 21,00 €)
RECO1_AVEC_CAPPING = 19,80 € (capping sensibilité)
RECO2 = 19,26 € (hausse PAS +7%)
PRIX_RECOMMANDE = MAX(19,80 ; 19,26) = 19,80 €
→ Entre PL2_PL3 et PL1_PL2 = PL2
→ Repositionnement réussi : de PL4 à PL2
SYNTHÈSE :
PL3 (anciennes) → PL4 (nouvelles, recalibrage) → PL2 (optimisation)
18,00 € → 18,00 € → 19,80 € (+10%)
Valeurs possibles¶
Toutes les positions utilisent le même référentiel de valeurs :
ABOVE_PRB: Prix au-dessus du PRB (anormal)PL1: Palier 1 (meilleure valorisation)PL2: Palier 2PL3: Palier 3PL4: Palier 4PL5: Palier 5PL6: Palier 6PLX: Palier X (entre PAS et PL6)BELOW_PAS: En dessous du PAS (anormal)
Ajustement des cappings (Étape optionnelle)¶
Principe¶
Après avoir généré une première série de recommandations, l'utilisateur peut affiner les cappings par cube (combinaison UNIVERS × TYPE_CLIENT × TYPE_RESTAURANT × GEO) et relancer le calcul.
Workflow d'ajustement¶
Run initial
↓
Génération capping_cubes_generated.csv
↓
Analyse des impacts
↓
Décision d'ajustement ?
├─ OUI → Copier/renommer en capping_cubes_corrections.csv
│ Modifier les valeurs
│ Lancer 2_ajuster_cappings.bat
│ Revenir à "Analyse des impacts"
│
└─ NON → Passer à la génération finale
Fichier capping_cubes_generated.csv¶
Génération : Automatique lors du calcul des recommandations
Emplacement : outputs/run_YYYYMMDD_HHMMSS/capping_cubes_generated.csv
Structure :
UNIVERS;TYPE_CLIENT;TYPE_RESTAURANT;GEO;CUBE_TYPE;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
ZOOM1;RCI PI GI;TRADITIONNEL;ILE-DE-FRANCE;MASTER;0,025;0,05;0,075
ZOOM1;RCI PI GI;RESTAURATION_RAPIDE;ILE-DE-FRANCE;MASTER;0,025;0,05;0,075
ZOOM1;NATIONAL;NATIONAL;NATIONAL;NATIONAL;0,03;0,10;0,15
Contenu :
- Tous les cubes MASTER trouvés dans les offres
- Un cube NATIONAL par univers (pour le fallback)
- Cappings initiaux depuis capping_type_client.csv
Création du fichier de corrections¶
Étape 1 : Copier le fichier généré
cp outputs/run_20251103_143022/capping_cubes_generated.csv corrections/capping_cubes_corrections.csv
Étape 2 : Éditer avec Excel/LibreOffice - Garder la structure intacte (colonnes identiques) - Modifier uniquement les valeurs de capping souhaitées - Possibilité de ne modifier que certains cubes
Étape 3 : Sauvegarder au format CSV avec :
- Séparateur : ; (point-virgule)
- Décimales : , (virgule)
- Encodage : cp1252 (Windows ANSI)
Exemples de modifications¶
Cas 1 : Assouplir un segment spécifique¶
Contexte : TYPE_CLIENT "RSI HM" avec hausses trop faibles
Avant :
UNIVERS;TYPE_CLIENT;TYPE_RESTAURANT;GEO;CUBE_TYPE;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
ZOOM1;RSI HM;TRADITIONNEL;ILE-DE-FRANCE;MASTER;0,025;0,05;0,075
Après :
UNIVERS;TYPE_CLIENT;TYPE_RESTAURANT;GEO;CUBE_TYPE;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
ZOOM1;RSI HM;TRADITIONNEL;ILE-DE-FRANCE;MASTER;0,04;0,07;0,10
Effet : - HIGH : 2,5% → 4% (+60% de flexibilité) - MEDIUM : 5% → 7% (+40%) - LOW : 7,5% → 10% (+33%)
Cas 2 : Durcir un segment sensible¶
Contexte : TYPE_CLIENT "RCI PI GI" en zone rurale, hausses trop agressives
Avant :
UNIVERS;TYPE_CLIENT;TYPE_RESTAURANT;GEO;CUBE_TYPE;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
ZOOM1;RCI PI GI;RESTAURATION_RAPIDE;ZONE_RURALE;MASTER;0,025;0,05;0,075
Après :
UNIVERS;TYPE_CLIENT;TYPE_RESTAURANT;GEO;CUBE_TYPE;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
ZOOM1;RCI PI GI;RESTAURATION_RAPIDE;ZONE_RURALE;MASTER;0,015;0,03;0,05
Effet : - HIGH : 2,5% → 1,5% (-40% de flexibilité) - MEDIUM : 5% → 3% (-40%) - LOW : 7,5% → 5% (-33%)
Cas 3 : Différencier par TYPE_RESTAURANT¶
Contexte : RESTAURATION_RAPIDE peut accepter des hausses plus fortes
Avant :
ZOOM1;RCI PI GI;TRADITIONNEL;ILE-DE-FRANCE;MASTER;0,025;0,05;0,075
ZOOM1;RCI PI GI;RESTAURATION_RAPIDE;ILE-DE-FRANCE;MASTER;0,025;0,05;0,075
Après :
ZOOM1;RCI PI GI;TRADITIONNEL;ILE-DE-FRANCE;MASTER;0,025;0,05;0,075
ZOOM1;RCI PI GI;RESTAURATION_RAPIDE;ILE-DE-FRANCE;MASTER;0,035;0,065;0,095
Effet : Restauration rapide accepte ~30% de hausses en plus
Application des corrections¶
Commande :
Ou via le menu :
Paramètres implicites :
- --skip-extraction : Ne réextrait pas les offres (utilise tables existantes)
- --apply-corrections : Active le mode corrections
- --corrections-folder corrections : Lit depuis le dossier corrections
Comportement :
- Lecture des tables existantes :
PT2QE_PRICE_OFFERS(offres extraites lors du run initial)-
PT2QE_PRICE_OFFERS_ENRICHED(jointure corridors existante) -
Upload des corrections :
- Création table temporaire
TEMP_CAPPING_CORRECTIONS -
Upload du CSV ligne par ligne
-
Recalcul complet :
- Génération nouvelle table
PT2QE_CAPPING_CUBES(écrase l'ancienne) - Recalcul
PT2QE_RECOMMENDATIONSavec nouveaux cappings - Conservation de l'arbre de décision (DECISION_PATH inchangé)
-
Recalcul de la cascade de capping avec nouveaux paramètres
-
Nouvelle génération d'analyses :
- Tous les CSV sont régénérés dans
outputs/corrections_YYYYMMDD_HHMMSS/ capping_cubes_generated.csvreflète les nouvelles valeurs
Logique de mise à jour¶
Requête MERGE (dans apply_capping_rules.py) :
MERGE INTO PT2QE_RECOMMENDATIONS r
USING (SELECT * FROM TEMP_CAPPING_CORRECTIONS) c
ON (
r.UNIVERS = c.UNIVERS
AND r.TYPE_CLIENT = c.TYPE_CLIENT
AND r.TYPE_RESTAURANT = c.TYPE_RESTAURANT
AND NVL(r.GEO, 'NULL') = NVL(c.GEO, 'NULL')
)
WHEN MATCHED THEN UPDATE SET
-- Recalcul RECO1_APRES_CAPPING_SENSIBILITE
r.RECO1_APRES_CAPPING_SENSIBILITE = CASE
WHEN r.PRICE_SENSITIVITY = 'HIGH' AND c.CAPPING_HIGH IS NOT NULL THEN
LEAST(r.RECO1_BASE, r.PRIX_TARIF_ACTUEL * (1 + c.CAPPING_HIGH))
WHEN r.PRICE_SENSITIVITY = 'MEDIUM' AND c.CAPPING_MEDIUM IS NOT NULL THEN
LEAST(r.RECO1_BASE, r.PRIX_TARIF_ACTUEL * (1 + c.CAPPING_MEDIUM))
WHEN r.PRICE_SENSITIVITY = 'LOW' AND c.CAPPING_LOW IS NOT NULL THEN
LEAST(r.RECO1_BASE, r.PRIX_TARIF_ACTUEL * (1 + c.CAPPING_LOW))
ELSE r.RECO1_BASE
END,
-- Recalcul RECO1_AVEC_CAPPING (avec basiques)
r.RECO1_AVEC_CAPPING = CASE
WHEN r.LC_ATTRIBUT = 'Basiques' THEN
LEAST(
[RECO1_APRES_CAPPING_SENSIBILITE avec nouveaux cappings],
r.PRIX_TARIF_ACTUEL * 1.50
)
ELSE [RECO1_APRES_CAPPING_SENSIBILITE avec nouveaux cappings]
END,
-- Recalcul PRIX_RECOMMANDE selon DECISION_PATH
r.PRIX_RECOMMANDE = CASE
WHEN r.DECISION_PATH = 'PAS_BAISSE_GEL_PRIX' THEN r.PRIX_TARIF_ACTUEL
WHEN r.DECISION_PATH = 'PL1_CONSERVATION_PREMIUM' THEN
LEAST(GREATEST(r.PRIX_TARIF_ACTUEL, r.NEW_BORNE_PL2_PL3), r.NEW_PRB)
WHEN r.DECISION_PATH = 'OPTIMISATION_STANDARD' THEN
LEAST(GREATEST([RECO1_AVEC_CAPPING recalculée], r.RECO2), r.NEW_PRB)
END,
-- Recalcul PCT_HAUSSE_FINALE
r.PCT_HAUSSE_FINALE = [formule selon DECISION_PATH],
-- Recalcul POSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNES
r.POSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNES = [CASE paliers],
-- Recalcul CAPPING_APPLIED
r.CAPPING_APPLIED = [logique de priorité],
-- Recalcul RECO_SELECTIONNEE
r.RECO_SELECTIONNEE = [selon RECO1 vs RECO2],
-- Update timestamp
r.CALCULATION_DATE = SYSDATE
Points clés : - RECO1_BASE et RECO2 ne changent pas (recalcul inutile) - DECISION_PATH ne change pas (contexte identique) - Seule la cascade de capping est recalculée - PRIX_RECOMMANDE est recalculé avec les nouvelles contraintes
Itérations successives¶
L'utilisateur peut effectuer plusieurs cycles d'ajustement :
Run 1 → capping_cubes_generated.csv → Analyse
↓
Corrections A → Run 2 → capping_cubes_generated.csv (v2) → Analyse
↓
Corrections B → Run 3 → capping_cubes_generated.csv (v3) → Analyse
↓
Validation → Génération finale
Traçabilité :
- Chaque run génère un dossier horodaté
- corrections_YYYYMMDD_HHMMSS vs run_YYYYMMDD_HHMMSS
- Possibilité de comparer les versions
Cas d'usage typiques¶
Scénario 1 : Convergence progressive¶
Objectif : Atteindre une hausse moyenne de 5%
Run 1 : Hausse moyenne 3,2% → Assouplir les cappings de 10%
Run 2 : Hausse moyenne 4,1% → Assouplir encore de 8%
Run 3 : Hausse moyenne 5,0% → Validation
Scénario 2 : Segmentation fine¶
Objectif : Différencier ILE-DE-FRANCE vs PROVINCE
Run 1 : Cappings uniformes → Identifier les écarts de comportement
Run 2 : Assouplir PROVINCE de 15% → Analyser les distributions
Run 3 : Ajuster ILE-DE-FRANCE à la baisse de 5% → Validation
Scénario 3 : Protection segments sensibles¶
Objectif : Limiter l'impact sur RSI HM
Run 1 : Hausse RSI HM = 4,8% (trop fort) → Durcir cappings RSI HM de 30%
Run 2 : Hausse RSI HM = 3,2% (acceptable) → Validation
Exports et analyses¶
Vue d'ensemble des exports¶
PT2QE génère 6 fichiers CSV à chaque run, permettant une analyse multi-angles des recommandations.
Emplacement :
- Run initial : outputs/run_YYYYMMDD_HHMMSS/
- Après corrections : outputs/corrections_YYYYMMDD_HHMMSS/
- Génération finale : outputs/final_YYYYMMDD_HHMMSS/
Format commun :
- Séparateur : ; (point-virgule)
- Décimales : , (virgule)
- Encodage : cp1252 (Windows ANSI)
- Compatible Excel/LibreOffice
Export 1 : recommendations_detail.csv¶
Objectif : Ligne à ligne, traçabilité complète de chaque recommandation
Tri : Par PCT_HAUSSE_FINALE DESC (hausses les plus fortes en haut)
Colonnes principales :
Identifiants :
ID_CLN, LC_CLN -- Client
ID_ART, LC_ART -- Article
ID_CND -- Condition
DT_DEB_CONDITION, DT_FIN_CONDITION
Catégories produit :
CATEGORIE_N1 -- Hiérarchie N1 (ex: ALIMENTAIRE)
CATEGORIE_N2 -- Hiérarchie N2 (ex: EPICERIE)
CATEGORIE_N3 -- Hiérarchie N3 (ex: CONSERVES)
CATEGORIE_N4, N5, N6 -- Niveaux détaillés
MARQUE -- Marque article
LC_ATTRIBUT -- Attribut (ex: Basiques, Premium)
Dimensions commerciales :
TYPE_CLIENT -- Segment client (ex: RCI PI GI)
TYPE_RESTAURANT -- Type établissement
GEO -- Zone géographique
UNIVERS -- ZOOM1/ZOOM2/ZOOM3
MATCH_TYPE -- MASTER / NATIONAL / NO_MATCH
Métriques historiques 4Q :
MT_CAB_4Q -- CA total hors promos
QT_UF_4Q, QT_KG_4Q -- Volumes UF et KG
MT_CAB_4Q_FERMES -- CA sur prix fermes
QT_UF_4Q_FERMES, QT_KG_4Q_FERMES
MT_CAB_4Q_INDEXES -- CA sur prix indexés
QT_UF_4Q_INDEXES, QT_KG_4Q_INDEXES
Prix et positions :
PRIX_TARIF_ACTUEL -- Prix actuel de l'offre
PRIX_UNITAIRE_ACTUEL -- Prix actuel / QT_UF
-- Position 1 : Prix actuel dans anciennes bornes
POSITION_TARIF_ACTUEL_DANS_ANCIENNES_BORNES
-- Position 2 : Prix actuel dans nouvelles bornes
PALIER_TARIF_ACTUEL_VS_NOUVELLES_BORNES
-- Position 3 : Prix recommandé dans nouvelles bornes
POSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNES
Cascade de calcul :
PRICE_SENSITIVITY -- HIGH / MEDIUM / LOW
RECO1_BASE -- Repositionnement brut
RECO1_APRES_CAPPING_SENSIBILITE -- Après étape 1 capping
RECO1_AVEC_CAPPING -- Après cascade complète
RECO2 -- Hausse proportionnelle PAS
Décision finale :
DECISION_PATH -- Chemin emprunté (1, 2 ou 3)
RECO_TYPE -- Type reco courte
RECO_SELECTIONNEE -- Recommandation explicite
CAPPING_APPLIED -- Type de capping appliqué
PRIX_RECOMMANDE -- Prix final PT2QE
PCT_HAUSSE_FINALE -- % de hausse (0.0523 = 5,23%)
Utilisation : - Base pour toutes les analyses pivots - Investigation ligne à ligne - Validation qualité - Export vers Excel pour présentations
Export 2 : statistics_by_dimension.csv¶
Objectif : Agrégations par dimension clé (TYPE_CLIENT, TYPE_RESTAURANT, UNIVERS)
Structure :
DIMENSION -- TYPE_CLIENT / TYPE_RESTAURANT / UNIVERS
VALEUR -- Valeur spécifique (ex: RCI PI GI)
NB_OFFRES -- Nombre d'offres
NB_CLIENTS -- Clients uniques
NB_ARTICLES -- Articles uniques
PRIX_MOY_ACTUEL -- Prix moyen actuel
PRIX_MOY_RECOMMANDE -- Prix moyen recommandé
PCT_HAUSSE_MOY -- Hausse moyenne (%)
PCT_HAUSSE_MIN -- Hausse minimale (%)
PCT_HAUSSE_MAX -- Hausse maximale (%)
PCT_HAUSSE_STDDEV -- Écart-type des hausses (%)
Utilisation : - Comparaison inter-segments - Identification des segments les plus impactés - Détection d'anomalies (hausse moyenne anormale) - Support aux décisions de calibrage
Export 3 : impact_analysis.csv¶
Objectif : Impact CA et distribution des hausses par segment
Agrégation : TYPE_CLIENT × UNIVERS
Colonnes :
Volumétrie :
Impact CA :
CA_ACTUEL -- Somme des prix actuels (approx CA)
CA_FUTUR -- Somme des prix recommandés
IMPACT_EUROS -- Gain CA en € (CA_FUTUR - CA_ACTUEL)
IMPACT_PCT -- Gain CA en % ((CA_FUTUR/CA_ACTUEL - 1) × 100)
HAUSSE_MOY_PCT -- Hausse moyenne pondérée
Distribution des hausses (comptage absolu) :
NB_SANS_HAUSSE -- 0%
NB_0_2PCT -- ]0%, 2%]
NB_2_5PCT -- ]2%, 5%]
NB_5_10PCT -- ]5%, 10%]
NB_10_15PCT -- ]10%, 15%]
NB_15_20PCT -- ]15%, 20%]
NB_PLUS_20PCT -- > 20%
Distribution des hausses (pourcentages) :
PCT_SANS_HAUSSE -- % d'offres à 0%
PCT_0_2 -- % d'offres ]0%, 2%]
PCT_2_5 -- % d'offres ]2%, 5%]
PCT_5_10 -- % d'offres ]5%, 10%]
PCT_10_15 -- % d'offres ]10%, 15%]
PCT_15_20 -- % d'offres ]15%, 20%]
PCT_PLUS_20 -- % d'offres > 20%
Utilisation : - Estimation du gain CA potentiel par segment - Identification des segments à fort impact - Communication business aux décideurs - Priorisation pour déploiement progressif
Export 4 : price_increase_distribution.csv¶
Objectif : Histogramme global des hausses
Tranches détaillées (10 buckets) :
00. Pas de hausse -- 0%
01. 0-2% -- ]0%, 2%]
02. 2-5% -- ]2%, 5%]
03. 5-7% -- ]5%, 7%]
04. 7-10% -- ]7%, 10%]
05. 10-12% -- ]10%, 12%]
06. 12-15% -- ]12%, 15%]
07. 15-17% -- ]15%, 17%]
08. 17-20% -- ]17%, 20%]
09. Plus de 20% -- > 20%
Métriques par tranche :
TRANCHE_HAUSSE -- Libellé de la tranche
NB_OFFRES -- Volume d'offres
NB_CLIENTS_UNIQUES -- Nombre de clients touchés
NB_ARTICLES_UNIQUES -- Nombre d'articles touchés
PRIX_MOY_ACTUEL -- Prix moyen actuel dans la tranche
PRIX_MOY_RECOMMANDE -- Prix moyen recommandé
HAUSSE_MIN_PCT -- Hausse min dans la tranche
HAUSSE_MAX_PCT -- Hausse max dans la tranche
HAUSSE_MOY_PCT -- Hausse moyenne de la tranche
PCT_OFFRES -- % d'offres dans cette tranche
PCT_CUMULE -- % cumulé jusqu'à cette tranche
Utilisation : - Vision synthétique de l'agressivité globale - Identification rapide des outliers - Communication visuelle (histogramme) - Benchmark entre simulations - Validation des paramètres de capping
Lecture rapide :
Profil CONSERVATEUR :
Profil AGRESSIF :
Profil PROBLÉMATIQUE :
Export 5 : decision_path_analysis.csv¶
Objectif : Statistiques par chemin de décision et type de recommandation
Agrégation : DECISION_PATH × RECO_SELECTIONNEE
Colonnes :
Dimensions de l'arbre :
DECISION_PATH -- PAS_BAISSE_GEL_PRIX / PL1_CONSERVATION_PREMIUM / OPTIMISATION_STANDARD
RECO_SELECTIONNEE -- GEL_PRIX / CONSERVATION_PREMIUM / RECO1_* / RECO2_*
Volumétrie :
NB_OFFRES -- Nombre d'offres par combinaison
NB_CLIENTS -- Clients uniques
NB_ARTICLES -- Articles uniques
Statistiques de hausse :
Distribution des cappings :
NB_CAP_GEL -- Nombre avec CAPPING_APPLIED = GEL_PAS
NB_CAP_PRB -- Nombre avec PRB_FINAL
NB_CAP_PLANCHER -- Nombre avec PLANCHER_PL2_PL3
NB_CAP_BASIQUES -- Nombre avec BASIQUES_50PCT
NB_CAP_SENSIBILITE -- Nombre avec SENSIBILITE
NB_SANS_CAPPING -- Nombre sans capping
Utilisation : - Comprendre la répartition entre les 3 chemins - Identifier quel chemin génère le plus de hausses - Analyser l'impact de chaque type de capping - Calibrer les seuils de déclenchement
Exemple d'analyse :
DECISION_PATH: OPTIMISATION_STANDARD
RECO_SELECTIONNEE: RECO1_REPOSITIONNEMENT_PALIERS
NB_OFFRES: 45000
HAUSSE_MOY_PCT: 6,2%
NB_CAP_SENSIBILITE: 12000
NB_CAP_BASIQUES: 3000
NB_SANS_CAPPING: 30000
→ Interprétation :
- 67% des offres en OPTIMISATION_STANDARD
- RECO1 gagne dans la majorité des cas
- Capping sensibilité actif pour 27% des offres
- Capping basiques actif pour 7%
- 67% sans capping = paliers bien calibrés
Export 6 : capping_distribution.csv¶
Objectif : Distribution détaillée des cappings appliqués
Agrégation : CAPPING_APPLIED × DECISION_PATH × RECO_SELECTIONNEE
Colonnes :
CAPPING_APPLIED -- Type de capping (GEL_PAS, PRB_FINAL, etc.)
DECISION_PATH -- Chemin emprunté
RECO_SELECTIONNEE -- Recommandation finale
NB_OFFRES -- Volume d'offres
HAUSSE_MOY_PCT -- Hausse moyenne dans cette combinaison
Types de capping analysés :
GEL_PAS -- Gel total (CHEMIN 1)
PRB_FINAL -- Plafond absolu PRB
PLANCHER_PL2_PL3 -- Plancher de sécurité (CHEMIN 2)
BASIQUES_50PCT -- Capping produits basiques
SENSIBILITE -- Capping selon sensibilité prix
NONE -- Aucun capping
Utilisation : - Mesurer l'impact de chaque type de capping - Identifier les cappings les plus contraignants - Calibrer les paramètres (ex: 50% pour basiques est-il adapté ?) - Comprendre les interactions entre chemins et cappings
Exemple d'analyse :
CAPPING_APPLIED: BASIQUES_50PCT
DECISION_PATH: OPTIMISATION_STANDARD
RECO_SELECTIONNEE: RECO1_REPOSITIONNEMENT_PALIERS
NB_OFFRES: 5000
HAUSSE_MOY_PCT: 35%
→ Interprétation :
- Capping basiques actif sur 5000 offres
- Hausse moyenne de 35% (proche du plafond 50%)
- Preuve que sans ce capping, hausses auraient été > 50%
- Le capping basiques est effectif et utile
Export 7 : capping_cubes_generated.csv¶
Objectif : Cappings générés par cube pour ajustement éventuel
Structure :
UNIVERS -- ZOOM1
TYPE_CLIENT -- Segment client
TYPE_RESTAURANT -- Type établissement
GEO -- Zone géographique
CUBE_TYPE -- MASTER ou NATIONAL
CAPPING_HIGH -- Taux HIGH (ex: 0,025)
CAPPING_MEDIUM -- Taux MEDIUM (ex: 0,05)
CAPPING_LOW -- Taux LOW (ex: 0,075)
Utilisation :
- Base pour créer capping_cubes_corrections.csv
- Voir les valeurs appliquées par cube
- Identifier les cubes à ajuster
Configuration avancée¶
Fichier pt2qe_config.json¶
Emplacement : config/pt2qe_config.json
Chargement : Automatique au démarrage de PT2QE
Structure complète :
{
"processing": {
"batch_size": 100000,
"parallel_degree": 8
},
"capping": {
"default_high": 0.05,
"default_medium": 0.15,
"default_low": 0.20,
"basiques": 0.50,
"allow_overrides": true
},
"recommendations": {
"reco1_rules": [
{
"position": "ABOVE_PL1",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL1_PL2",
"action": "NO_CHANGE",
"target": "PRIX_TARIF_ACTUEL",
"comment": "Prix déjà en PL1, pas de changement"
},
...
],
"selection_rule": "max"
},
"output": {
"csv_separator": ";",
"csv_encoding": "cp1252",
"decimal_places": 3,
"percentage_format": 2
}
}
Section processing¶
Paramètres de traitement Oracle :
"processing": {
"batch_size": 100000, // Taille des batchs SQL
"parallel_degree": 8 // Degré de parallélisation Oracle
}
Utilisation : - Optimisation des requêtes volumineuses - Ajuster selon les ressources serveur
Section capping¶
Cappings par défaut :
"capping": {
"default_high": 0.05, // 5% si HIGH et pas de valeur fichier CSV
"default_medium": 0.15, // 15% si MEDIUM
"default_low": 0.20, // 20% si LOW
"basiques": 0.50, // 50% pour produits basiques
"allow_overrides": true // Autoriser les corrections
}
Modification :
Pour changer les valeurs par défaut :
1. Éditer config/pt2qe_config.json
2. Modifier les taux souhaités
3. Relancer PT2QE
⚠️ Attention : Les valeurs du fichier CSV capping_type_client.csv prioritaires sur les valeurs du JSON.
Section recommendations¶
Règles de repositionnement RECO1 :
Voir section "Recommandation 1 : Repositionnement par paliers" pour la documentation complète.
Règle de sélection :
Section output¶
Formats d'export :
"output": {
"csv_separator": ";", // Séparateur CSV
"csv_encoding": "cp1252", // Encodage Windows
"decimal_places": 3, // Décimales pour les prix
"percentage_format": 2 // Décimales pour les %
}
Modification :
Pour changer le format d'export :
1. Éditer config/pt2qe_config.json
2. Modifier les valeurs souhaitées
3. Relancer PT2QE
Exemples :
// Format européen (défaut)
{
"csv_separator": ";",
"decimal_places": 3
}
// Format US
{
"csv_separator": ",",
"decimal_places": 2
}
Points d'attention et limites¶
Périmètre fonctionnel¶
Restrictions :
- ZOOM1 EXCLUSIF : Les offres ZOOM2/ZOOM3 sont exclues
- Offres valides : Uniquement DT_FIN > SYSDATE
- Conditions actives : FG_CND_VLD = 'X'
- Types de conditions : ZFAP, ZFSP, ZIAP, ZISP
- Séquences : A305 et A565 uniquement
Exclusions produits :
- ID_GMM = 'PSN'
- LC_HIC_SYSCO_N2 IN ('Divers', 'Freezer')
- LC_HI1 = 'Qualificatif article inconnu'
Exclusions clients :
- ID_TC_CG = 'AUTRES'
Dépendances externes¶
Prérequis obligatoires :
- Tables PT1CE :
PT1CE_OPTIMAL_ZOOM1PT1CE_OPTIMAL_ZOOM2PT1CE_OPTIMAL_ZOOM3-
Doivent contenir des corridors avec
STATUS = 'OPTIMAL' -
Tables de mapping :
PT0CE_TYPE_CLIENT_MAPPINGPT0CE_TYPE_RESTAURANT_MAPPING-
Doivent couvrir les combinaisons ZOOM1
-
Calendrier fiscal :
SYS_MD_CALENDRIER_SYSCO- Doit contenir les 4 derniers trimestres complets
Vérification : Utiliser PT2QE_Menu.bat → Option 5
Cas particuliers¶
Nouveaux clients¶
Situation : Client sans historique transactionnel
Comportement : - FG_HM = NULL → Pas de calcul UNIVERS - Pas de matching TYPE_CLIENT → Exclu
Solution : Attendre que le client génère des transactions
Articles saisonniers¶
Situation : Article avec peu de transactions sur 4Q
Comportement :
- MT_CAB_4Q faible
- Historique incomplet mais traité
Impact : Recommandations potentiellement moins fiables
Corridors manquants¶
Situation : Pas de corridor MASTER ni NATIONAL pour un article
Comportement :
- MATCH_TYPE = NO_MATCH
- HAS_CORRIDOR = 0
- Offre exclue du calcul de recommandations
Solution : Exécuter PT1CE pour générer les corridors manquants
Cohérence tarifaire¶
Garanties PT2QE :
- Cohérence verticale :
PRIX_RECOMMANDE >= NEW_PAS(toujours)-
PRIX_RECOMMANDE <= NEW_PRB(si capping PRB actif) -
Cohérence horizontale :
- Hausses alignées sur l'évolution des coûts (RECO2)
-
Repositionnement vers paliers supérieurs (RECO1)
-
Cohérence commerciale :
- Respect des cappings par sensibilité
- Protection des produits basiques
- Conservation des clients premium (CHEMIN 2)
Non garanti : - Cohérence inter-clients (même article peut avoir des hausses différentes) - Cohérence temporelle (run suivant peut recommander des prix différents si corridor change)
Limitations techniques¶
Volume maximal : - Dépend des ressources Oracle - Testé sur ~500k offres - Au-delà : risque de timeout
Temps d'exécution : - Variable selon le volume - Dépend du degré de parallélisation - Dépend de la charge serveur
Espace disque :
- Tables temporaires volumineuses
- CSV d'export peuvent être lourds
- Prévoir espace suffisant dans outputs/
Mémoire : - Python pandas peut nécessiter de la RAM pour les gros fichiers CSV - Recommandation : 8 GB RAM minimum