Aller au contenu

Architecture technique - PT1CE

Table des matières

  1. Structure modulaire
  2. Flux de données
  3. Gestion des tables Oracle
  4. Stratégies d'optimisation
  5. Configuration et paramétrage
  6. 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

  1. Existence des tables sources
  2. Validation format CSV
  3. Cohérence des bornes après calcul
  4. Présence table mapping pour export SAP
  5. Espace disque pour exports Excel