diff --git a/database/docs/plan_pck_net_storage.md b/database/docs/plan_pck_net_storage.md index 76236dd..c2bee1d 100644 --- a/database/docs/plan_pck_net_storage.md +++ b/database/docs/plan_pck_net_storage.md @@ -62,6 +62,8 @@ Alle zur Laufzeit via `pck_system.f_get_par_wert_by_programmid`: | `NETSTORE_BA_PREFIX` | Pfad in Object Storage, wo BA-Daten liegen. Muss mit einem `/` enden, z.B. `BA/Eingang/` | | `NETSTORE_BA_IMPORT` | Name des Unterordners von NETSTORE_BA_PREFIX im Object Storage, wo entpackte Dateien, die noch importiert werden müssen, zwischengespeichert werden. | | `BA_IMPORT_SB_MIT_ID` | Mitarbeiter-ID für Import von BA Daten (z.B. Korrespondenzen). Diese Mitarbeiter-ID bekommt eine Wiedervorlage, für jede Datei, die nicht automatisch importiert werden konnte. | +| `AUTOMATON_BASE_URL` | Base-URL des Quarkus Dateieingang Service, z.B. `http://dateieingang:8080` | +| `AUTOMATON_API_KEY` | API-Key für den Quarkus Dateieingang Service (Header `X-Api-Key`) | --- @@ -156,7 +158,7 @@ Bucket nicht erreichbar → -20003 via `f_make_request` ## Sicherheit / Tenant-Isolation `p_assert_allowed` wird am Anfang jeder öffentlichen Funktion/Prozedur aufgerufen: -1. Tenant-Prefix laden via `f_get_cfg('NET_STORAGE_TENANT_ID')` +1. Tenant-Prefix laden via `f_get_cfg('NETSTORE_TENANT_ID')` 2. `..`-Sequenzen im übergebenen Key prüfen → -20004 3. Prüfen ob Key mit Tenant-Prefix beginnt → -20005 @@ -184,7 +186,22 @@ Die DB verarbeitet einen Unterordner **ausschließlich wenn der Marker vorhanden Der Marker wird erst gelöscht wenn **alle** Dateien des Unterordners erfolgreich verarbeitet wurden — so werden fehlgeschlagene Dateien beim nächsten Lauf erneut versucht. -### Prozedur `p_process_incoming_ba_data` (in `pck_auto_import`, nicht in pck_net_storage) +### Prozedur `p_run_ba_korrespondenz_dateieingang_automation` (in `pck_auto_import`) — APEX Automation Einstiegspunkt + +Von der APEX Automation (stündlich) aufgerufen. Orchestriert den Gesamtablauf: + +``` +1. p_process_incoming_ba_data aufrufen + → verarbeitet Batches die bereits in OCI liegen (Fallback, falls ORDS-Aufruf letzten Lauf fehlschlug) + → Fehler eskalieren (APEX Automation markiert Lauf als fehlerhaft) + +2. HTTP POST an AUTOMATON_BASE_URL/api/process-incoming (Header: X-Api-Key: AUTOMATON_API_KEY) + → 202 Accepted: Pipeline gestartet + → 409 Conflict: Service läuft bereits — kein zweiter Lauf, kein Fehler + → sonstige Fehler: loggen, nicht eskalieren (nächster Lauf führt Schritt 1 wieder aus) +``` + +### Prozedur `p_process_incoming_ba_data` (in `pck_auto_import`) — ORDS-Einstiegspunkt ``` 1. Unterordner von eingang/ auflisten (f_list_objects mit delimiter='/') diff --git a/database/packages/pck_auto_import.pkb b/database/packages/pck_auto_import.pkb index 83acc7a..72c02d4 100644 --- a/database/packages/pck_auto_import.pkb +++ b/database/packages/pck_auto_import.pkb @@ -2,6 +2,82 @@ create or replace package body pck_auto_import as c_log_module constant lg_app_log.log_module%type := 'AUTOMATISCHER_BA_IMPORT'; + procedure p_run_ba_korrespondenz_dateieingang_automation + /*Kopf------------------------------------------------------------------------------------------------ + -- Beschreibung: Einstiegspunkt für die APEX Automation (stündlich). + -- Schritt 1: p_process_incoming_ba_data aufrufen — verarbeitet Batches, die bereits + -- in OCI liegen (Fallback für den Fall, dass der ORDS-Aufruf im letzten + -- Quarkus-Lauf fehlgeschlagen ist). + -- Schritt 2: Quarkus Dateieingang Service via HTTP POST anstoßen (fire & forget). + -- Schlägt Schritt 2 fehl, läuft Schritt 1 beim nächsten Stundenlauf erneut. + ------------------------------------------------------------------------------------------------------ + -- Parameter: — + ------------------------------------------------------------------------------------------------------ + -- MA Datum Änderung + -- SCK 2026-04-21 Prozedur erstellt + ------------------------------------------------------------------------------------------------Kopf*/ + is + l_service_url varchar2(4000 char); + l_api_key varchar2(500 char); + l_response clob; + l_http_status number; + l_log_action constant varchar2(512 char) := 'DATEIEINGANG_AUTOMATION'; + begin + -- Schritt 1: Offene Batches in OCI verarbeiten + p_process_incoming_ba_data; + + -- Schritt 2: Quarkus anstoßen — Fehler werden geloggt, nicht eskaliert + begin + l_service_url := pck_system.f_get_par_wert_by_programmid('AUTOMATON_BASE_URL') || '/api/process-incoming'; + l_api_key := pck_system.f_get_par_wert_by_programmid('AUTOMATON_API_KEY'); + + apex_web_service.g_request_headers.delete; + apex_web_service.g_request_headers(1).name := 'X-Api-Key'; + apex_web_service.g_request_headers(1).value := l_api_key; + + l_response := apex_web_service.make_rest_request( + p_url => l_service_url + ,p_http_method => 'POST' + ); + l_http_status := apex_web_service.g_status_code; + + case l_http_status + when 202 + then + pck_log.p_info( + i_module => c_log_module + ,i_action => l_log_action + ,i_message => 'Dateieingang Service angestoßen (202 Accepted)' + ); + when 409 + then + -- Service läuft bereits — kein Fehler, kein zweiter Lauf nötig + pck_log.p_info( + i_module => c_log_module + ,i_action => l_log_action + ,i_message => 'Dateieingang Service läuft bereits (409 Conflict) — kein neuer Lauf gestartet' + ); + else + pck_log.p_warn( + i_module => c_log_module + ,i_action => l_log_action + ,i_message => 'Dateieingang Service: unerwarteter HTTP-Status ' || l_http_status + ); + end case; + exception + when others + then + -- Quarkus-Aufruf fehlgeschlagen: loggen, nicht eskalieren. + -- Nächster Stundenlauf führt Schritt 1 erneut aus. + pck_log.p_error( + i_module => c_log_module + ,i_action => l_log_action + ,i_message => 'Aufruf des Dateieingang Service fehlgeschlagen: ' || sqlerrm + ,i_detail => to_clob(dbms_utility.format_error_backtrace) + ); + end; + end p_run_ba_korrespondenz_dateieingang_automation; + procedure p_import_ba_korrespondenz ( i_object_key in varchar2 ,i_content in blob @@ -52,7 +128,7 @@ create or replace package body pck_auto_import as -- Wiedervorlage für Sachbearbeiter erstellen pck_wiedervorlage.p_wiedervorlage_anlegen (i_swv_stp_id_art => pck_stammdaten.f_get_stp_id_by_programmid('WV_IMPORT_DATEV') -- TODO: neue WV Art? z.B. WV_IMPORT_BA_DATEN ,i_swv_wdatum => sysdate --TODO: welches Datum? sysdate? - ,i_swv_bemerkung => 'Bitte manuell Prüfen: Die Datei "' || l_filename || '" konnte nicht automatisch importiert werden (Siehe "' || i_object_key || '").' + ,i_swv_bemerkung => 'Bitte manuell Prüfen: Die BA-Datei "' || l_filename || '" konnte nicht automatisch importiert werden (Siehe "' || i_object_key || '").' ,i_swv_mit_id_wsachbearbeiter => pck_system.f_get_par_wert_by_programmid('BA_IMPORT_SB_MIT_ID') ); @@ -75,12 +151,12 @@ create or replace package body pck_auto_import as procedure p_process_incoming_ba_data /*Kopf------------------------------------------------------------------------------------------------ - -- Beschreibung: Verarbeitet alle fertigen Eingangs-Batches aus dem OCI Eingangsordner. - -- Wird von ORDS-Endpunkt und APEX Automation aufgerufen. + -- Beschreibung: Verarbeitet alle fertigen Eingangs-Batches aus dem OCI Eingangsordner (Netzlaufwerk). + -- Wird von ORDS-Endpunkt (von Quarkus Automaton) und aus Apex Automation aufgerufen. -- Pro Datei: Import + Move → Commit; bei Exception: Rollback, Datei bleibt liegen, -- nächste Datei wird trotzdem verarbeitet. -- Nach dem Datei-Loop: DB-Marker immer löschen (verhindert erneuten Durchlauf). - -- Wenn danach noch Dateien im Ordner liegen: SB-Marker anlegen damit Sachbearbeiter + -- Wenn danach noch Dateien im Ordner liegen: Sachbearbeiter(SB)-Marker anlegen damit Sachbearbeiter -- die übriggebliebenen Dateien manuell prüfen können. ------------------------------------------------------------------------------------------------------ -- MA Datum Änderung @@ -106,13 +182,13 @@ create or replace package body pck_auto_import as l_target_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || 'Verarbeitet ' || to_char(sysdate, 'YYYY') || '/'; l_eingang_prefix := pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_PREFIX') || pck_system.f_get_par_wert_by_programmid('NETSTORE_BA_IMPORT'); - -- Unterordner in eingangs-ordner auflisten (es gibt einen Ordner für jeden entpackte ZIP-Datei) (Delimiter '/' liefert nur direkte Kinder) + -- Unterordner in eingangs-ordner auflisten (es gibt einen Ordner für jeden entpackte ZIP-Datei) l_folders := pck_net_storage.f_list_objects( - i_prefix => l_eingang_prefix - ,i_delimiter => '/' + i_parent_folder => l_eingang_prefix + ,i_include_subfolders => 'N' ); - for rec_folder in (select object_name, is_folder from table(l_folders)) + for rec_folder in (select object_key, is_folder from table(l_folders)) loop -- Nur Unterordner verarbeiten if rec_folder.is_folder != 'Y' @@ -122,7 +198,7 @@ create or replace package body pck_auto_import as -- Der Marker ist eine Datei mit speziellem Namen, welche vom quarkus automaton in einen entpackten zip-ordner gelegt wird um zu signalisieren, dass alle Dateien des ZIPs erfolgreich in den ordner gelegt wurden. -- Das verhindert die verarbeitung von unvollständig entpackten zips - l_db_processing_marker_key := rec_folder.object_name || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_DB'); + l_db_processing_marker_key := rec_folder.object_key || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_DB'); -- Marker prüfen: -20001 = nicht vorhanden → Upload noch nicht abgeschlossen begin @@ -139,32 +215,32 @@ create or replace package body pck_auto_import as -- Zip-Namen aus Ordnerpfad ableiten: eingang// → l_zip_name := substr( - rec_folder.object_name + rec_folder.object_key ,length(l_eingang_prefix) + 1 - ,length(rec_folder.object_name) - length(l_eingang_prefix) - 1 + ,length(rec_folder.object_key) - length(l_eingang_prefix) - 1 ); l_target_folder := l_target_prefix || l_zip_name || '/'; - -- Alle Dateien im Unterordner auflisten (kein Delimiter = flach, alle Tiefen) + -- Alle Dateien im Unterordner auflisten (inkl. Unterordner = alle Tiefen) l_files := pck_net_storage.f_list_objects( - i_prefix => rec_folder.object_name - ,i_delimiter => '' + i_parent_folder => rec_folder.object_key + ,i_include_subfolders => 'Y' ); - for rec_file in (select object_name, is_folder from table(l_files)) + for rec_file in (select object_key, is_folder from table(l_files)) loop -- Marker und Pseudo-Ordner überspringen - if rec_file.object_name = l_db_processing_marker_key or rec_file.is_folder = 'Y' + if rec_file.object_key = l_db_processing_marker_key or rec_file.is_folder = 'Y' then continue; end if; begin -- 1. Dateiinhalt laden - l_file_content := pck_net_storage.f_download_object(rec_file.object_name); + l_file_content := pck_net_storage.f_download_object(rec_file.object_key); -- 2. Fachliche Verarbeitung + Move bei Erfolg (innerhalb p_import_ba_korrespondenz) - p_import_ba_korrespondenz(rec_file.object_name, l_file_content, l_target_folder); + p_import_ba_korrespondenz(rec_file.object_key, l_file_content, l_target_folder); -- Commit pro Datei: OCI-Move ist nicht transaktional, daher DB-Änderungen sofort sichern -- sonst würde ein Fehler bei einer späteren Datei den DB-Import bereits verschobener Dateien zurückrollen @@ -179,7 +255,7 @@ create or replace package body pck_auto_import as ,i_action => 'IMPORT_FILE' ,i_message => 'Fehler bei Dateiverarbeitung: ' || sqlerrm ,i_detail => to_clob(dbms_utility.format_error_backtrace) - ,i_object_ref => rec_file.object_name + ,i_object_ref => rec_file.object_key ); end; end loop; @@ -189,13 +265,13 @@ create or replace package body pck_auto_import as -- Prüfen ob noch Dateien im Unterordner liegen (nicht erfolgreich importierte Dateien) l_files := pck_net_storage.f_list_objects( - i_prefix => rec_folder.object_name - ,i_delimiter => '' + i_parent_folder => rec_folder.object_key + ,i_include_subfolders => 'Y' ); l_has_remaining_files := false; - for rec_remaining in (select object_name, is_folder from table(l_files)) + for rec_remaining in (select object_key, is_folder from table(l_files)) loop if rec_remaining.is_folder != 'Y' then @@ -206,8 +282,8 @@ create or replace package body pck_auto_import as if l_has_remaining_files then - -- SB-Marker anlegen: signalisiert Sachbearbeitern, dass Dateien manuell geprüft werden müssen - l_sb_marker_key := rec_folder.object_name || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_SB'); + -- Sachbearbeiter (SB)-Marker anlegen: signalisiert Sachbearbeitern, dass Dateien manuell geprüft werden müssen + l_sb_marker_key := rec_folder.object_key || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_SB'); pck_net_storage.p_upload_object( i_object_key => l_sb_marker_key ,i_content => empty_blob() @@ -217,14 +293,14 @@ create or replace package body pck_auto_import as i_module => c_log_module ,i_action => l_log_action ,i_message => 'Batch mit Fehlern abgeschlossen — mind. eine Datei konnte nicht importiert werden, SB-Marker gesetzt' - ,i_object_ref => rec_folder.object_name + ,i_object_ref => rec_folder.object_key ); else pck_log.p_info( i_module => c_log_module ,i_action => l_log_action ,i_message => 'Batch abgeschlossen, alle Dateien erfolgreich importiert' - ,i_object_ref => rec_folder.object_name + ,i_object_ref => rec_folder.object_key ); end if; end loop; diff --git a/database/packages/pck_auto_import.pkh b/database/packages/pck_auto_import.pkh index 6093396..fedbd6f 100644 --- a/database/packages/pck_auto_import.pkh +++ b/database/packages/pck_auto_import.pkh @@ -1,5 +1,9 @@ create or replace package pck_auto_import as + -- Von APEX Automation aufgerufen: verarbeitet offene OCI-Batches, stößt dann Quarkus an + procedure p_run_ba_korrespondenz_dateieingang_automation; + + -- Von ORDS-Endpunkt aufgerufen (net_storage/process_incoming_ba_data): importiert Dateien aus OCI procedure p_process_incoming_ba_data; end pck_auto_import; diff --git a/quarkus-automaton/docs/Architecture.md b/quarkus-automaton/docs/Architecture.md index 805ab5c..e04f68b 100644 --- a/quarkus-automaton/docs/Architecture.md +++ b/quarkus-automaton/docs/Architecture.md @@ -4,7 +4,7 @@ Der Service empfängt einen HTTP-Trigger von der APEX Automation, verbindet sich mit einem SFTP-Server, lädt neue ZIP-Dateien herunter, entpackt sie und lädt den Inhalt in OCI Object -Storage hoch. Anschließend wird der ORDS-Endpunkt `pck_auto_import.p_process_incoming_files` +Storage hoch. Anschließend wird der ORDS-Endpunkt `pck_auto_import.p_process_incoming_ba_data` aufgerufen, der die DB-Verarbeitung anstößt. Das Scheduling bleibt bei APEX — der Service selbst hat keinen eigenen Scheduler. @@ -34,7 +34,7 @@ FileProcessingPipeline [ManagedExecutor — Hintergrund-Thread] │ └─ .processed (Erfolg) oder .error (Fehler) │ ├─→ OrdsNotificationService.notify() [MicroProfile REST Client] - │ └─ POST pck_auto_import.p_process_incoming_files + │ └─ POST pck_auto_import.p_process_incoming_ba_data │ └─→ Cleanup: lokale Dateien löschen [immer, im finally] │ @@ -105,7 +105,7 @@ galabau.oci.private-key-path=${OCI_PRIVATE_KEY_PATH} # ORDS galabau.ords.base-url=http://ords:8080 -galabau.ords.process-incoming-path=/ords/.../auto_import/process_incoming +galabau.ords.process-incoming-path=/ords/.../net_storage/process_incoming_ba_data galabau.ords.api-key=${GALABAU_ORDS_API_KEY} quarkus.rest-client.ords-client.url=${galabau.ords.base-url} diff --git a/quarkus-automaton/docs/Plan.md b/quarkus-automaton/docs/Plan.md index 7ec9639..daca2c4 100644 --- a/quarkus-automaton/docs/Plan.md +++ b/quarkus-automaton/docs/Plan.md @@ -49,7 +49,7 @@ FileProcessingPipeline [Hintergrund-Thread — für jede neue ZIP] ## Projektstruktur (Maven) ``` -backend-n8n-replacement/ +quarkus-automaton/ ├── pom.xml (Java 25, Quarkus 3.x LTS) │ ├── src/main/java/de/galabau/ @@ -150,7 +150,7 @@ galabau.oci.private-key-path=${OCI_PRIVATE_KEY_PATH} # z.B. /etc/oci/private-k # ORDS galabau.ords.base-url=http://ords:8080 -galabau.ords.process-incoming-path=/ords/.../net_storage/process_incoming +galabau.ords.process-incoming-path=/ords/.../net_storage/process_incoming_ba_data galabau.ords.api-key=${GALABAU_ORDS_API_KEY} # Fehlerbehandlung @@ -160,7 +160,7 @@ galabau.retry.backoff-ms=1000 **Credential-Strategie:** - Alle Passwörter und API-Keys über `${ENV_VAR_NAME}` — Quarkus liest Umgebungsvariablen nativ -- OCI-Auth via **Instance Principal** — kein Key-Management, keine Rotation (empfohlen auf OKE) +- OCI-Auth via **SimpleAuthenticationDetailsProvider** — Credentials aus Env-Vars, Private Key als gemountetes Kubernetes Secret (PEM-Datei); entspricht dem was `apex_web_service` mit OCI Web Credential intern tut (OCI HTTP Signature V1) - Deployment: Kubernetes Secrets → Environment Injection für Passwörter/API-Keys --- @@ -249,7 +249,7 @@ n8n fire-and-forget-Verhalten. | SFTP-Verbindung fehlgeschlagen | transient | nein | Nächster APEX-Lauf (1h) versucht es | | ZIP beschädigt | persistent | nein | ZIP auf SFTP umbenennen zu `.error`, Log | | OCI-Verbindung fehlgeschlagen (z.B. 503) | transient | ja (exponential backoff) | @Retry | -| OCI-Upload einer Datei schlägt fehl | persistent | nein | Bereits hochgeladene löschen, ZIP belassen, Log | +| OCI-Upload einer Datei schlägt fehl | persistent | nein | SFTP-Rename zu `.error`, Log — bereits hochgeladene OCI-Dateien bleiben (idempotent) | | ORDS-Aufruf schlägt fehl | transient | ja (2-3x) | Marker liegt vor → APEX Automation schlägt beim nächsten Lauf ein | | Allgemein technischer Fehler | fallabhängig | siehe SmallRye Fault Tolerance | Exception-Log | @@ -300,7 +300,7 @@ public class FileEntry { ## SFTP-Verarbeitung mit SSHJ -Siehe `plan_camel_integration.md` für Details zur SSHJ-Integration (Host-Key-Verification, +Siehe `docs/SFTP-Integration.md` für Details zur SSHJ-Integration (Host-Key-Verification, Credentials, Fehlerbehandlung). ### Verarbeitungslogik @@ -580,7 +580,7 @@ MDC nach jeder ZIP mit `MDC.clear()` leeren — damit kein Kontext in die nächs - [x] Concurrency Guard: **AtomicBoolean** (kein Doppellauf bei schnellen APEX-Retries) - [x] SFTP-Lösung: **SSHJ** (`com.hierynomus:sshj`) — leichtgewichtiger SFTP-Client - [x] SFTP Host-Key: **Fingerprint-basiert** via `galabau.sftp.host-key-fingerprint` in Config -- [x] OCI Auth: **SimpleAuthenticationDetailsProvider** (Credentials via Env-Vars + gemountetes Kubernetes Secret für Private Key) +- [x] OCI Auth: **SimpleAuthenticationDetailsProvider** (Credentials via Env-Vars + gemountetes Kubernetes Secret für Private Key — entspricht `apex_web_service` mit OCI Credential) - [x] OCI SDK: **OCI Java SDK** (HTTP Signature V1 automatisch) - [x] ZIP: **Apache Commons Compress** - [x] REST Clients: **MicroProfile REST Client** (configKey-basiert) diff --git a/quarkus-automaton/docs/SFTP-Integration.md b/quarkus-automaton/docs/SFTP-Integration.md index aa717e7..d099b69 100644 --- a/quarkus-automaton/docs/SFTP-Integration.md +++ b/quarkus-automaton/docs/SFTP-Integration.md @@ -288,4 +288,4 @@ galabau.sftp.password=${GALABAU_SFTP_PASSWORD} galabau.api.key=${GALABAU_API_KEY} ``` -Siehe `plan_n8n_replacement_quarkus.md` für vollständige Konfigurationsübersicht. +Siehe `docs/Plan.md` für vollständige Konfigurationsübersicht. diff --git a/workflow_dateieingang.md b/workflow_dateieingang.md index 9306799..5b48262 100644 --- a/workflow_dateieingang.md +++ b/workflow_dateieingang.md @@ -13,7 +13,7 @@ | **OCI Object Storage** | Zwischenspeicher — Eingangsordner, Zielordner nach Verarbeitung | | **Oracle DB / APEX** | Verarbeitung — liest Dateien aus OCI, importiert Daten | -Details zum Dateieingang Service: `agent-backend/docs/Architecture.md` +Details zum Dateieingang Service: `quarkus-automaton/docs/Architecture.md` Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md` --- @@ -23,9 +23,11 @@ Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md` ``` ┌─────────────────────────────────────────────────────────────────┐ │ APEX Automation (stündlich) │ +│ → pck_auto_import.p_run_ba_korrespondenz_dateieingang_automation │ │ │ -│ 1. Unterordner in eingang/ mit Marker vorhanden? │ -│ → ja: Dateien verarbeiten (gleiche Logik wie Schritt 4-6) │ +│ 1. p_process_incoming_ba_data aufrufen │ +│ → OCI-Batches mit Marker verarbeiten (Fallback: ORDS-Aufruf │ +│ im letzten Quarkus-Lauf fehlgeschlagen) │ │ │ │ 2. Dateieingang Service aufrufen (fire & forget) │ │ HTTP POST /api/process-incoming (Header: X-Api-Key) │ @@ -44,13 +46,13 @@ Details zur DB-Verarbeitung: `database/docs/plan_pck_net_storage.md` │ hochladen │ │ 3e. ZIP auf SFTP umbenennen (.processed oder .error) │ │ → erst NACH erfolgreichem Marker-Upload │ -│ 3f. ORDS-Endpunkt aufrufen (pck_auto_import.p_process_incoming)│ +│ 3f. ORDS-Endpunkt aufrufen (pck_auto_import.p_process_incoming_ba_data)│ │ 3g. Lokale Arbeitsdateien löschen │ └────────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ -│ Oracle DB (via ORDS-Endpunkt oder APEX Automation) │ +│ Oracle DB (via ORDS-Endpunkt) │ │ │ │ 4. Unterordner in eingang/ auflisten │ │ 5. Für jeden Unterordner mit Marker: │ @@ -85,7 +87,9 @@ bucket/ ``` Der Marker bleibt solange erhalten bis **alle** Dateien des Unterordners -erfolgreich verarbeitet wurden. Fehlgeschlagene Dateien werden beim nächsten Lauf erneut versucht. +verarbeitet wurden. Fehlgeschlagene Dateien bleiben im Ordner. +Wenn ein Import-Lauf alle Dateien im Ordner einmal versucht hat zu importieren, und min. eine Datei übrig geblieben ist, für die der automatische Import also nicht funktioniert hat, dann wird für jede dieser Dateien eine Wiedervorlage für die Sachbearbeiter erstellt und eine Marker Datei für die Sachbearbeiter wird im Ordner abgelegt. +Daran können die Sachbearbeiter erkennen, dass der Ordner nicht mehr automatisch importiert wird, sondern sie manuell tätig werden müssen. --- @@ -104,13 +108,30 @@ erfolgreich verarbeitet wurden. Fehlgeschlagene Dateien werden beim nächsten La **DB: Verarbeitung einer einzelnen Datei schlägt fehl** - Rollback — Datei bleibt in `eingang//` - ERROR in `lg_app_log` mit `log_object_ref = eingang//datei.csv` -- Marker bleibt erhalten → nächste Dateien im Batch werden weiterverarbeitet -- Beim nächsten Lauf wird die fehlgeschlagene Datei erneut versucht +- Nächste Dateien im Batch werden weiterverarbeitet + +**DB: Batch-Abschluss (nach dem Datei-Loop)** +- DB-Marker (`_READY_FOR_DB_PROCESSING_`) wird **immer gelöscht** — kein automatischer Retry +- Liegen noch Dateien im Unterordner: SB-Marker (`_BITTE_PRÜFEN_`) wird angelegt → Sachbearbeiter müssen manuell eingreifen +- Alle Dateien erfolgreich: INFO in `lg_app_log`, Unterordner ist leer **DB: p_move_object schlägt nach erfolgreichem Import fehl** - Rollback des Imports → sauberer Ausgangszustand -- Datei bleibt in `eingang//`, Marker bleibt erhalten -- Nächster Lauf versucht es erneut +- Datei bleibt in `eingang//` +- DB-Marker wird trotzdem am Ende des Loops gelöscht; falls noch Dateien übrig → SB-Marker + +--- + +## Warum ruft die APEX Automation p_process_incoming_ba_data auf, obwohl Quarkus das auch tut? + +`p_process_incoming_ba_data` wird in jedem Stundenlauf **zweimal** aufgerufen: + +1. Direkt durch `p_run_ba_korrespondenz_dateieingang_automation` (Schritt 1) +2. Indirekt — von Quarkus via ORDS, nachdem der Upload abgeschlossen ist (Schritt 3f) + +Der direkte Aufruf in Schritt 1 ist ein **Fallback**: Wenn der ORDS-Aufruf in einem vorherigen Quarkus-Lauf fehlgeschlagen ist, liegt der Marker bereits in OCI, aber `p_process_incoming_ba_data` wurde nie aufgerufen. Ohne Schritt 1 würde dieser Batch erst beim nächsten Quarkus-Lauf verarbeitet — und nur dann, wenn Quarkus beim nächsten Mal auch wieder erfolgreich bis zum ORDS-Aufruf kommt. Mit Schritt 1 ist die DB-Verarbeitung unabhängig davon, ob Quarkus den ORDS-Aufruf erfolgreich abgeschlossen hat. + +Das heißt: Quarkus ist der **schnelle Pfad** (Upload + sofortiger DB-Trigger), die APEX Automation ist die **Absicherung** (findet Marker, die noch nicht verarbeitet wurden). ---