Files
AFC-Demo/STUFE_2_TRANSFORMATION_PLAN.md
T

11 KiB
Raw Blame History

Stufe 2: Transformation vom Data Pool in produktive Tabellen

Aktueller Stand (Stufe 1 )

backend.transactions (74 Transaktionen)
       +
backend.transaction_outputs (7,471 Outputs)
       ↓
       [SyncBackendDataPool Job]
       ↓
public.backend_data_pool (7,471 Datensätze)

Status: Datenpool ist befüllt und wird alle 6h aktualisiert


Ziel von Stufe 2

public.backend_data_pool (7,471 Datensätze)
       ↓
       [TransformDataPoolToProduction Job] ← NEU
       ↓
public.companies (Unique Companies mit KYC Risk Level)
       +
public.transactions (Transaktionen mit allen 139 Spalten befüllt)

🎯 Was Stufe 2 macht

1. Companies erstellen/aktualisieren

Aus dem Data Pool werden unique Companies extrahiert:

Input (Data Pool):

transaction_id | corporate_entity           | output_key        | content
---------------|----------------------------|-------------------|------------------
1              | Mercedes-Benz Group AG     | corporate_summary | {"answer": "..."}
1              | Mercedes-Benz Group AG     | corporate_sector  | {"answer": "..."}
2              | Samsung Electronics        | corporate_summary | {"answer": "..."}

Output (public.companies):

id | name                    | sector        | country | kyc_risk_level | summary
---|-------------------------|---------------|---------|----------------|----------
1  | Mercedes-Benz Group AG  | Automotive    | DE      | low            | German...
2  | Samsung Electronics     | Electronics   | KR      | high           | Korean...

Wie:

  • Gruppiere nach corporate_entity (eindeutige Firmennamen)
  • Extrahiere Company-Daten aus corporate_* outputs
  • Berechne KYC Risk Level basierend auf:
    • tranx_score
    • corporate_eusanctions, corporate_ofacsanctions
    • country_risk
    • corporate_pepexposure, corporate_AMLexposure
    • corruption_* outputs

2. Transactions erstellen/aktualisieren

Für jede Transaction im Data Pool wird ein Datensatz in public.transactions erstellt:

Input (Data Pool - gruppiert nach transaction_id):

transaction_id: 1
  - corporate_entity: "Mercedes-Benz Group AG"
  - tx_amount: 54880.9
  - tx_date: "2025-10-28"
  - outputs:
    - corporate_summary: {...}
    - corporate_history: {...}
    - tranx_score: {"score": 45}
    - ... (101 output_keys total)

Output (public.transactions):

id | company_id | reference        | amount   | executed_at | risk_score | corporate_summary | tranx_score | ... (139 Spalten)
---|------------|------------------|----------|-------------|------------|-------------------|-------------|----
1  | 1          | MIGRATED-1       | 54880.9  | 2025-10-28  | 45         | {"answer": "..."} | {"score":45}| ...

Mapping:

  • Core Felder:

    • company_id ← Lookup/Create Company by corporate_entity
    • reference'MIGRATED-' || transaction_id
    • amounttx_amount
    • currencytx_currency
    • executed_attx_date::timestamp
    • counterpartycorporate_counterparty
    • statusstatus
  • Output Felder (102 JSONB Spalten):

    • corporate_summary ← content WHERE output_key='corporate_summary'
    • corporate_history ← content WHERE output_key='corporate_history'
    • tranx_score ← content WHERE output_key='tranx_score'
    • ... für alle 102 output_keys

🔧 Technische Details

Job-Struktur

class TransformDataPoolToProduction implements ShouldQueue
{
    public function handle()
    {
        // 1. Hole alle unique transactions aus data pool
        $transactions = DB::table('backend_data_pool')
            ->select('transaction_id')
            ->distinct()
            ->get();

        foreach ($transactions as $transaction) {
            // 2. Hole alle Outputs für diese Transaction
            $outputs = $this->getOutputsForTransaction($transaction->transaction_id);

            // 3. Erstelle/Update Company
            $company = $this->createOrUpdateCompany($outputs);

            // 4. Erstelle/Update Transaction
            $this->createOrUpdateTransaction($company, $outputs);
        }
    }

    private function createOrUpdateCompany($outputs)
    {
        $corporateEntity = $outputs->first()->corporate_entity;

        // Berechne KYC Risk Level
        $kycRiskLevel = $this->calculateKycRiskLevel($outputs);

        return Company::updateOrCreate(
            ['name' => $corporateEntity],
            [
                'sector' => $this->extractSector($outputs),
                'country' => $this->extractCountry($outputs),
                'kyc_risk_level' => $kycRiskLevel,
                'summary' => $this->extractSummary($outputs),
                // ... weitere Felder
            ]
        );
    }

    private function createOrUpdateTransaction($company, $outputs)
    {
        $first = $outputs->first();

        // Core Felder
        $transactionData = [
            'company_id' => $company->id,
            'reference' => 'MIGRATED-' . $first->transaction_id,
            'amount' => $first->tx_amount,
            'currency' => $first->tx_currency,
            'executed_at' => Carbon::parse($first->tx_date),
            'status' => $first->status,
            // ...
        ];

        // Output Felder (102 JSONB columns)
        foreach ($outputs as $output) {
            $columnName = $output->output_key;
            $transactionData[$columnName] = json_decode($output->content, true);
        }

        return Transaction::updateOrCreate(
            ['reference' => $transactionData['reference']],
            $transactionData
        );
    }
}

📊 Beispiel-Transformation

Input: Data Pool Datensätze für Transaction ID=1

transaction_id=1, corporate_entity="Mercedes-Benz Group AG", tx_amount=54880.9
  output_key="corporate_summary"  → content={"answer": "..."}
  output_key="corporate_sector"   → content={"answer": "Automotive"}
  output_key="corporate_HQ"       → content={"answer": "Stuttgart, Germany"}
  output_key="tranx_score"        → content={"score": 45}
  output_key="corporate_eusanctions" → content={"found": false}
  ... (101 outputs total)

Output 1: Companies Tabelle

INSERT INTO public.companies (name, sector, headquarters, kyc_risk_level, summary)
VALUES (
  'Mercedes-Benz Group AG',
  'Automotive',
  'Stuttgart, Germany',
  'low',  -- Berechnet aus tranx_score=45, keine Sanctions, etc.
  '...'
);

Output 2: Transactions Tabelle

INSERT INTO public.transactions (
  company_id,
  reference,
  amount,
  currency,
  executed_at,
  status,
  corporate_summary,
  corporate_sector,
  tranx_score,
  corporate_eusanctions,
  ... -- alle 102 output columns
)
VALUES (
  1,  -- Company ID
  'MIGRATED-1',
  54880.9,
  'EUR',
  '2025-10-28',
  'done',
  '{"answer": "..."}',
  '{"answer": "Automotive"}',
  '{"score": 45}',
  '{"found": false}',
  ...
);

🎯 Features von Stufe 2

Intelligente Company-Erstellung

  • Deduplizierung: Gleicher Name = gleiche Company
  • Enrichment: Automatische Extraktion von Sector, HQ, etc.
  • KYC Risk Berechnung: Automatische Risikobewertung

Vollständige Transaction-Daten

  • Alle 139 Spalten werden befüllt
  • JSON-Daten aus Outputs werden korrekt gemappt
  • Referenz-Nummern für Tracking

Idempotent

  • Mehrfaches Ausführen ist sicher
  • updateOrCreate() verhindert Duplikate
  • Bestehende Daten werden aktualisiert

Batch Processing

  • Verarbeitet Daten in Batches
  • Kann in Queue laufen
  • Progress Tracking

🔄 Workflow nach Implementation

1. Backend Daten ändern sich
   ↓
2. SyncBackendDataPool läuft (alle 6h)
   → backend_data_pool aktualisiert
   ↓
3. TransformDataPoolToProduction läuft (nach Sync)
   → Companies aktualisiert
   → Transactions aktualisiert
   ↓
4. Frontend zeigt aktuelle Daten

Offene Fragen für Stufe 2

1. Update-Strategie

  • Sollen bestehende Transactions überschrieben werden?
  • Oder nur neue Transactions hinzufügen?

2. Company Matching

  • Nur exakter Name-Match?
  • Oder fuzzy matching (z.B. "BASF SE" vs "BASF")?

3. Risk Level Defaults

  • Was wenn keine Risk-Daten vorhanden?
  • Default zu "high" (vorsichtig) oder "low"?

4. Fehlende Felder

  • Was wenn ein output_key fehlt?
  • NULL speichern oder Default-Wert?

5. Scheduling

  • Soll Stufe 2 direkt nach Stufe 1 laufen?
  • Oder separater Schedule?

🔑 KYC Risk Level Berechnung

Verfügbare Risk-Daten

Aus transaction_outputs:

  • tranx_score - Haupt-Risiko-Score (JSON mit numerischem Wert)
  • tranx_reasoning - Begründung für den Score
  • country_risk - Länder-Risiko
  • sanctions_circumvention - Sanktionsumgehung
  • corruption_sector - Korruption im Sektor
  • corruption_country - Korruption im Land
  • corruption_relationship - Korruption in Beziehungen
  • corporate_eusanctions - EU Sanktionen
  • corporate_ofacsanctions - OFAC Sanktionen
  • corporate_uksanctions - UK Sanktionen
  • corporate_pepexposure - PEP Exposure
  • corporate_AMLexposure - AML Exposure
  • corporate_adverse - Adverse Media

Berechnungs-Logik (3 Risk Levels)

Risk Levels:

  1. low (Geringes Risiko): Score 0-40
  2. high (Hohes Risiko): Score 41-70
  3. critical (Kritisches Risiko): Score 71-100

Gewichtung:

  • Transaction Score: 40%
  • Sanctions: 25%
  • Country Risk: 15%
  • PEP/Adverse: 10%
  • Corruption: 10%

Beispiel:

Transaction Score: 45 × 0.40 = 18
Sanctions:        0  × 0.25 = 0
Country Risk:     30 × 0.15 = 4.5
PEP/Adverse:      0  × 0.10 = 0
Corruption:       0  × 0.10 = 0
--------------------------------
Total Score:                 22.5 → "low"

Company-Level Risk:

  • Aggregiert über alle Transaktionen einer Company
  • Worst-Case-Prinzip: Eine critical Transaction → Company ist critical
  • Wenn >30% high → Company ist critical
  • Wenn >10% high → Company ist high

📋 Implementierungs-Schritte

Phase 1: Vorbereitung

  1. Data Pool ist befüllt
  2. KYC Risk Calculator Service erstellen
  3. Data Extraction Helpers erstellen
  4. Mapping-Logik definieren

Phase 2: Job Implementation

  1. TransformDataPoolToProduction Job erstellen
  2. Company Creation Logic implementieren
  3. Transaction Creation Logic implementieren
  4. Error Handling & Logging

Phase 3: Testing

  1. Unit Tests für Risk Calculator
  2. Feature Tests für Transformation Job
  3. Datenintegritäts-Checks

Phase 4: Scheduling

  1. Schedule konfigurieren
  2. Queue Setup (optional)
  3. Monitoring einrichten

Phase 5: Deployment

  1. Produktions-Test mit echten Daten
  2. Performance-Optimierung
  3. Dokumentation finalisieren

📈 Erwartete Ergebnisse

Nach erfolgreicher Implementation von Stufe 2:

Companies:

  • ~60-70 unique Companies (geschätzt aus 74 Transaktionen)
  • Alle mit KYC Risk Level
  • Enriched mit Sector, Country, HQ, etc.

Transactions:

  • 74 Transaktionen
  • Alle 139 Spalten befüllt
  • Verlinkt mit Companies
  • Referenz-Nummern für Tracking

Performance:

  • Erste Transformation: ~30-60 Sekunden
  • Incremental Updates: ~5-10 Sekunden
  • Kann parallel zu Stufe 1 laufen

🚀 Nächste Schritte

  1. Beantworten der offenen Fragen
  2. Implementation des TransformDataPoolToProduction Jobs
  3. Tests schreiben und ausführen
  4. Scheduling einrichten
  5. Monitoring & Alerts konfigurieren

Erstellt am: 2025-11-16 Status: PLANUNG - Wartet auf Entscheidungen zu offenen Fragen