416 lines
10 KiB
Markdown
416 lines
10 KiB
Markdown
# Implementation Summary: Backend Schema Integration
|
|||
|
|
|
||
|
|
## ✅ Was wurde implementiert
|
||
|
|
|
||
|
|
### 1. Database Configuration
|
||
|
|
**Datei**: [config/database.php](config/database.php)
|
||
|
|
|
||
|
|
Zwei Verbindungen hinzugefügt:
|
||
|
|
- `pgsql` → public Schema (Default)
|
||
|
|
- `backend` → backend Schema (search_path: 'backend')
|
||
|
|
|
||
|
|
```php
|
||
|
|
'default' => env('DB_CONNECTION', 'pgsql'), // Geändert von sqlite zu pgsql
|
||
|
|
|
||
|
|
'pgsql' => [
|
||
|
|
'search_path' => 'public',
|
||
|
|
],
|
||
|
|
|
||
|
|
'backend' => [
|
||
|
|
'search_path' => 'backend', // Zugriff auf backend Schema
|
||
|
|
],
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Backend Models
|
||
|
|
|
||
|
|
#### Backend\Transaction Model
|
||
|
|
**Datei**: [app/Models/Backend/Transaction.php](app/Models/Backend/Transaction.php)
|
||
|
|
|
||
|
|
**Eigenschaften**:
|
||
|
|
- Connection: `backend`
|
||
|
|
- Table: `transactions`
|
||
|
|
- 14 Felder (Rohdaten aus CSV Upload)
|
||
|
|
|
||
|
|
**Relationships**:
|
||
|
|
- `outputs()` - HasMany zu TransactionOutput
|
||
|
|
|
||
|
|
**Helper Methods**:
|
||
|
|
```php
|
||
|
|
// Outputs abrufen
|
||
|
|
getOutput(string $key): ?array
|
||
|
|
getOutputsArray(): array
|
||
|
|
getCompanyInfo(): ?array
|
||
|
|
getRiskAssessment(): ?array
|
||
|
|
getSanctionsCheck(): ?array
|
||
|
|
getPepCheck(): ?array
|
||
|
|
|
||
|
|
// Status & Review
|
||
|
|
hasOutput(string $key): bool
|
||
|
|
isProcessed(): bool
|
||
|
|
requiresReview(): bool
|
||
|
|
|
||
|
|
// Display Helpers
|
||
|
|
getCompanyName(): string
|
||
|
|
getRiskScore(): int
|
||
|
|
getRiskLevel(): string
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Backend\TransactionOutput Model
|
||
|
|
**Datei**: [app/Models/Backend/TransactionOutput.php](app/Models/Backend/TransactionOutput.php)
|
||
|
|
|
||
|
|
**Eigenschaften**:
|
||
|
|
- Connection: `backend`
|
||
|
|
- Table: `transaction_outputs`
|
||
|
|
- 5 Felder (KI-generierte Outputs)
|
||
|
|
|
||
|
|
**Relationships**:
|
||
|
|
- `transaction()` - BelongsTo Transaction
|
||
|
|
|
||
|
|
**Konstanten** (Output-Keys):
|
||
|
|
```php
|
||
|
|
KEY_COMPANY_INFO = 'company_info'
|
||
|
|
KEY_RISK_ASSESSMENT = 'risk_assessment'
|
||
|
|
KEY_SANCTIONS = 'sanctions'
|
||
|
|
KEY_PEP = 'pep'
|
||
|
|
KEY_REGISTRY = 'registry'
|
||
|
|
KEY_GLEIF = 'gleif'
|
||
|
|
KEY_INSOLVENCY = 'insolvency'
|
||
|
|
KEY_BUNDESANZEIGER = 'bundesanzeiger'
|
||
|
|
KEY_RSS = 'rss'
|
||
|
|
KEY_EU_SANCTIONS = 'eu_sanctions'
|
||
|
|
KEY_HANDELSREGISTER = 'handelsregister'
|
||
|
|
KEY_GENESIS = 'genesis'
|
||
|
|
KEY_GOVDATA = 'govdata'
|
||
|
|
```
|
||
|
|
|
||
|
|
**Helper Methods**:
|
||
|
|
```php
|
||
|
|
availableKeys(): array
|
||
|
|
getKeyLabel(string $key): string
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Repository Layer
|
||
|
|
|
||
|
|
**Datei**: [app/Repositories/TransactionRepository.php](app/Repositories/TransactionRepository.php)
|
||
|
|
|
||
|
|
**Methoden**:
|
||
|
|
```php
|
||
|
|
// Basic CRUD
|
||
|
|
all(): Collection
|
||
|
|
find(int $id): ?Transaction
|
||
|
|
paginated(int $perPage = 20)
|
||
|
|
|
||
|
|
// Filtering
|
||
|
|
requiresReview(): Collection
|
||
|
|
highRisk(int $threshold = 70): Collection
|
||
|
|
byCompany(string $companyName): Collection
|
||
|
|
byStatus(string $status): Collection
|
||
|
|
byDateRange(string $startDate, string $endDate): Collection
|
||
|
|
search(string $query): Collection
|
||
|
|
|
||
|
|
// Status-specific
|
||
|
|
pending(): Collection
|
||
|
|
processing(): Collection
|
||
|
|
completed(): Collection
|
||
|
|
failed(): Collection
|
||
|
|
|
||
|
|
// Statistics
|
||
|
|
stats(): array
|
||
|
|
dashboardData(): array
|
||
|
|
getAllCompanies(): Collection
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Tests
|
||
|
|
|
||
|
|
**Datei**: [tests/Feature/BackendModelsTest.php](tests/Feature/BackendModelsTest.php)
|
||
|
|
|
||
|
|
11 Tests geschrieben (3 laufen ohne DB-Verbindung):
|
||
|
|
- ✅ Connection Tests
|
||
|
|
- ✅ Model Relationship Tests
|
||
|
|
- ✅ Helper Method Tests
|
||
|
|
- ✅ Constant Tests
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 Nächste Schritte: Component-Migration
|
||
|
|
|
||
|
|
### Betroffene Components
|
||
|
|
|
||
|
|
#### 1. transaction-review.blade.php
|
||
|
|
**Aktuell**: Verwendet `App\Models\Transaction` und `App\Models\Company`
|
||
|
|
|
||
|
|
**Änderungen**:
|
||
|
|
```php
|
||
|
|
// Alt
|
||
|
|
use App\Models\Transaction;
|
||
|
|
use App\Models\Company;
|
||
|
|
|
||
|
|
// Neu
|
||
|
|
use App\Models\Backend\Transaction;
|
||
|
|
use App\Repositories\TransactionRepository;
|
||
|
|
|
||
|
|
// Repository verwenden statt direkte Model-Queries
|
||
|
|
public function __construct(
|
||
|
|
private TransactionRepository $transactions
|
||
|
|
) {}
|
||
|
|
|
||
|
|
$transactions = $this->transactions->all();
|
||
|
|
```
|
||
|
|
|
||
|
|
**Mapping**:
|
||
|
|
| Alt (public.transactions) | Neu (backend) |
|
||
|
|
|---------------------------|---------------|
|
||
|
|
| `$transaction->company->name` | `$transaction->getCompanyName()` |
|
||
|
|
| `$transaction->company->legal_name` | `$transaction->getCompanyInfo()['legal_name']` |
|
||
|
|
| `$transaction->company->sector` | `$transaction->getCompanyInfo()['sector']` |
|
||
|
|
| `$transaction->risk_score` | `$transaction->getRiskScore()` |
|
||
|
|
| `$transaction->requires_review` | `$transaction->requiresReview()` |
|
||
|
|
| `$transaction->amount` | `$transaction->tx_amount` |
|
||
|
|
| `$transaction->counterparty` | `$transaction->corporate_counterparty` |
|
||
|
|
| `$transaction->executed_at` | `$transaction->tx_date` (⚠️ ist text) |
|
||
|
|
|
||
|
|
#### 2. companies/transactions.blade.php
|
||
|
|
**Aktuell**: Verwendet `App\Models\Company` Parameter
|
||
|
|
|
||
|
|
**Änderungen**:
|
||
|
|
- Company-Daten kommen jetzt aus `transaction_outputs`
|
||
|
|
- Grouping nach `corporate_entity` statt `company_id`
|
||
|
|
|
||
|
|
**Neuer Ansatz**:
|
||
|
|
```php
|
||
|
|
// Alle Transaktionen für eine Firma
|
||
|
|
$companyName = 'Siemens AG'; // Aus Route oder Parameter
|
||
|
|
$transactions = $this->transactions->byCompany($companyName);
|
||
|
|
|
||
|
|
// Company-Info aus erster Transaction
|
||
|
|
$companyInfo = $transactions->first()?->getCompanyInfo();
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 3. company-search.blade.php
|
||
|
|
**Änderungen**:
|
||
|
|
- Suche jetzt in `backend.transactions.corporate_entity`
|
||
|
|
- Keine separate Company-Tabelle mehr
|
||
|
|
|
||
|
|
```php
|
||
|
|
// Repository Method
|
||
|
|
public function getAllCompanies(): Collection
|
||
|
|
{
|
||
|
|
return DB::connection('backend')
|
||
|
|
->table('transactions')
|
||
|
|
->select('corporate_entity as name')
|
||
|
|
->distinct()
|
||
|
|
->orderBy('corporate_entity')
|
||
|
|
->get();
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔄 Migrationsplan (Step-by-Step)
|
||
|
|
|
||
|
|
### Phase 1: Testen ohne Breaking Changes (empfohlen)
|
||
|
|
|
||
|
|
**Option A: Feature Flag**
|
||
|
|
```php
|
||
|
|
// config/features.php
|
||
|
|
'use_backend_schema' => env('FEATURE_USE_BACKEND_SCHEMA', false),
|
||
|
|
|
||
|
|
// In Components
|
||
|
|
if (config('features.use_backend_schema')) {
|
||
|
|
$transactions = app(TransactionRepository::class)->all();
|
||
|
|
} else {
|
||
|
|
$transactions = Transaction::all(); // Alt
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Option B: Neue Routes** (empfohlen für parallele Tests)
|
||
|
|
```php
|
||
|
|
// routes/web.php
|
||
|
|
Route::get('/beta/transactions', ...); // Nutzt Backend-Schema
|
||
|
|
Route::get('/transactions', ...); // Alte Implementation
|
||
|
|
```
|
||
|
|
|
||
|
|
### Phase 2: Direkte Migration (schneller, aber riskanter)
|
||
|
|
|
||
|
|
1. **Alle `use App\Models\Transaction` ersetzen**:
|
||
|
|
```bash
|
||
|
|
find resources/views/livewire -type f -name "*.php" -exec sed -i '' 's/use App\\Models\\Transaction/use App\\Models\\Backend\\Transaction/g' {} +
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Alle `use App\Models\Company` entfernen**:
|
||
|
|
```bash
|
||
|
|
find resources/views/livewire -type f -name "*.php" -exec sed -i '' 's/use App\\Models\\Company;//g' {} +
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Component für Component anpassen**:
|
||
|
|
- transaction-review.blade.php
|
||
|
|
- companies/transactions.blade.php
|
||
|
|
- company-search.blade.php
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🗺️ Daten-Mapping Referenz
|
||
|
|
|
||
|
|
### Transaction Fields
|
||
|
|
|
||
|
|
| public.transactions | backend.transactions | Typ-Unterschied |
|
||
|
|
|---------------------|----------------------|-----------------|
|
||
|
|
| id | id | bigint → integer |
|
||
|
|
| company_id | - (via corporate_entity) | Relationship entfällt |
|
||
|
|
| reference | - | Neu: auto-generated |
|
||
|
|
| amount | tx_amount | numeric → double |
|
||
|
|
| currency | tx_currency | ✓ |
|
||
|
|
| counterparty | corporate_counterparty | ✓ |
|
||
|
|
| counterparty_country | tx_country_incoming | ✓ |
|
||
|
|
| channel | - | Nicht in backend |
|
||
|
|
| executed_at | tx_date | **timestamp → text!** |
|
||
|
|
| risk_score | - (in outputs) | Via getRiskScore() |
|
||
|
|
| status | status | ✓ |
|
||
|
|
| requires_review | - (computed) | Via requiresReview() |
|
||
|
|
| flagged_by | - | Nicht in backend |
|
||
|
|
| flagged_reason | tx_purpose | Ähnlich |
|
||
|
|
| signals | - | Nicht in backend |
|
||
|
|
|
||
|
|
### Company Fields (jetzt in transaction_outputs)
|
||
|
|
|
||
|
|
| public.companies | transaction_outputs (key='company_info') |
|
||
|
|
|------------------|-------------------------------------------|
|
||
|
|
| name | content['name'] |
|
||
|
|
| legal_name | content['legal_name'] |
|
||
|
|
| ticker | content['ticker'] |
|
||
|
|
| sector | content['sector'] |
|
||
|
|
| country | content['country'] |
|
||
|
|
| headquarters | content['headquarters'] |
|
||
|
|
| kyc_risk_level | content['kyc_risk_level'] |
|
||
|
|
| summary | content['summary'] |
|
||
|
|
|
||
|
|
### Enrichment Fields (in transaction_outputs)
|
||
|
|
|
||
|
|
Alle 35 Enrichment-Felder aus `public.transactions` sind jetzt separate Outputs:
|
||
|
|
|
||
|
|
```php
|
||
|
|
// Alt
|
||
|
|
$transaction->sanctions_data // JSON
|
||
|
|
|
||
|
|
// Neu
|
||
|
|
$transaction->getOutput('sanctions') // Array
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ⚠️ Breaking Changes
|
||
|
|
|
||
|
|
### 1. Timestamps sind Text
|
||
|
|
```php
|
||
|
|
// Alt
|
||
|
|
$transaction->executed_at->format('d.m.Y')
|
||
|
|
|
||
|
|
// Neu (ACHTUNG: tx_date ist Text!)
|
||
|
|
$transaction->tx_date // Already string, no format()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Empfehlung**: Migration hinzufügen um `tx_date` von `text` zu `timestamp` zu ändern.
|
||
|
|
|
||
|
|
### 2. Company Relationship entfällt
|
||
|
|
```php
|
||
|
|
// Alt
|
||
|
|
$transaction->company->name
|
||
|
|
$transaction->company()->where(...)
|
||
|
|
|
||
|
|
// Neu
|
||
|
|
$transaction->getCompanyName()
|
||
|
|
$transaction->getCompanyInfo()['name']
|
||
|
|
// Kein Relationship mehr verfügbar
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Feld-Namen ändern sich
|
||
|
|
```php
|
||
|
|
// Alt → Neu
|
||
|
|
amount → tx_amount
|
||
|
|
counterparty → corporate_counterparty
|
||
|
|
executed_at → tx_date
|
||
|
|
```
|
||
|
|
|
||
|
|
**Empfehlung**: Accessor in Model für Backward-Compatibility:
|
||
|
|
|
||
|
|
```php
|
||
|
|
// In Backend\Transaction Model
|
||
|
|
protected $appends = ['amount', 'counterparty', 'executed_at'];
|
||
|
|
|
||
|
|
public function getAmountAttribute(): float
|
||
|
|
{
|
||
|
|
return $this->tx_amount;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getCounterpartyAttribute(): string
|
||
|
|
{
|
||
|
|
return $this->corporate_counterparty;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getExecutedAtAttribute(): string
|
||
|
|
{
|
||
|
|
return $this->tx_date;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🎯 Empfohlenes Vorgehen
|
||
|
|
|
||
|
|
### Option 1: Schrittweise mit Feature Flags (Sicher, 2-3 Wochen)
|
||
|
|
1. ✅ Backend Models & Repository erstellt
|
||
|
|
2. ⬜ Feature Flag System einrichten
|
||
|
|
3. ⬜ Parallele Routes `/beta/*` erstellen
|
||
|
|
4. ⬜ Einen Component nach dem anderen migrieren
|
||
|
|
5. ⬜ Testen mit echten Nutzern (10%)
|
||
|
|
6. ⬜ Gradual Rollout
|
||
|
|
7. ⬜ Alte Components entfernen
|
||
|
|
|
||
|
|
### Option 2: Direkte Migration (Schnell, 3-5 Tage)
|
||
|
|
1. ✅ Backend Models & Repository erstellt
|
||
|
|
2. ⬜ Alle Components in einem PR umstellen
|
||
|
|
3. ⬜ Accessor für Backward-Compatibility hinzufügen
|
||
|
|
4. ⬜ Intensives Testing
|
||
|
|
5. ⬜ Deploy mit Rollback-Plan
|
||
|
|
|
||
|
|
### Option 3: Hybrid (Empfohlen, 1 Woche)
|
||
|
|
1. ✅ Backend Models & Repository erstellt
|
||
|
|
2. ⬜ Accessor für Backward-Compatibility in Backend Models
|
||
|
|
3. ⬜ `transaction-review` Component migrieren (wichtigster)
|
||
|
|
4. ⬜ 1-2 Tage Testing
|
||
|
|
5. ⬜ Restliche Components migrieren
|
||
|
|
6. ⬜ Deploy
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📝 Checkliste für Component-Migration
|
||
|
|
|
||
|
|
Für jeden Component:
|
||
|
|
|
||
|
|
- [ ] Import `App\Models\Transaction` → `App\Models\Backend\Transaction`
|
||
|
|
- [ ] Import `App\Models\Company` entfernen
|
||
|
|
- [ ] Repository injecten statt direkte Model-Queries
|
||
|
|
- [ ] `company->` zu `getCompanyInfo()` ändern
|
||
|
|
- [ ] Field-Namen anpassen (`amount` → `tx_amount`, etc.)
|
||
|
|
- [ ] `executed_at` zu `tx_date` ändern
|
||
|
|
- [ ] `->format()` Calls bei `tx_date` entfernen (ist schon Text)
|
||
|
|
- [ ] Tests schreiben/anpassen
|
||
|
|
- [ ] Manuell testen
|
||
|
|
- [ ] PR erstellen
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 Los geht's!
|
||
|
|
|
||
|
|
Möchten Sie:
|
||
|
|
|
||
|
|
**A)** Dass ich jetzt `transaction-review.blade.php` auf Backend-Schema umstelle?
|
||
|
|
|
||
|
|
**B)** Erst Accessors für Backward-Compatibility hinzufügen?
|
||
|
|
|
||
|
|
**C)** Ein Feature-Flag-System einrichten?
|
||
|
|
|
||
|
|
**D)** Etwas anderes?
|
||
|
|
|
||
|
|
Was ist Ihr bevorzugter Ansatz?
|