# `global/` — versionsunabhängige Datenbestände

Dieser Ordner liegt auf DocumentRoot-Ebene, also **neben `.private/` und eine Ebene über `v1/`**.
Was hier liegt, gehört keiner API-Version: `v1`, `v2` und alle künftigen lesen dieselben Dateien.

Der Ordner ist **nie per HTTP erreichbar** (`.htaccess`, `Require all denied` → DWS-803 / HTTP 451).
Der Zugriff erfolgt ausschließlich serverseitig über das Dateisystem.

```
global/
└── epc/
    ├── participants_schemes_list.json     ← Laufzeitdatei, nicht in Git
    └── participants_bic_bankname.csv      ← Komfort-Export derselben Liste, ebenfalls nicht in Git
```

## Zugriff aus dem Code

Alles läuft über die Registry `Config::GLOBAL_DATASETS`. Ein neuer Datenbestand ist damit ein
weiterer Eintrag dort plus ein Builder — weder `GlobalDataStore` noch `GlobalDataTrait` müssen
dafür angefasst werden.

| Schlüssel | Unterordner | Datei | Höchstalter |
|---|---|---|---|
| `epcParticipants` | `epc/` | `participants_schemes_list.json` | 3 Tage |

Gelesen wird im Request über `GlobalDataTrait` (im Kernel eingehängt), **beim ersten Zugriff**:

```php
$bankDataArray = $this->getEpcParticipantsArray();
$bankArray     = $this->getEpcBankArrayByBic( "PBNKDEFFXXX" );   // null = keine Aussage möglich
```

Die EPC-Liste ist rund 1,6 MB. Sie in der Setup-Prozedur zu laden würde jeden Request rund 23 ms
und rund 20 MB kosten, auch die, die nie einen BIC ansehen — deshalb lazy.

## `epc/` — Teilnehmerlisten des European Payments Council

Das EPC veröffentlicht je Verfahren eine XML-Liste und aktualisiert sie **einmal täglich**:

| Verfahren | Scheme-Schlüssel | Quelle |
|---|---|---|
| SEPA-Überweisung | `sct` | `participants_export/sct/sct.xml` |
| SEPA-Echtzeitüberweisung | `sctInst` | `participants_export/sct_inst/sct_inst.xml` |
| Verification of Payee | `vopRequest` / `vopResponse` | `participants_export/vop/vop.xml` |
| SEPA-Lastschrift CORE | `sddCore` | `participants_export/sdd_core/sdd_core.xml` |
| SEPA-Lastschrift B2B | `sddB2b` | `participants_export/sdd_b2b/sdd_b2b.xml` |
| One-Leg-Out Instant Credit Transfer (Auslandsüberweisung, nicht SEPA) | `octPayerPsp` / `octPayeePsp` / `octProcessor` / `octEntryPsp` / `octExitPsp` | `participants_export/oct_inst/oct_inst.xml` |

`EpcParticipantsListBuilder` führt alle sechs über den BIC zusammen (rund 3.550 Banken, `octInst`
allein davon nur rund 100 — jung und absichtlich klein, deshalb eigene, niedrigere
Plausibilitätsschwelle in `Config::EPC_MIN_BYTE_SIZE_PER_SOURCE_OVERRIDE_ARRAY` /
`_BANK_COUNT_PER_SOURCE_OVERRIDE_ARRAY`) und schreibt eine Datei mit `bankData` und
`unixTimestamp`. `available` je Verfahren heißt **heute wirklich teilnehmend**: Bereitschaftsdatum
vorhanden und erreicht, Austrittsdatum leer oder noch in der Zukunft; bei VOP zusätzlich die
passende Rolle und `Status = "Ready for operations"`; bei `octInst` zusätzlich die passende Rolle
(`<Roles><Role>`) — anders als VOP führt `octInst` aber kein `<Status>`, entsprechend fehlt der
`status`-Schlüssel bei den fünf `oct*`-Schemata ganz (wie bei `sct`/`sctInst`/`sddCore`/`sddB2b`).

**Alles oder nichts:** scheitert eine der sechs Quellen, wird gar nichts geschrieben und die
vorhandene Datei bleibt unverändert stehen. Eine Datei mit einem Verfahren von gestern und anderen
von heute wäre von außen nicht erkennbar — es gibt nur einen `unixTimestamp`.

**Kein BIC wird je aus der Datei entfernt.** Der EPC-Server selbst ist tagesweise nicht stabil — ein
und dieselbe Bank kann an einem Tag im Export fehlen und am nächsten wieder auftauchen, ohne dass
sich am Teilnahmestatus real etwas geändert hat (beobachtet über Cache-Effekte der Export-URL: der
Cache-Buster-Parameter `v` liefert je nachdem, ob er schon einmal abgerufen wurde, einen älteren oder
den aktuellen Serverstand). Jeder Bankdatensatz trägt deshalb zusätzlich:

| Feld | Bedeutung |
|---|---|
| `presenceStatus` | `new` (heute (wieder) gesehen, vorher nicht oder als `removed` vermerkt), `active` (heute gesehen, war es beim letzten Lauf schon), `removed` (heute in keiner der sechs Quellen mehr gefunden) |
| `lastSeenDate` | Tag des letzten Laufs, in dem die Bank tatsächlich in einer Quelle stand — bei `removed` bleibt das der Tag, an dem sie zuletzt gesehen wurde, nicht der heutige Bautag |

Ein `removed`-Datensatz trägt `bankName`/`address`/`schemes`/… unverändert vom letzten Mal, an dem
er gesehen wurde (eingefroren, keine neuen Daten vorhanden). Kommt der BIC später wieder in einer
Quelle vor, wird er mit frischen Daten neu aufgebaut und als `new` markiert — nicht als `active`,
weil er zwischenzeitlich als fehlend galt.

## `participants_bic_bankname.csv` — Komfort-Export

Bei jedem erfolgreichen Lauf schreibt `EpcParticipantsListBuilder` zusätzlich eine flache CSV
(`bic,bankName,presenceStatus,lastSeenDate`) in denselben Ordner — für den externen bicEpc-Abgleich
des Nutzers (eigenes Tool, nicht Teil von DWS), der eine flache Liste statt der verschachtelten
`schemes` braucht. Kein `Config::GLOBAL_DATASETS`-Eintrag und keiner der Verträge oben gilt dafür:
die Datei wird nie über `GlobalDataTrait` zurückgelesen. Ein Fehlschlag beim Schreiben dieser Datei
lässt den Lauf nicht insgesamt scheitern — die JSON-Datei ist zu dem Zeitpunkt schon geschrieben —,
steht aber in der Ergebnis-Message (`EpcParticipantsListResult::getMessage()`), auch ohne `-v`.

## Aktualisieren

```bash
php /var/www/html/dws/develop/v1/globalupdate.php --all -v
php /var/www/html/dws/develop/v1/globalupdate.php --dataset=epcParticipants
php /var/www/html/dws/develop/v1/globalupdate.php --status     # nur anzeigen, nichts ändern
```

Exit-Codes: `0` in Ordnung (auch „läuft bereits"), `1` nichts geschrieben, `2` Aufruffehler.
Parallele Läufe sind über eine `flock`-Sperre je Datensatz ausgeschlossen.

### Cronjob

Täglich, versetzt gegen den EPC-Rhythmus, **je Umgebung eine Zeile**:

```cron
17 4 * * * www-data /usr/bin/php /var/www/html/dws/develop/v1/globalupdate.php --all >> /var/log/dws-globalupdate.log 2>&1
37 4 * * * www-data /usr/bin/php /var/www/html/dws/productive/v1/globalupdate.php --all >> /var/log/dws-globalupdate.log 2>&1
```

DEV und PROD halten bewusst je eine eigene Kopie — wie überall ergibt sich die Umgebung allein aus
dem Ablageort. Ein systemd-Timer (analog `batch/systemd/`) wäre die Alternative, für einen Lauf pro
Tag aber mehr Apparat als nötig.

### Nach einem frischen Deploy

Die JSON-Datei ist nicht in Git — sie entsteht erst beim ersten Lauf. Bis dahin meldet der Loader
„Datensatz nicht vorhanden". Nach dem Ausrollen also einmal von Hand anstoßen, statt auf den
nächsten Cron zu warten. Der Ordner selbst wird bei Bedarf angelegt.
