p_import_ba_korrespondenz implementiert

This commit is contained in:
2026-04-09 11:18:46 +02:00
parent 7425e0d2c3
commit 70c3a26b39
2 changed files with 117 additions and 56 deletions

View File

@@ -56,9 +56,11 @@ Alle zur Laufzeit via `pck_system.f_get_par_wert_by_programmid`:
| `NETSTORE_REGION` | z.B. `eu-frankfurt-1` |
| `NETSTORE_TENANT_ID` | Erlaubter Root-Prefix, z.B. `mandant_42/` |
| `NETSTORE_CRED_ID` | Credential Static ID für apex_web_service calls, z.B. 'OCI_OBJ_STORE_CRED' |
| `NETSTORE_MARKER_NAME` | Name der Marker-Datei im Object Store, der von Automaton abgelegt wird, um zu signalisieren, dass der entsprechende Unterordner komplett hochgeladen wurde. Verhindert die Verarbeitung von unvollständig hochgeladenen Ordnern. z.B.: `_READY_FOR_DB_PROCESSING_` |
| `NETSTORE_MARKER_DB` | Name der Marker-Datei im Object Store, der von Automaton abgelegt wird, um zu signalisieren, dass der entsprechende Unterordner komplett hochgeladen wurde. Verhindert die Verarbeitung von unvollständig hochgeladenen Ordnern. z.B.: `_READY_FOR_DB_PROCESSING_` |
| `NETSTORE_MARKER_SB` | Name der Marker-Datei im Object Store, der von DB beim Verarbeiten angelegt wird, wenn eine oder mehrere Dateien eines ZIPs nicht automatisiert importiert werden konnten. Das soll signalisieren, dass ein Sachbearbeiter die Dateien in diesem Unterordner manuell prüfen und importieren muss. z.B.: `_BITTE_PRÜFEN_` |
| `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. |
---

View File

@@ -2,56 +2,111 @@ create or replace package body pck_auto_import as
c_module constant varchar2(20) := 'PCK_AUTO_IMPORT';
procedure p_import_file (
i_object_key in varchar2
,i_content in blob
procedure p_import_ba_korrespondenz (
i_object_key in varchar2
,i_content in blob
,i_target_folder in varchar2
)
/*Kopf------------------------------------------------------------------------------------------------
-- Beschreibung: Importiert eine einzelne Datei aus dem OCI Eingangsordner in die Datenbank.
-- Stub — muss pro Dateityp fachlich implementiert werden.
-- Kein Commit hier — wird von p_process_incoming_files übernommen.
-- Ruft inkasso.pck_import.f_import_ba_dokument auf.
-- Bei Rückgabe 1 (Erfolg): Datei in den Zielordner verschieben.
-- Bei Rückgabe != 1 (z.B. ungültiger Dateiname): Warnung loggen und Exception werfen
-- — Datei bleibt liegen, Commit/Rollback liegt beim Aufrufer.
-- Kein Commit hier — wird von p_process_incoming_ba_data übernommen.
------------------------------------------------------------------------------------------------------
-- Parameter: i_object_key Vollständiger OCI-Objektkey der zu verarbeitenden Datei
-- i_content Dateiinhalt als BLOB
-- Parameter: i_object_key Vollständiger OCI-Objektkey der zu verarbeitenden Datei
-- i_content Dateiinhalt als BLOB
-- i_target_folder Zielordner-Prefix für erfolgreich verarbeitete Dateien
------------------------------------------------------------------------------------------------------
-- MA Datum Änderung
-- SCK 2026-04-08 Stub erstellt
-- SCK 2026-04-09 Implementierung: Aufruf f_import_ba_dokument, Wiedervorlage bei Fehler
-- SCK 2026-04-09 Move nach erfolgreichem Import in p_import_ba_korrespondenz verschoben
------------------------------------------------------------------------------------------------Kopf*/
is
l_filename varchar2(512);
l_file_size number;
l_return number;
l_log_action varchar2(512 char) := 'IMPORT_BA_PDF_FILE';
begin
-- TODO: Fachliche Verarbeitung implementieren
-- Beispiel: CSV parsen und in Zieltabellen schreiben
null;
end p_import_file;
-- Dateiname aus OCI-Objektkey ableiten (letztes Segment nach '/')
l_filename := substr(i_object_key, instr(i_object_key, '/', -1) + 1);
l_file_size := dbms_lob.getlength(i_content);
l_return := inkasso.pck_import.f_import_ba_dokument(
i_datei => i_content
,i_dateiname => l_filename
,i_datei_groesse => l_file_size
);
if l_return != 1
then
pck_log.p_warn(
i_module => c_module
,i_action => l_log_action
,i_message => 'Import für Datei "' || l_filename || '" fehlgeschlagen (Rückgabe: ' || l_return || ') — Wiedervorlage erforderlich'
,i_object_ref => i_object_key
);
-- 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_mit_id_wsachbearbeiter => pck_system.f_get_par_wert_by_programmid('BA_IMPORT_SB_MIT_ID')
);
raise_application_error(-20000, 'Import fehlgeschlagen: "' || l_filename || '" (Rückgabe: ' || l_return || ')');
end if;
-- Datei in Verarbeitet-Ordner verschieben
pck_net_storage.p_move_object(
i_object_key => i_object_key
,i_target_prefix => i_target_folder
);
pck_log.p_info(
i_module => c_module
,i_action => l_log_action
,i_message => 'Datei "' || l_filename || '" erfolgreich verarbeitet und verschoben'
,i_object_ref => i_object_key
);
end p_import_ba_korrespondenz;
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.
-- Pro Datei: Import Move → Commit (Rollback + Fehlereintrag bei Exception).
-- Marker wird gelöscht wenn keine Dateien mehr im Unterordner verbleiben.
-- 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
-- die übriggebliebenen Dateien manuell prüfen können.
------------------------------------------------------------------------------------------------------
-- MA Datum Änderung
-- SCK 2026-04-08 Prozedur erstellt
-- SCK 2026-04-09 Fehlerbehandlung: Datei bleibt liegen, Fehler-Marker, kein erneuter Durchlauf
-- SCK 2026-04-09 SB-Marker statt l_had_errors-Flag; Move in p_import_ba_korrespondenz verschoben
------------------------------------------------------------------------------------------------Kopf*/
is
l_marker_key varchar2(1024);
l_target_folder varchar2(4000 char);
l_target_prefix varchar2(4000 char);
l_eingang_prefix varchar2(4000 char);
l_zip_name varchar2(512);
l_file_content blob;
l_meta pck_net_storage.t_object_meta;
l_remaining number;
l_folders t_net_storage_tab;
l_files t_net_storage_tab;
l_check t_net_storage_tab;
l_db_processing_marker_key varchar2(1024);
l_sb_marker_key varchar2(1024);
l_target_folder varchar2(4000 char);
l_target_prefix varchar2(4000 char);
l_eingang_prefix varchar2(4000 char);
l_zip_name varchar2(512);
l_file_content blob;
l_meta pck_net_storage.t_object_meta;
l_folders t_net_storage_tab;
l_files t_net_storage_tab;
l_has_remaining_files boolean;
l_log_action varchar2(512 char) := 'IMPORT_BA_DATA';
begin
-- Zielordner Name zusammenstellen
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 eingang/ auflisten (Delimiter '/' liefert nur direkte Kinder)
-- Unterordner in eingangs-ordner auflisten (es gibt einen Ordner für jeden entpackte ZIP-Datei) (Delimiter '/' liefert nur direkte Kinder)
l_folders := pck_net_storage.f_list_objects(
i_prefix => l_eingang_prefix
,i_delimiter => '/'
@@ -67,11 +122,11 @@ 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_marker_key := rec_folder.object_name || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_NAME');
l_db_processing_marker_key := rec_folder.object_name || pck_system.f_get_par_wert_by_programmid('NETSTORE_MARKER_DB');
-- Marker prüfen: -20001 = nicht vorhanden → Upload noch nicht abgeschlossen
begin
l_meta := pck_net_storage.f_get_object_metadata(l_marker_key);
l_meta := pck_net_storage.f_get_object_metadata(l_db_processing_marker_key);
exception
when others
then
@@ -99,7 +154,7 @@ create or replace package body pck_auto_import as
for rec_file in (select object_name, is_folder from table(l_files))
loop
-- Marker und Pseudo-Ordner überspringen
if rec_file.object_name = l_marker_key or rec_file.is_folder = 'Y'
if rec_file.object_name = l_db_processing_marker_key or rec_file.is_folder = 'Y'
then
continue;
end if;
@@ -108,24 +163,13 @@ create or replace package body pck_auto_import as
-- 1. Dateiinhalt laden
l_file_content := pck_net_storage.f_download_object(rec_file.object_name);
-- 2. Fachliche Verarbeitung (noch kein Commit)
p_import_file(rec_file.object_name, l_file_content);
-- 3. Datei in Zielordner verschieben (noch kein Commit)
-- Rollback von p_move_object macht auch den Import rückgängig
pck_net_storage.p_move_object(
i_object_key => rec_file.object_name
,i_target_prefix => l_target_folder
);
-- 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);
-- 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
commit;
pck_log.p_info(
i_module => c_module
,i_action => 'IMPORT_FILE'
,i_message => 'Datei erfolgreich verarbeitet'
,i_object_ref => rec_file.object_name
);
exception
when others
then
@@ -140,31 +184,46 @@ create or replace package body pck_auto_import as
end;
end loop;
-- Prüfen ob noch nicht verarbeitete Dateien im Unterordner verbleiben
l_remaining := 0;
-- DB-Marker immer entfernen — verhindert erneute Verarbeitung beim nächsten Lauf
pck_net_storage.p_delete_object(l_db_processing_marker_key);
l_check := pck_net_storage.f_list_objects(
-- 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 => ''
);
for rec_check in (select object_name, is_folder from table(l_check))
l_has_remaining_files := false;
for rec_remaining in (select object_name, is_folder from table(l_files))
loop
if rec_check.object_name != l_marker_key and rec_check.is_folder = 'N'
if rec_remaining.is_folder != 'Y'
then
l_remaining := l_remaining + 1;
l_has_remaining_files := true;
exit;
end if;
end loop;
-- Marker löschen wenn Batch vollständig abgeschlossen
if l_remaining = 0
if l_has_remaining_files
then
pck_net_storage.p_delete_object(l_marker_key);
-- 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');
pck_net_storage.p_upload_object(
i_object_key => l_sb_marker_key
,i_content => empty_blob()
,i_content_type => 'application/octet-stream'
);
pck_log.p_warn(
i_module => c_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
);
else
pck_log.p_info(
i_module => c_module
,i_action => 'PROCESS_INCOMING'
,i_message => 'Batch abgeschlossen, Marker gelöscht'
,i_action => l_log_action
,i_message => 'Batch abgeschlossen, alle Dateien erfolgreich importiert'
,i_object_ref => rec_folder.object_name
);
end if;