Architecture technique - PT1CE¶
Table des matières¶
- Structure modulaire
- Flux de données
- Gestion des tables Oracle
- Stratégies d'optimisation
- Configuration et paramétrage
- Gestion des erreurs
Structure modulaire¶
Organisation des modules¶
pt1ce_main.py
├── apply_new_prices.py # Application nouveaux PAS/PRB
├── detect_suboptimal.py # Détection corridors sous-optimaux
├── apply_corrections.py # Application corrections manuelles
├── export_sap_format.py # Export format SAP Excel
├── generate_kpi_reports.py # Génération rapports KPI
└── config/
└── pt1ce_config.py # Configuration centralisée
└── utils/
├── corridor_calculator.py # Logique de calcul
├── quality_metrics.py # Métriques qualité
├── excel_formatter.py # Formatage Excel SAP
└── period_manager.py # Gestion périodes fiscales
Responsabilités des modules¶
pt1ce_main.py¶
- Orchestration du workflow global
- Gestion des arguments ligne de commande
- Coordination des phases d'exécution
- Gestion des modes (nouveau run, corrections, export seul)
apply_new_prices.py¶
- Chargement du fichier CSV des nouveaux prix
- Application des écarts historiques
- Calcul des nouvelles bornes avec contraintes
- Création des tables
PT1CE_CORRIDORS_*
detect_suboptimal.py¶
- Identification des corridors sous-optimaux
- Marquage du statut (OPTIMAL/SUBOPTIMAL)
- Export des fichiers CSV d'analyse
- Création des tables
PT1CE_OPTIMAL_*
apply_corrections.py¶
- Chargement des corrections manuelles
- Update ciblé des corridors
- Validation de la cohérence
- Traçabilité des modifications
export_sap_format.py¶
- Recherche de
PT0CE_DIMENSION_MAPPING
- Génération des 4 extracts séparés
- Création des fichiers Excel au format SAP
- Gestion de la volumétrie (400k lignes max)
generate_kpi_reports.py¶
- Calcul des évolutions par dimension
- Analyse de la distribution des écarts-types
- Génération des métriques de qualité
- Export des rapports CSV
Modules utilitaires¶
corridor_calculator.py¶
def calculate_new_borne(new_pas, ecart, prb_value):
# Calcul de base
new_borne = new_pas + ecart
# Contrainte minimale
if new_borne < new_pas:
new_borne = new_pas
# Contrainte maximale
if prb_value and new_borne > prb_value:
new_borne = prb_value
return new_borne
quality_metrics.py¶
- Génération des métriques par dimension
- Calcul des pourcentages par niveau source
- Analyse de la distribution des écarts-types
- Agrégation des statistiques globales
excel_formatter.py¶
- Formatage spécifique SAP (en-têtes ligne 18)
- Gestion des styles et colonnes
- Division automatique des gros fichiers
- Validation du format généré
Flux de données¶
Vue d'ensemble¶
graph TD
A[new_pas_prb.csv] -->|Étape 1| B[Application prix]
B --> C[PT1CE_CORRIDORS_*]
C --> D[Détection sous-optimaux]
D --> E[PT1CE_OPTIMAL_*]
F[corrections_ZOOM*.csv] -->|Étape 2| G[Application corrections]
G --> C
E -->|Étape 3| H[Export SAP]
H --> I[Fichiers Excel]
J[PT0CE_DIMENSION_MAPPING] -->|Codes SAP| H
Phase 1 : Application des nouveaux prix¶
-- Pour chaque corridor PT0CE
SELECT
c.*,
-- Nouveaux prix ou anciens si pas de mise à jour
COALESCE(np.NEW_PAS, c.PAS_ACTIF) as NEW_PAS,
COALESCE(np.NEW_PRB_RC, c.PRB_RC_ACTIF) as NEW_PRB_RC,
COALESCE(np.NEW_PRB_COLL, c.PRB_COLL_ACTIF) as NEW_PRB_COLL,
-- Recalcul des bornes avec contraintes
CASE
WHEN np.ID_ART IS NOT NULL THEN
CASE
WHEN np.NEW_PAS + c.ECART_PL1_PL2_PAS < np.NEW_PAS
THEN np.NEW_PAS
WHEN c.PRB_TO_USE = 1
AND np.NEW_PAS + c.ECART_PL1_PL2_PAS > np.NEW_PRB_RC
THEN np.NEW_PRB_RC
WHEN c.PRB_TO_USE = 2
AND np.NEW_PAS + c.ECART_PL1_PL2_PAS > np.NEW_PRB_COLL
THEN np.NEW_PRB_COLL
ELSE np.NEW_PAS + c.ECART_PL1_PL2_PAS
END
ELSE c.BORNE_PL1_PL2
END as NEW_BORNE_PL1_PL2
FROM PT0CE_CORRIDOR_HISTORY_ZOOM1 c
LEFT JOIN TEMP_NEW_PRICES np ON c.ID_ART = np.ID_ART
Phase 2 : Détection et marquage¶
-- Marquage du statut
CASE
-- Sous-optimal si PL6 = PAS
WHEN s.NEW_BORNE_PL6_PLX = s.NEW_PAS THEN 'SUBOPTIMAL'
-- Optimal même avec écart-type élevé
WHEN s.ECART_TYPE > 0.10 THEN 'OPTIMAL'
ELSE 'OPTIMAL'
END as STATUS,
-- Type de problème détaillé
CASE
WHEN s.NEW_BORNE_PL6_PLX = s.NEW_PAS
AND s.ECART_TYPE > 0.10 THEN 'PL6_ET_ECART_TYPE'
WHEN s.NEW_BORNE_PL6_PLX = s.NEW_PAS THEN 'PL6_EGAL_PAS'
WHEN s.ECART_TYPE > 0.10 THEN 'ECART_TYPE_ELEVE'
ELSE 'AUCUN'
END as PROBLEM_TYPE
Phase 3 : Export SAP¶
# Transformation au format SAP
for corridor in corridors:
type_tarif = f"{int(corridor['PRB_TO_USE']):02d}"
prb_used = corridor['NEW_PRB_RC'] if corridor['PRB_TO_USE'] == 1
else corridor['NEW_PRB_COLL']
for code_tarif_sap, borne_col in tarif_mapping.items():
taux_remise = round((prb_used - borne_value) / prb_used, 2)
row = {
'code_sequence': 526,
'paliers': code_tarif_sap, # ZRPL, ZP05, etc.
'Type tarif': type_tarif, # '01' ou '02'
'Montant': taux_remise,
'debut': next_quarter_start,
'fin': '31.12.9999'
}
Gestion des tables Oracle¶
Tables permanentes¶
-- Tables écrasées à chaque run
PT1CE_CORRIDORS_ZOOM1
PT1CE_CORRIDORS_ZOOM2
PT1CE_CORRIDORS_ZOOM3
PT1CE_OPTIMAL_ZOOM1
PT1CE_OPTIMAL_ZOOM2
PT1CE_OPTIMAL_ZOOM3
Caractéristiques :
- COMPRESS NOLOGGING
pour performance
- Pas d'horodatage dans le nom
- Écrasement systématique
- Index sur colonnes clés
Tables temporaires¶
-- Utilisées pendant le traitement
TEMP_NEW_PRICES_ZOOM1
TEMP_CORRECTIONS_ZOOM1
-- Supprimées automatiquement après usage
Index créés¶
-- Sur les tables CORRIDORS
CREATE INDEX IX_PT1CE_CORRIDORS_ART
ON PT1CE_CORRIDORS_ZOOM1 (ID_ART) COMPRESS;
CREATE INDEX IX_PT1CE_CORRIDORS_UPDATE
ON PT1CE_CORRIDORS_ZOOM1 (IS_UPDATED) COMPRESS;
-- Sur les tables OPTIMAL
CREATE INDEX IX_PT1CE_OPTIMAL_STATUS
ON PT1CE_OPTIMAL_ZOOM1 (STATUS) COMPRESS;
CREATE INDEX IX_PT1CE_OPTIMAL_PROBLEM
ON PT1CE_OPTIMAL_ZOOM1 (PROBLEM_TYPE) COMPRESS;
Stratégies d'optimisation¶
Performance des requêtes¶
Utilisation des CASE optimisés¶
-- Éviter les sous-requêtes répétées
CASE
WHEN condition1 THEN
CASE
WHEN sous_condition1 THEN valeur1
WHEN sous_condition2 THEN valeur2
ELSE valeur3
END
ELSE valeur_defaut
END
Create Table As Select (CTAS)¶
CREATE TABLE table_finale COMPRESS NOLOGGING AS
SELECT /*+ PARALLEL(8) */
-- Calculs complexes en une passe
FROM source_table
Gestion mémoire¶
Traitement par univers¶
- Chaque ZOOM traité séparément
- Libération mémoire entre chaque traitement
- Commit régulier pour éviter l'accumulation
Optimisation des updates¶
# Updates par batch plutôt que ligne par ligne
UPDATE table
SET colonne = (
SELECT valeur
FROM corrections
WHERE condition
)
WHERE EXISTS (
SELECT 1
FROM corrections
WHERE condition
)
Export Excel optimisé¶
# Division automatique des gros volumes
total_files = (len(data) - 1) // max_rows_per_file + 1
for file_idx in range(total_files):
start_idx = file_idx * max_rows_per_file
end_idx = min((file_idx + 1) * max_rows_per_file, len(data))
chunk_data = data.iloc[start_idx:end_idx]
create_sap_excel(chunk_data, f'part{file_idx + 1:02d}.xlsx')
Configuration et paramétrage¶
Fichier pt1ce_config.json¶
{
"processing": {
"ecart_type_threshold": 0.10,
"max_rows_per_excel": 400000,
"batch_size": 10000
},
"sap_export": {
"code_sequence": 526,
"orga": 1000,
"canal": 10,
"date_format": "%d.%m.%Y",
"end_date": "31.12.9999"
},
"quality_thresholds": {
"std_levels": [0.02, 0.05, 0.07, 0.10, 0.15, 0.20]
},
"output_formats": {
"csv_separator": ";",
"csv_encoding": "cp1252",
"decimal_places": 3
}
}
Surcharge par arguments¶
# Arguments ligne de commande prioritaires
parser.add_argument('--ecart-type-threshold',
type=float,
default=0.10)
parser.add_argument('--skip-price-update',
action='store_true')
parser.add_argument('--export-sap-only',
action='store_true')
Gestion des erreurs¶
Validation des entrées¶
# Vérification du CSV
required_cols = ['ID_ART', 'PAS', 'PRB_RC', 'PRB_COLL']
missing = set(required_cols) - set(df.columns)
if missing:
raise ValueError(f"Colonnes manquantes : {missing}")
# Validation des valeurs
if pd.isna(row['PAS']) or row['PAS'] <= 0:
raise ValueError(f"PAS invalide pour {row['ID_ART']}")
Gestion des cas limites¶
# Protection division par zéro
if prb_value == 0:
taux_remise = 0
else:
taux_remise = (prb_value - borne) / prb_value
# Gestion des NULL
borne = corridor.get(borne_col)
if pd.isna(borne):
continue # Skip cette borne
Logging structuré¶
# Niveaux de log configurables
if app.args.quiet:
log_level = logging.WARNING
elif app.args.very_quiet:
log_level = logging.ERROR
else:
log_level = logging.INFO
# Messages structurés
app.log.info(f"Phase 1: Application des nouveaux prix")
app.log.info(f" → {len(new_prices)} articles avec nouveaux prix")
app.log.warning(f" ⚠ {incoherent_count} corridors incohérents")
Points de contrôle¶
- Existence des tables sources
- Validation format CSV
- Cohérence des bornes après calcul
- Présence table mapping pour export SAP
- Espace disque pour exports Excel