Add PACK_JSON generic parser and refactor IMPORT_COMENZI package

- Create PACK_JSON: Generic JSON parser for Oracle 10g/11g/12c compatibility
  * Uses only standard Oracle functions (no dependencies)
  * Functions: parse_array, get_string, get_number, get_boolean
  * Built-in error tracking with g_last_error property
  * Comprehensive test suite with 4 test functions
  * Supports nested objects and complex JSON structures

- Refactor IMPORT_COMENZI to use PACK_JSON instead of manual parsing
  * Clean separation of concerns - JSON parsing vs business logic
  * Improved error handling and logging consistency
  * Uses pINFO for consistent logging across packages

- Update IMPORT_PARTENERI logging to use pINFO consistently
  * Remove custom log_operatie function
  * Standardize logging format across all packages

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-09 22:36:03 +03:00
parent 3a4029cc6e
commit dc91372760
4 changed files with 943 additions and 547 deletions

View File

@@ -134,15 +134,6 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_PARTENERI AS
p_prenume OUT VARCHAR2 p_prenume OUT VARCHAR2
); );
/**
* Scrie in log operatiile executate
* @param p_mesaj Mesajul de logat
* @param p_nivel Nivelul: INFO, WARN, ERROR
*/
PROCEDURE log_operatie(
p_mesaj IN VARCHAR2,
p_nivel IN VARCHAR2 DEFAULT 'INFO'
);
END PACK_IMPORT_PARTENERI; END PACK_IMPORT_PARTENERI;
/ /
@@ -181,7 +172,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR in valideaza_date_partener: ' || SQLERRM, 'ERROR'); pINFO('ERROR in valideaza_date_partener: ' || SQLERRM, 'IMPORT_PARTENERI');
RAISE; RAISE;
END valideaza_date_partener; END valideaza_date_partener;
@@ -212,7 +203,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
v_cod_fiscal_curat := curata_text_cautare(p_cod_fiscal); v_cod_fiscal_curat := curata_text_cautare(p_cod_fiscal);
log_operatie('Cautare partener dupa cod_fiscal: ' || v_cod_fiscal_curat); pINFO('Cautare partener dupa cod_fiscal: ' || v_cod_fiscal_curat, 'IMPORT_PARTENERI');
-- Cautare in NOM_PARTENERI -- Cautare in NOM_PARTENERI
BEGIN BEGIN
@@ -222,12 +213,12 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
WHERE UPPER(TRIM(cod_fiscal)) = v_cod_fiscal_curat WHERE UPPER(TRIM(cod_fiscal)) = v_cod_fiscal_curat
AND ROWNUM = 1; -- În caz de duplicate, luam primul AND ROWNUM = 1; -- În caz de duplicate, luam primul
log_operatie('Gasit partener cu cod_fiscal ' || v_cod_fiscal_curat || ': ID_PART=' || v_id_part); pINFO('Gasit partener cu cod_fiscal ' || v_cod_fiscal_curat || ': ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
EXCEPTION EXCEPTION
WHEN NO_DATA_FOUND THEN WHEN NO_DATA_FOUND THEN
log_operatie('Nu s-a gasit partener cu cod_fiscal: ' || v_cod_fiscal_curat); pINFO('Nu s-a gasit partener cu cod_fiscal: ' || v_cod_fiscal_curat, 'IMPORT_PARTENERI');
RETURN NULL; RETURN NULL;
WHEN TOO_MANY_ROWS THEN WHEN TOO_MANY_ROWS THEN
@@ -242,14 +233,14 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
) )
WHERE ROWNUM = 1; WHERE ROWNUM = 1;
log_operatie('WARNING: Multiple parteneri cu acelasi cod_fiscal ' || v_cod_fiscal_curat || pINFO('WARNING: Multiple parteneri cu acelasi cod_fiscal ' || v_cod_fiscal_curat ||
'. Selectat ID_PART=' || v_id_part, 'WARN'); '. Selectat ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
END; END;
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR in cauta_partener_dupa_cod_fiscal: ' || SQLERRM, 'ERROR'); pINFO('ERROR in cauta_partener_dupa_cod_fiscal: ' || SQLERRM, 'IMPORT_PARTENERI');
RAISE; RAISE;
END cauta_partener_dupa_cod_fiscal; END cauta_partener_dupa_cod_fiscal;
@@ -264,7 +255,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
v_denumire_curata := curata_text_cautare(p_denumire); v_denumire_curata := curata_text_cautare(p_denumire);
log_operatie('Cautare partener dupa denumire: ' || v_denumire_curata); pINFO('Cautare partener dupa denumire: ' || v_denumire_curata, 'IMPORT_PARTENERI');
-- Cautare in NOM_PARTENERI -- Cautare in NOM_PARTENERI
BEGIN BEGIN
@@ -274,12 +265,12 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
WHERE UPPER(TRIM(denumire)) = v_denumire_curata WHERE UPPER(TRIM(denumire)) = v_denumire_curata
AND ROWNUM = 1; -- În caz de duplicate, luam primul AND ROWNUM = 1; -- În caz de duplicate, luam primul
log_operatie('Gasit partener cu denumirea ' || v_denumire_curata || ': ID_PART=' || v_id_part); pINFO('Gasit partener cu denumirea ' || v_denumire_curata || ': ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
EXCEPTION EXCEPTION
WHEN NO_DATA_FOUND THEN WHEN NO_DATA_FOUND THEN
log_operatie('Nu s-a gasit partener cu denumirea: ' || v_denumire_curata); pINFO('Nu s-a gasit partener cu denumirea: ' || v_denumire_curata, 'IMPORT_PARTENERI');
RETURN NULL; RETURN NULL;
WHEN TOO_MANY_ROWS THEN WHEN TOO_MANY_ROWS THEN
@@ -294,14 +285,14 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
) )
WHERE ROWNUM = 1; WHERE ROWNUM = 1;
log_operatie('WARNING: Multiple parteneri cu aceeasi denumire ' || v_denumire_curata || pINFO('WARNING: Multiple parteneri cu aceeasi denumire ' || v_denumire_curata ||
'. Selectat ID_PART=' || v_id_part, 'WARN'); '. Selectat ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
END; END;
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR in cauta_partener_dupa_denumire: ' || SQLERRM, 'ERROR'); pINFO('ERROR in cauta_partener_dupa_denumire: ' || SQLERRM, 'IMPORT_PARTENERI');
RAISE; RAISE;
END cauta_partener_dupa_denumire; END cauta_partener_dupa_denumire;
@@ -324,7 +315,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR in este_persoana_fizica: ' || SQLERRM, 'ERROR'); pINFO('ERROR in este_persoana_fizica: ' || SQLERRM, 'IMPORT_PARTENERI');
RETURN 0; RETURN 0;
END este_persoana_fizica; END este_persoana_fizica;
@@ -369,7 +360,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR in separa_nume_prenume: ' || SQLERRM, 'ERROR'); pINFO('ERROR in separa_nume_prenume: ' || SQLERRM, 'IMPORT_PARTENERI');
p_nume := SUBSTR(p_denumire_completa, 1, 50); -- fallback p_nume := SUBSTR(p_denumire_completa, 1, 50); -- fallback
p_prenume := NULL; p_prenume := NULL;
END separa_nume_prenume; END separa_nume_prenume;
@@ -394,13 +385,13 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
-- Validare input -- Validare input
IF p_adresa_text IS NULL THEN IF p_adresa_text IS NULL THEN
log_operatie('Adresa goala, se folosesc valorile default', 'WARN'); pINFO('Adresa goala, se folosesc valorile default', 'IMPORT_PARTENERI');
RETURN; RETURN;
END IF; END IF;
v_adresa_curata := TRIM(p_adresa_text); v_adresa_curata := TRIM(p_adresa_text);
log_operatie('Parsare adresa: ' || v_adresa_curata); pINFO('Parsare adresa: ' || v_adresa_curata, 'IMPORT_PARTENERI');
-- Split dupa semicolon -- Split dupa semicolon
SELECT TRIM(REGEXP_SUBSTR(v_adresa_curata, '[^;]+', 1, LEVEL)) SELECT TRIM(REGEXP_SUBSTR(v_adresa_curata, '[^;]+', 1, LEVEL))
@@ -411,7 +402,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
v_count := v_componente.COUNT; v_count := v_componente.COUNT;
IF v_count = 0 THEN IF v_count = 0 THEN
log_operatie('Nu s-au gasit componente in adresa', 'WARN'); pINFO('Nu s-au gasit componente in adresa', 'IMPORT_PARTENERI');
RETURN; RETURN;
END IF; END IF;
@@ -472,12 +463,12 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
p_sector := C_SECTOR_DEFAULT; p_sector := C_SECTOR_DEFAULT;
END IF; END IF;
log_operatie('Adresa parsata: JUD=' || p_judet || ', LOC=' || p_localitate || pINFO('Adresa parsata: JUD=' || p_judet || ', LOC=' || p_localitate ||
', STRADA=' || NVL(p_strada, 'NULL') || ', SECTOR=' || p_sector); ', STRADA=' || NVL(p_strada, 'NULL') || ', SECTOR=' || p_sector, 'IMPORT_PARTENERI');
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR in parseaza_adresa_semicolon: ' || SQLERRM, 'ERROR'); pINFO('ERROR in parseaza_adresa_semicolon: ' || SQLERRM, 'IMPORT_PARTENERI');
-- Pastram valorile default in caz de eroare -- Pastram valorile default in caz de eroare
p_judet := C_JUD_DEFAULT; p_judet := C_JUD_DEFAULT;
p_localitate := C_LOCALITATE_DEFAULT; p_localitate := C_LOCALITATE_DEFAULT;
@@ -509,10 +500,10 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
v_denumire_curata VARCHAR2(200); v_denumire_curata VARCHAR2(200);
BEGIN BEGIN
log_operatie('=== ÎNCEPUT cauta_sau_creeaza_partener ==='); pINFO('=== ÎNCEPUT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
log_operatie('Input: cod_fiscal=' || NVL(p_cod_fiscal, 'NULL') || pINFO('Input: cod_fiscal=' || NVL(p_cod_fiscal, 'NULL') ||
', denumire=' || NVL(p_denumire, 'NULL') || ', denumire=' || NVL(p_denumire, 'NULL') ||
', adresa=' || NVL(p_adresa, 'NULL')); ', adresa=' || NVL(p_adresa, 'NULL'), 'IMPORT_PARTENERI');
-- Validare date input -- Validare date input
IF NOT valideaza_date_partener(p_cod_fiscal, p_denumire) THEN IF NOT valideaza_date_partener(p_cod_fiscal, p_denumire) THEN
@@ -527,8 +518,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
v_id_part := cauta_partener_dupa_cod_fiscal(v_cod_fiscal_curat); v_id_part := cauta_partener_dupa_cod_fiscal(v_cod_fiscal_curat);
IF v_id_part IS NOT NULL THEN IF v_id_part IS NOT NULL THEN
log_operatie('Partener gasit dupa cod_fiscal. ID_PART=' || v_id_part); pINFO('Partener gasit dupa cod_fiscal. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
log_operatie('=== SFÂRȘIT cauta_sau_creeaza_partener ==='); pINFO('=== SFÂRȘIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
END IF; END IF;
END IF; END IF;
@@ -537,21 +528,21 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
v_id_part := cauta_partener_dupa_denumire(v_denumire_curata); v_id_part := cauta_partener_dupa_denumire(v_denumire_curata);
IF v_id_part IS NOT NULL THEN IF v_id_part IS NOT NULL THEN
log_operatie('Partener gasit dupa denumire. ID_PART=' || v_id_part); pINFO('Partener gasit dupa denumire. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
log_operatie('=== SFÂRȘIT cauta_sau_creeaza_partener ==='); pINFO('=== SFÂRȘIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
END IF; END IF;
-- STEP 3: Creare partener nou -- STEP 3: Creare partener nou
log_operatie('Nu s-a gasit partener existent. Se creeaza unul nou...'); pINFO('Nu s-a gasit partener existent. Se creeaza unul nou...', 'IMPORT_PARTENERI');
-- Verifica tipul partenerului -- Verifica tipul partenerului
v_este_persoana_fizica := este_persoana_fizica(v_cod_fiscal_curat); v_este_persoana_fizica := este_persoana_fizica(v_cod_fiscal_curat);
IF v_este_persoana_fizica = 1 THEN IF v_este_persoana_fizica = 1 THEN
log_operatie('Detectata persoana fizica (CUI 13 cifre)'); pINFO('Detectata persoana fizica (CUI 13 cifre)', 'IMPORT_PARTENERI');
separa_nume_prenume(v_denumire_curata, v_nume, v_prenume); separa_nume_prenume(v_denumire_curata, v_nume, v_prenume);
log_operatie('Nume separat: NUME=' || NVL(v_nume, 'NULL') || ', PRENUME=' || NVL(v_prenume, 'NULL')); pINFO('Nume separat: NUME=' || NVL(v_nume, 'NULL') || ', PRENUME=' || NVL(v_prenume, 'NULL'), 'IMPORT_PARTENERI');
END IF; END IF;
-- Creare partener prin pack_def -- Creare partener prin pack_def
@@ -606,17 +597,17 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
RAISE_APPLICATION_ERROR(-20003, 'pack_def.adauga_partener a returnat ID invalid'); RAISE_APPLICATION_ERROR(-20003, 'pack_def.adauga_partener a returnat ID invalid');
END IF; END IF;
log_operatie('Partener creat cu succes. ID_PART=' || v_id_part); pINFO('Partener creat cu succes. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR la crearea partenerului prin pack_def: ' || SQLERRM, 'ERROR'); pINFO('ERROR la crearea partenerului prin pack_def: ' || SQLERRM, 'IMPORT_PARTENERI');
RAISE integrare_pack_def_exception; RAISE integrare_pack_def_exception;
END; END;
-- STEP 4: Adaugare adresa (daca exista) -- STEP 4: Adaugare adresa (daca exista)
IF p_adresa IS NOT NULL THEN IF p_adresa IS NOT NULL THEN
log_operatie('Se adauga adresa pentru partenerul nou creat...'); pINFO('Se adauga adresa pentru partenerul nou creat...', 'IMPORT_PARTENERI');
-- Parseaza adresa -- Parseaza adresa
parseaza_adresa_semicolon(p_adresa, v_judet, v_localitate, v_strada, v_sector); parseaza_adresa_semicolon(p_adresa, v_judet, v_localitate, v_strada, v_sector);
@@ -650,72 +641,41 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
); );
IF v_id_adresa IS NOT NULL AND v_id_adresa > 0 THEN IF v_id_adresa IS NOT NULL AND v_id_adresa > 0 THEN
log_operatie('Adresa adaugata cu succes. ID_ADRESA=' || v_id_adresa); pINFO('Adresa adaugata cu succes. ID_ADRESA=' || v_id_adresa, 'IMPORT_PARTENERI');
ELSE ELSE
log_operatie('WARNING: pack_def.adauga_adresa_partener2 a returnat ID invalid: ' || NVL(TO_CHAR(v_id_adresa), 'NULL'), 'WARN'); pINFO('WARNING: pack_def.adauga_adresa_partener2 a returnat ID invalid: ' || NVL(TO_CHAR(v_id_adresa), 'NULL'), 'IMPORT_PARTENERI');
END IF; END IF;
EXCEPTION EXCEPTION
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR la adaugarea adresei prin pack_def: ' || SQLERRM, 'ERROR'); pINFO('ERROR la adaugarea adresei prin pack_def: ' || SQLERRM, 'IMPORT_PARTENERI');
-- Nu raisam exceptia pentru adresa, partenerii pot exista fara adresa -- Nu raisam exceptia pentru adresa, partenerii pot exista fara adresa
log_operatie('Partenerul a fost creat, dar adresa nu a putut fi adaugata', 'WARN'); pINFO('Partenerul a fost creat, dar adresa nu a putut fi adaugata', 'IMPORT_PARTENERI');
END; END;
ELSE ELSE
log_operatie('Nu s-a furnizat adresa pentru partenerul nou'); pINFO('Nu s-a furnizat adresa pentru partenerul nou', 'IMPORT_PARTENERI');
END IF; END IF;
log_operatie('Partener creat complet. ID_PART=' || v_id_part); pINFO('Partener creat complet. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
log_operatie('=== SFÂRȘIT cauta_sau_creeaza_partener ==='); pINFO('=== SFÂRȘIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
RETURN v_id_part; RETURN v_id_part;
EXCEPTION EXCEPTION
WHEN partener_invalid_exception THEN WHEN partener_invalid_exception THEN
log_operatie('ERROR: Date partener invalide', 'ERROR'); pINFO('ERROR: Date partener invalide', 'IMPORT_PARTENERI');
RAISE_APPLICATION_ERROR(-20001, 'Date partener invalide: ' || SQLERRM); RAISE_APPLICATION_ERROR(-20001, 'Date partener invalide: ' || SQLERRM);
WHEN integrare_pack_def_exception THEN WHEN integrare_pack_def_exception THEN
log_operatie('ERROR: Problema la integrarea cu pack_def', 'ERROR'); pINFO('ERROR: Problema la integrarea cu pack_def', 'IMPORT_PARTENERI');
RAISE_APPLICATION_ERROR(-20003, 'Eroare la integrarea cu pack_def: ' || SQLERRM); RAISE_APPLICATION_ERROR(-20003, 'Eroare la integrarea cu pack_def: ' || SQLERRM);
WHEN OTHERS THEN WHEN OTHERS THEN
log_operatie('ERROR NEAȘTEPTAT in cauta_sau_creeaza_partener: ' || SQLERRM, 'ERROR'); pINFO('ERROR NEAȘTEPTAT in cauta_sau_creeaza_partener: ' || SQLERRM, 'IMPORT_PARTENERI');
RAISE_APPLICATION_ERROR(-20099, 'Eroare neasteptata la crearea partenerului: ' || SQLERRM); RAISE_APPLICATION_ERROR(-20099, 'Eroare neasteptata la crearea partenerului: ' || SQLERRM);
END cauta_sau_creeaza_partener; END cauta_sau_creeaza_partener;
PROCEDURE log_operatie(
p_mesaj IN VARCHAR2,
p_nivel IN VARCHAR2 DEFAULT 'INFO'
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
v_timestamp VARCHAR2(50);
v_mesaj_complet VARCHAR2(4000);
BEGIN
v_timestamp := TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
v_mesaj_complet := v_timestamp || ' | ' || RPAD(p_nivel, 5) || ' | IMPORT_PARTENERI | ' || p_mesaj;
-- Output in server log (DBMS_OUTPUT pentru sesiuni interactive)
DBMS_OUTPUT.PUT_LINE(v_mesaj_complet);
-- Scrie in tabela INFO pentru logging
BEGIN
INSERT INTO INFO (info, locatia)
VALUES (v_mesaj_complet, 'IMPORT_PARTENERI');
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- Ignora erorile de logging - nu vrem sa intrerupa procesul principal
NULL;
END;
EXCEPTION
WHEN OTHERS THEN
-- Nu lasam logging-ul sa intrerupa procesul principal
NULL;
END log_operatie;
END PACK_IMPORT_PARTENERI; END PACK_IMPORT_PARTENERI;
/ /

View File

@@ -1,463 +0,0 @@
-- ====================================================================
-- P1-003: Package IMPORT_COMENZI pentru import comenzi web → ROA
-- Sistem Import Comenzi Web → ROA
-- ====================================================================
-- Package pentru importul comenzilor web cu mapări complexe SKU → CODMAT
CREATE OR REPLACE PACKAGE IMPORT_COMENZI AS
-- Tipuri pentru returnarea rezultatelor
TYPE t_articol_result IS RECORD (
id_articol NUMBER,
codmat VARCHAR2(50),
cantitate_roa NUMBER,
pret_unitar NUMBER,
success NUMBER,
error_message VARCHAR2(4000)
);
TYPE t_articol_table IS TABLE OF t_articol_result;
-- Funcție pentru găsirea/maparea articolelor ROA
FUNCTION gaseste_articol_roa(
p_sku IN VARCHAR2,
p_pret_web IN NUMBER DEFAULT NULL,
p_cantitate_web IN NUMBER DEFAULT 1
) RETURN t_articol_table PIPELINED;
-- Funcție pentru importul complet al unei comenzi web
FUNCTION importa_comanda_web(
p_nr_comanda_ext IN VARCHAR2,
p_data_comanda IN DATE,
p_id_partener IN NUMBER,
p_json_articole IN CLOB, -- JSON array cu articolele
p_id_adresa_livrare IN NUMBER DEFAULT NULL,
p_observatii IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER; -- Returnează ID_COMANDA sau -1 pentru eroare
END IMPORT_COMENZI;
/
-- ====================================================================
-- Package Body - Implementarea funcțiilor
-- ====================================================================
CREATE OR REPLACE PACKAGE BODY IMPORT_COMENZI AS
-- Constante pentru configurare
c_id_gestiune CONSTANT NUMBER := 1;
c_id_sectie CONSTANT NUMBER := 1;
c_id_pol CONSTANT NUMBER := NULL;
c_id_util CONSTANT NUMBER := -3; -- Sistem
c_interna CONSTANT NUMBER := 0; -- Externe
-- Procedură internă pentru logging
PROCEDURE log_operation(
p_level IN VARCHAR2, -- INFO, WARN, ERROR
p_operation IN VARCHAR2, -- GASESTE_ARTICOL, IMPORTA_COMANDA
p_reference IN VARCHAR2, -- SKU sau Nr_Comanda
p_message IN VARCHAR2,
p_details IN VARCHAR2 DEFAULT NULL
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- Log în tabel sau fișier sistem (simplificat pentru acest exemplu)
INSERT INTO import_log (
log_time, log_level, operation, reference_id, message, details
) VALUES (
SYSDATE, p_level, p_operation, p_reference, p_message, p_details
);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
-- Fallback: Nu bloca operația principală dacă logging-ul eșuează
NULL;
END log_operation;
-- Procedură internă pentru validarea seturilor
FUNCTION valideaza_set(p_sku IN VARCHAR2) RETURN BOOLEAN IS
v_suma_procent NUMBER := 0;
v_count_articole NUMBER := 0;
BEGIN
SELECT NVL(SUM(procent_pret), 0), COUNT(*)
INTO v_suma_procent, v_count_articole
FROM articole_terti
WHERE sku = p_sku
AND activ = 1;
-- Validări logice pentru seturi
IF v_count_articole > 1 THEN
-- Set compus - suma procentelor trebuie să fie între 95-105% (toleranță)
IF v_suma_procent < 95 OR v_suma_procent > 105 THEN
log_operation('WARN', 'VALIDEAZA_SET', p_sku,
'Suma procente nelogică: ' || v_suma_procent || '%');
RETURN FALSE;
END IF;
ELSIF v_count_articole = 1 THEN
-- Reîmpachetare - procentul trebuie să fie 100%
IF v_suma_procent != 100 THEN
log_operation('WARN', 'VALIDEAZA_SET', p_sku,
'Reîmpachetare cu procent != 100%: ' || v_suma_procent || '%');
RETURN FALSE;
END IF;
END IF;
RETURN TRUE;
END valideaza_set;
-- ================================================================
-- Funcția principală pentru găsirea articolelor ROA
-- ================================================================
FUNCTION gaseste_articol_roa(
p_sku IN VARCHAR2,
p_pret_web IN NUMBER DEFAULT NULL,
p_cantitate_web IN NUMBER DEFAULT 1
) RETURN t_articol_table PIPELINED IS
v_result t_articol_result;
v_found_mapping BOOLEAN := FALSE;
v_id_articol NUMBER;
-- Cursor pentru mapările din ARTICOLE_TERTI
CURSOR c_mapari IS
SELECT at.codmat, at.cantitate_roa, at.procent_pret,
na.id_articol, na.pret_vanzare
FROM articole_terti at
JOIN nom_articole na ON na.codmat = at.codmat
WHERE at.sku = p_sku
AND at.activ = 1
ORDER BY at.procent_pret DESC; -- Articolele principale primul
BEGIN
log_operation('INFO', 'GASESTE_ARTICOL', p_sku,
'Căutare articol pentru SKU: ' || p_sku);
-- Inițializare rezultat
v_result.success := 0;
v_result.error_message := NULL;
-- STEP 1: Verifică mapările speciale din ARTICOLE_TERTI
FOR rec IN c_mapari LOOP
v_found_mapping := TRUE;
v_result.id_articol := rec.id_articol;
v_result.codmat := rec.codmat;
v_result.cantitate_roa := rec.cantitate_roa * p_cantitate_web;
-- Calculează prețul unitar pe baza procentului alocat
IF p_pret_web IS NOT NULL THEN
v_result.pret_unitar := (p_pret_web * rec.procent_pret / 100) / rec.cantitate_roa;
ELSE
-- Folosește prețul din nomenclator
v_result.pret_unitar := NVL(rec.pret_vanzare, 0);
END IF;
v_result.success := 1;
log_operation('INFO', 'GASESTE_ARTICOL', p_sku,
'Mapare găsită: ' || rec.codmat ||
', Cant: ' || v_result.cantitate_roa ||
', Preț: ' || v_result.pret_unitar);
PIPE ROW(v_result);
END LOOP;
-- STEP 2: Dacă nu s-au găsit mapări speciale, caută direct în nom_articole
IF NOT v_found_mapping THEN
BEGIN
SELECT id_articol, codmat, NVL(pret_vanzare, 0)
INTO v_result.id_articol, v_result.codmat, v_result.pret_unitar
FROM nom_articole
WHERE codmat = p_sku
AND activ = 1;
v_result.cantitate_roa := p_cantitate_web;
-- Pentru căutare directă, folosește prețul din web dacă este furnizat
IF p_pret_web IS NOT NULL THEN
v_result.pret_unitar := p_pret_web;
END IF;
v_result.success := 1;
log_operation('INFO', 'GASESTE_ARTICOL', p_sku,
'Găsit direct în nomenclator: ' || v_result.codmat);
PIPE ROW(v_result);
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_result.success := 0;
v_result.error_message := 'SKU nu a fost găsit nici în ARTICOLE_TERTI, nici în nom_articole: ' || p_sku;
log_operation('ERROR', 'GASESTE_ARTICOL', p_sku, v_result.error_message);
PIPE ROW(v_result);
WHEN TOO_MANY_ROWS THEN
v_result.success := 0;
v_result.error_message := 'Multiple articole găsite pentru SKU: ' || p_sku;
log_operation('ERROR', 'GASESTE_ARTICOL', p_sku, v_result.error_message);
PIPE ROW(v_result);
END;
ELSE
-- Validează seturile după ce au fost returnate toate mapările
IF NOT valideaza_set(p_sku) THEN
log_operation('WARN', 'GASESTE_ARTICOL', p_sku,
'Set cu configurație suspectă - verifică procentele');
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
v_result.success := 0;
v_result.error_message := 'Eroare neașteptată: ' || SQLERRM;
log_operation('ERROR', 'GASESTE_ARTICOL', p_sku,
'Eroare neașteptată', SQLERRM);
PIPE ROW(v_result);
END gaseste_articol_roa;
-- ================================================================
-- Funcția pentru importul complet al unei comenzi web
-- ================================================================
FUNCTION importa_comanda_web(
p_nr_comanda_ext IN VARCHAR2,
p_data_comanda IN DATE,
p_id_partener IN NUMBER,
p_json_articole IN CLOB,
p_id_adresa_livrare IN NUMBER DEFAULT NULL,
p_observatii IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER IS
v_id_comanda NUMBER;
v_data_livrare DATE;
v_json_obj JSON_OBJECT_T;
v_json_array JSON_ARRAY_T;
v_articol_obj JSON_OBJECT_T;
v_sku VARCHAR2(100);
v_cantitate_web NUMBER;
v_pret_web NUMBER;
v_articole_procesate NUMBER := 0;
v_articole_eroare NUMBER := 0;
v_start_time DATE;
BEGIN
v_start_time := SYSDATE;
log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Începere import comandă pentru partener: ' || p_id_partener);
-- Validări de bază
IF p_nr_comanda_ext IS NULL OR p_id_partener IS NULL THEN
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Parametri obligatorii lipsă');
RETURN -1;
END IF;
-- Verifică dacă comanda nu există deja
BEGIN
SELECT id_comanda INTO v_id_comanda
FROM comenzi
WHERE comanda_externa = p_nr_comanda_ext
AND sters = 0;
log_operation('WARN', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Comanda există deja cu ID: ' || v_id_comanda);
RETURN v_id_comanda; -- Returnează ID-ul comenzii existente
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL; -- Normal, comanda nu există
END;
-- Calculează data de livrare (comanda + 1 zi)
v_data_livrare := p_data_comanda + 1;
-- STEP 1: Creează comanda folosind package-ul existent
BEGIN
v_id_comanda := PACK_COMENZI.adauga_comanda(
p_nr_comanda => p_nr_comanda_ext,
p_data_comanda => p_data_comanda,
p_id_partener => p_id_partener,
p_data_livrare => v_data_livrare,
p_id_gestiune => c_id_gestiune,
p_id_sectie => c_id_sectie,
p_interna => c_interna,
p_id_util => c_id_util,
p_comanda_externa => p_nr_comanda_ext,
p_id_adresa_livrare => p_id_adresa_livrare,
p_observatii => p_observatii
);
IF v_id_comanda IS NULL OR v_id_comanda <= 0 THEN
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'PACK_COMENZI.adauga_comanda a returnat ID invalid');
RETURN -1;
END IF;
log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Comanda creată cu ID: ' || v_id_comanda);
EXCEPTION
WHEN OTHERS THEN
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Eroare la crearea comenzii', SQLERRM);
RETURN -1;
END;
-- STEP 2: Procesează articolele din JSON
BEGIN
v_json_array := JSON_ARRAY_T.parse(p_json_articole);
FOR i IN 0 .. v_json_array.get_size() - 1 LOOP
v_articol_obj := TREAT(v_json_array.get(i) AS JSON_OBJECT_T);
-- Extrage datele articolului
v_sku := v_articol_obj.get_string('sku');
v_cantitate_web := v_articol_obj.get_number('cantitate');
v_pret_web := v_articol_obj.get_number('pret');
log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Procesez articol: ' || v_sku || ', cant: ' || v_cantitate_web);
-- STEP 3: Găsește mapările pentru acest SKU
FOR art_rec IN (
SELECT * FROM TABLE(gaseste_articol_roa(v_sku, v_pret_web, v_cantitate_web))
) LOOP
IF art_rec.success = 1 THEN
-- Adaugă articolul la comandă
BEGIN
PACK_COMENZI.adauga_articol_comanda(
p_id_comanda => v_id_comanda,
p_id_articol => art_rec.id_articol,
p_cantitate => art_rec.cantitate_roa,
p_pret => art_rec.pret_unitar,
p_id_pol => c_id_pol,
p_id_util => c_id_util
);
v_articole_procesate := v_articole_procesate + 1;
log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Articol adăugat: ' || art_rec.codmat ||
', cant: ' || art_rec.cantitate_roa ||
', preț: ' || art_rec.pret_unitar);
EXCEPTION
WHEN OTHERS THEN
v_articole_eroare := v_articole_eroare + 1;
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Eroare la adăugare articol ' || art_rec.codmat, SQLERRM);
END;
ELSE
v_articole_eroare := v_articole_eroare + 1;
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'SKU nu a putut fi mapat: ' || v_sku || ' - ' || art_rec.error_message);
END IF;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Eroare la procesarea JSON articole', SQLERRM);
RETURN -1;
END;
-- Verifică dacă s-au procesat articole cu succes
IF v_articole_procesate = 0 THEN
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Niciun articol nu a fost procesat cu succes');
RETURN -1;
END IF;
-- Log sumar final
log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Import finalizat - ID comanda: ' || v_id_comanda ||
', Articole procesate: ' || v_articole_procesate ||
', Articole cu erori: ' || v_articole_eroare ||
', Timp procesare: ' || ROUND((SYSDATE - v_start_time) * 24 * 60 * 60, 2) || 's');
RETURN v_id_comanda;
EXCEPTION
WHEN OTHERS THEN
log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
'Eroare neașteptată în importa_comanda_web', SQLERRM);
RETURN -1;
END importa_comanda_web;
END IMPORT_COMENZI;
/
-- ====================================================================
-- Tabel pentru logging (opțional - poate fi înlocuit cu logging extern)
-- ====================================================================
CREATE TABLE import_log (
id_log NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
log_time DATE DEFAULT SYSDATE,
log_level VARCHAR2(10), -- INFO, WARN, ERROR
operation VARCHAR2(50), -- GASESTE_ARTICOL, IMPORTA_COMANDA
reference_id VARCHAR2(100), -- SKU sau Nr_Comanda
message VARCHAR2(4000),
details CLOB
);
-- Index pentru căutări rapide în log
CREATE INDEX idx_import_log_time ON import_log(log_time);
CREATE INDEX idx_import_log_ref ON import_log(reference_id, operation);
-- Comentarii pentru documentație
COMMENT ON TABLE import_log IS 'Log pentru operațiile de import comenzi web';
COMMENT ON COLUMN import_log.operation IS 'Tipul operației: GASESTE_ARTICOL, IMPORTA_COMANDA, VALIDEAZA_SET';
COMMENT ON COLUMN import_log.reference_id IS 'Referința: SKU pentru articole, Nr_Comanda pentru comenzi';
-- Grant-uri pentru utilizarea package-ului
-- GRANT EXECUTE ON IMPORT_COMENZI TO PUBLIC;
-- GRANT SELECT, INSERT ON import_log TO PUBLIC;
-- ====================================================================
-- Exemple de utilizare și testare
-- ====================================================================
/*
-- Exemplu 1: Căutare articol simplu (direct în nomenclator)
SELECT * FROM TABLE(IMPORT_COMENZI.gaseste_articol_roa('CAF01', 15.50, 2));
-- Exemplu 2: Căutare articol cu reîmpachetare
SELECT * FROM TABLE(IMPORT_COMENZI.gaseste_articol_roa('CAFE100', 150.00, 1));
-- Exemplu 3: Căutare set compus
SELECT * FROM TABLE(IMPORT_COMENZI.gaseste_articol_roa('SET01', 200.00, 1));
-- Exemplu 4: Import comandă completă
DECLARE
v_json_articole CLOB := '[
{"sku": "CAF01", "cantitate": 2, "pret": 15.50},
{"sku": "CAFE100", "cantitate": 1, "pret": 150.00},
{"sku": "SET01", "cantitate": 1, "pret": 200.00}
]';
v_result NUMBER;
BEGIN
v_result := IMPORT_COMENZI.importa_comanda_web(
p_nr_comanda_ext => 'WEB-TEST-001',
p_data_comanda => SYSDATE,
p_id_partener => 12345, -- ID partener valid din sistem
p_json_articole => v_json_articole,
p_observatii => 'Test import din sistem web'
);
DBMS_OUTPUT.PUT_LINE('ID Comandă creată: ' || v_result);
END;
/
-- Interogare log pentru troubleshooting
SELECT log_time, log_level, operation, reference_id, message
FROM import_log
WHERE log_time >= SYSDATE - 1 -- Ultimele 24h
ORDER BY log_time DESC;
*/
-- ====================================================================
-- Finalizare
-- ====================================================================

View File

@@ -0,0 +1,532 @@
-- ====================================================================
-- P1-004: Package PACK_JSON pentru parsing JSON generic
-- Sistem Import Comenzi Web → ROA
-- ====================================================================
CREATE OR REPLACE PACKAGE PACK_JSON AS
-- Tipuri pentru lucrul cu JSON
TYPE t_json_array IS TABLE OF VARCHAR2(4000);
TYPE t_json_key_value IS RECORD (
key_name VARCHAR2(100),
key_value VARCHAR2(4000),
key_type VARCHAR2(20) -- 'STRING', 'NUMBER', 'BOOLEAN', 'NULL'
);
TYPE t_json_object IS TABLE OF t_json_key_value;
-- Proprietate pentru tracking erori
g_last_error VARCHAR2(4000);
-- Functie pentru accesarea ultimei erori
FUNCTION get_last_error RETURN VARCHAR2;
-- Functie pentru resetarea erorii
PROCEDURE clear_error;
-- Main parsing functions
FUNCTION parse_array(p_json_array IN CLOB) RETURN t_json_array PIPELINED; -- Parse [{"a":1},{"b":2}]
FUNCTION get_string(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN VARCHAR2; -- Get "value"
FUNCTION get_number(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN NUMBER; -- Get 123.45
FUNCTION get_boolean(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN BOOLEAN; -- Get true/false
-- Advanced functions
FUNCTION parse_object(p_json_object IN VARCHAR2) RETURN t_json_object PIPELINED; -- Parse to key-value pairs
FUNCTION clean(p_json IN CLOB) RETURN CLOB; -- Remove whitespace/formatting
-- Test functions
PROCEDURE run_tests; -- Run all built-in tests
FUNCTION test_basic_parsing RETURN VARCHAR2; -- Test basic JSON parsing
FUNCTION test_array_parsing RETURN VARCHAR2; -- Test array parsing
FUNCTION test_nested_objects RETURN VARCHAR2; -- Test nested JSON structures
FUNCTION test_error_handling RETURN VARCHAR2; -- Test error conditions
END PACK_JSON;
/
-- ====================================================================
-- Package Body - Implementarea functiilor
-- ====================================================================
CREATE OR REPLACE PACKAGE BODY PACK_JSON AS
/*
PACK_JSON - Generic JSON Parser (Oracle 10g/11g/12c compatible)
USAGE:
-- Parse array: [{"key":"val"},{"key":"val2"}]
FOR obj IN (SELECT * FROM TABLE(PACK_JSON.parse_array(json_clob))) LOOP
v_val := PACK_JSON.get_string(obj.COLUMN_VALUE, 'key');
END LOOP;
-- Get values from object: {"name":"John","age":25,"active":true}
v_name := PACK_JSON.get_string(json_obj, 'name'); -- Returns: John
v_age := PACK_JSON.get_number(json_obj, 'age'); -- Returns: 25
v_active := PACK_JSON.get_boolean(json_obj, 'active'); -- Returns: TRUE
-- Error handling:
IF PACK_JSON.get_last_error() IS NOT NULL THEN
-- Handle error: PACK_JSON.get_last_error()
PACK_JSON.clear_error();
END IF;
FUNCTIONS:
parse_array(clob) - Parse JSON array, returns table of objects
get_string(obj,key) - Extract string value from JSON object
get_number(obj,key) - Extract number value from JSON object
get_boolean(obj,key) - Extract boolean value from JSON object
get_last_error() - Get last parsing error (NULL if no error)
clear_error() - Clear error state
*/
-- ================================================================
-- Functii pentru managementul erorilor
-- ================================================================
FUNCTION get_last_error RETURN VARCHAR2 IS
BEGIN
RETURN g_last_error;
END get_last_error;
PROCEDURE clear_error IS
BEGIN
g_last_error := NULL;
END clear_error;
-- ================================================================
-- Functie utilitara pentru curatarea JSON
-- ================================================================
FUNCTION clean(
p_json IN CLOB
) RETURN CLOB IS
v_clean CLOB;
BEGIN
-- Elimina spatii, tab-uri, newline-uri pentru parsing mai usor
v_clean := REPLACE(REPLACE(REPLACE(REPLACE(p_json,
CHR(10), ''), CHR(13), ''), CHR(9), ''), ' ', '');
RETURN v_clean;
END clean;
-- ================================================================
-- Parse JSON array si returneaza fiecare obiect
-- ================================================================
FUNCTION parse_array(
p_json_array IN CLOB
) RETURN t_json_array PIPELINED IS
v_json_clean CLOB;
v_articol_json VARCHAR2(4000);
v_start_pos NUMBER := 1;
v_end_pos NUMBER;
v_bracket_count NUMBER;
BEGIN
-- Reset error
g_last_error := NULL;
-- Curata JSON-ul
v_json_clean := clean(p_json_array);
-- Elimina bracket-urile exterioare [ ]
v_json_clean := TRIM(BOTH '[]' FROM v_json_clean);
-- Parse fiecare obiect JSON din array
LOOP
-- Gaseste inceputul obiectului JSON {
v_start_pos := INSTR(v_json_clean, '{', v_start_pos);
EXIT WHEN v_start_pos = 0;
-- Gaseste sfarsitul obiectului JSON } - ia in considerare nested objects
v_bracket_count := 1;
v_end_pos := v_start_pos;
WHILE v_bracket_count > 0 AND v_end_pos < LENGTH(v_json_clean) LOOP
v_end_pos := v_end_pos + 1;
IF SUBSTR(v_json_clean, v_end_pos, 1) = '{' THEN
v_bracket_count := v_bracket_count + 1;
ELSIF SUBSTR(v_json_clean, v_end_pos, 1) = '}' THEN
v_bracket_count := v_bracket_count - 1;
END IF;
END LOOP;
-- Extrage obiectul JSON curent
IF v_bracket_count = 0 THEN
v_articol_json := SUBSTR(v_json_clean, v_start_pos, v_end_pos - v_start_pos + 1);
PIPE ROW(v_articol_json);
-- Trece la urmatorul articol
v_start_pos := v_end_pos + 1;
ELSE
-- JSON malformat
g_last_error := 'JSON malformat - bracket-uri neechilibrate';
EXIT;
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la parsing array: ' || SQLERRM;
END parse_array;
-- ================================================================
-- Extrage valoare string din obiect JSON
-- ================================================================
FUNCTION get_string(
p_json_object IN VARCHAR2,
p_key_name IN VARCHAR2
) RETURN VARCHAR2 IS
v_result VARCHAR2(4000);
BEGIN
-- Pattern: "key_name":"value"
v_result := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":"([^"]*)"', 1, 1, NULL, 1);
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la extragere string pentru ' || p_key_name || ': ' || SQLERRM;
RETURN NULL;
END get_string;
-- ================================================================
-- Extrage valoare numerica din obiect JSON
-- ================================================================
FUNCTION get_number(
p_json_object IN VARCHAR2,
p_key_name IN VARCHAR2
) RETURN NUMBER IS
v_result_str VARCHAR2(100);
v_result NUMBER;
BEGIN
-- Pattern: "key_name":123.45 sau "key_name":"123.45"
-- Incearca mai intai fara quotes
v_result_str := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":([0-9.]+)', 1, 1, NULL, 1);
-- Daca nu gaseste, incearca cu quotes
IF v_result_str IS NULL THEN
v_result_str := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":"([0-9.]+)"', 1, 1, NULL, 1);
END IF;
IF v_result_str IS NOT NULL THEN
v_result := TO_NUMBER(v_result_str);
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la extragere number pentru ' || p_key_name || ': ' || SQLERRM;
RETURN NULL;
END get_number;
-- ================================================================
-- Extrage valoare boolean din obiect JSON
-- ================================================================
FUNCTION get_boolean(
p_json_object IN VARCHAR2,
p_key_name IN VARCHAR2
) RETURN BOOLEAN IS
v_result_str VARCHAR2(10);
BEGIN
-- Pattern: "key_name":true/false
v_result_str := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":(true|false)', 1, 1, NULL, 1);
IF v_result_str = 'true' THEN
RETURN TRUE;
ELSIF v_result_str = 'false' THEN
RETURN FALSE;
ELSE
RETURN NULL;
END IF;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la extragere boolean pentru ' || p_key_name || ': ' || SQLERRM;
RETURN NULL;
END get_boolean;
-- ================================================================
-- Parse complet obiect JSON in structura cheie-valoare
-- ================================================================
FUNCTION parse_object(
p_json_object IN VARCHAR2
) RETURN t_json_object PIPELINED IS
v_clean_json VARCHAR2(4000);
v_key VARCHAR2(100);
v_value VARCHAR2(4000);
v_result t_json_key_value;
v_pos NUMBER := 1;
v_key_start NUMBER;
v_key_end NUMBER;
v_value_start NUMBER;
v_value_end NUMBER;
BEGIN
-- Curata JSON-ul si elimina { }
v_clean_json := TRIM(BOTH '{}' FROM REPLACE(p_json_object, ' ', ''));
-- Parse fiecare pereche key:value
WHILE v_pos < LENGTH(v_clean_json) LOOP
-- Gaseste cheia
v_key_start := INSTR(v_clean_json, '"', v_pos);
EXIT WHEN v_key_start = 0;
v_key_end := INSTR(v_clean_json, '"', v_key_start + 1);
EXIT WHEN v_key_end = 0;
v_key := SUBSTR(v_clean_json, v_key_start + 1, v_key_end - v_key_start - 1);
-- Gaseste valoarea
v_value_start := INSTR(v_clean_json, ':', v_key_end);
EXIT WHEN v_value_start = 0;
v_value_start := v_value_start + 1;
-- Determina tipul si extrage valoarea
IF SUBSTR(v_clean_json, v_value_start, 1) = '"' THEN
-- String value
v_value_end := INSTR(v_clean_json, '"', v_value_start + 1);
v_value := SUBSTR(v_clean_json, v_value_start + 1, v_value_end - v_value_start - 1);
v_result.key_type := 'STRING';
v_pos := v_value_end + 1;
ELSE
-- Number, boolean sau null
v_value_end := NVL(INSTR(v_clean_json, ',', v_value_start), LENGTH(v_clean_json) + 1);
v_value := SUBSTR(v_clean_json, v_value_start, v_value_end - v_value_start);
IF v_value IN ('true', 'false') THEN
v_result.key_type := 'BOOLEAN';
ELSIF v_value = 'null' THEN
v_result.key_type := 'NULL';
ELSIF REGEXP_LIKE(v_value, '^[0-9.]+$') THEN
v_result.key_type := 'NUMBER';
ELSE
v_result.key_type := 'UNKNOWN';
END IF;
v_pos := v_value_end + 1;
END IF;
v_result.key_name := v_key;
v_result.key_value := v_value;
PIPE ROW(v_result);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la parsing obiect: ' || SQLERRM;
END parse_object;
-- ================================================================
-- Functii de testare
-- ================================================================
FUNCTION test_basic_parsing RETURN VARCHAR2 IS
v_test_json VARCHAR2(1000) := '{"name":"John","age":25,"active":true,"score":98.5}';
v_name VARCHAR2(100);
v_age NUMBER;
v_active BOOLEAN;
v_score NUMBER;
v_result VARCHAR2(4000) := 'BASIC_PARSING: ';
BEGIN
clear_error();
v_name := get_string(v_test_json, 'name');
v_age := get_number(v_test_json, 'age');
v_active := get_boolean(v_test_json, 'active');
v_score := get_number(v_test_json, 'score');
-- Validate results
IF v_name = 'John' AND v_age = 25 AND v_active = TRUE AND v_score = 98.5 THEN
v_result := v_result || 'PASS - All values extracted correctly';
ELSE
v_result := v_result || 'FAIL - Values: name=' || v_name || ', age=' || v_age || ', score=' || v_score;
END IF;
IF get_last_error() IS NOT NULL THEN
v_result := v_result || ' ERROR: ' || get_last_error();
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'BASIC_PARSING: EXCEPTION - ' || SQLERRM;
END test_basic_parsing;
FUNCTION test_array_parsing RETURN VARCHAR2 IS
v_test_array CLOB := '[{"sku":"PROD1","price":10.5},{"sku":"PROD2","price":25.0}]';
v_count NUMBER := 0;
v_sku VARCHAR2(100);
v_price NUMBER;
v_result VARCHAR2(4000) := 'ARRAY_PARSING: ';
BEGIN
clear_error();
FOR obj IN (SELECT * FROM TABLE(parse_array(v_test_array))) LOOP
v_count := v_count + 1;
v_sku := get_string(obj.COLUMN_VALUE, 'sku');
v_price := get_number(obj.COLUMN_VALUE, 'price');
IF v_count = 1 THEN
IF v_sku != 'PROD1' OR v_price != 10.5 THEN
RETURN v_result || 'FAIL - First object: sku=' || v_sku || ', price=' || v_price;
END IF;
ELSIF v_count = 2 THEN
IF v_sku != 'PROD2' OR v_price != 25.0 THEN
RETURN v_result || 'FAIL - Second object: sku=' || v_sku || ', price=' || v_price;
END IF;
END IF;
END LOOP;
IF v_count = 2 THEN
v_result := v_result || 'PASS - Parsed ' || v_count || ' objects correctly';
ELSE
v_result := v_result || 'FAIL - Expected 2 objects, got ' || v_count;
END IF;
IF get_last_error() IS NOT NULL THEN
v_result := v_result || ' ERROR: ' || get_last_error();
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'ARRAY_PARSING: EXCEPTION - ' || SQLERRM;
END test_array_parsing;
FUNCTION test_nested_objects RETURN VARCHAR2 IS
v_test_nested CLOB := '[{"order":{"id":123,"items":[{"sku":"A1","qty":2}],"total":25.50}},{"order":{"id":124,"items":[{"sku":"B1","qty":1},{"sku":"C1","qty":3}],"total":45.00}}]';
v_count NUMBER := 0;
v_object VARCHAR2(4000);
v_order_id NUMBER;
v_total NUMBER;
v_result VARCHAR2(4000) := 'NESTED_OBJECTS: ';
v_order_json VARCHAR2(2000);
BEGIN
clear_error();
-- Test parsing array cu nested objects
FOR obj IN (SELECT * FROM TABLE(parse_array(v_test_nested))) LOOP
v_count := v_count + 1;
v_object := obj.COLUMN_VALUE;
-- Extrage nested object "order"
v_order_json := REGEXP_SUBSTR(v_object, '"order":\{([^}]+)\}', 1, 1, NULL, 1);
IF v_order_json IS NULL THEN
-- Incearca sa gaseasca tot nested object-ul (mai complex)
v_order_json := REGEXP_SUBSTR(v_object, '"order":\{.*\}', 1, 1);
-- Elimina "order": din fata
v_order_json := REGEXP_REPLACE(v_order_json, '^"order":', '');
END IF;
IF v_order_json IS NOT NULL THEN
v_order_id := get_number(v_order_json, 'id');
v_total := get_number(v_order_json, 'total');
IF v_count = 1 THEN
IF v_order_id != 123 OR v_total != 25.50 THEN
RETURN v_result || 'FAIL - First nested: id=' || v_order_id || ', total=' || v_total;
END IF;
ELSIF v_count = 2 THEN
IF v_order_id != 124 OR v_total != 45.00 THEN
RETURN v_result || 'FAIL - Second nested: id=' || v_order_id || ', total=' || v_total;
END IF;
END IF;
ELSE
RETURN v_result || 'FAIL - Could not extract nested order object from: ' || SUBSTR(v_object, 1, 100);
END IF;
END LOOP;
IF v_count = 2 THEN
v_result := v_result || 'PASS - Parsed ' || v_count || ' nested objects correctly';
ELSE
v_result := v_result || 'FAIL - Expected 2 nested objects, got ' || v_count;
END IF;
IF get_last_error() IS NOT NULL THEN
v_result := v_result || ' ERROR: ' || get_last_error();
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'NESTED_OBJECTS: EXCEPTION - ' || SQLERRM;
END test_nested_objects;
FUNCTION test_error_handling RETURN VARCHAR2 IS
v_result VARCHAR2(4000) := 'ERROR_HANDLING: ';
v_invalid_json VARCHAR2(1000) := '{"broken":}';
v_value VARCHAR2(100);
BEGIN
clear_error();
-- Test invalid JSON
v_value := get_string(v_invalid_json, 'broken');
-- Test non-existent key
v_value := get_string('{"valid":"json"}', 'nonexistent');
IF get_last_error() IS NOT NULL THEN
v_result := v_result || 'PASS - Error properly captured: ' || SUBSTR(get_last_error(), 1, 100);
clear_error();
ELSE
v_result := v_result || 'FAIL - No error captured for invalid operations';
END IF;
-- Test error clearing
IF get_last_error() IS NULL THEN
v_result := v_result || ' - Error cleared successfully';
ELSE
v_result := v_result || ' - Error not cleared properly';
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'ERROR_HANDLING: EXCEPTION - ' || SQLERRM;
END test_error_handling;
PROCEDURE run_tests IS
v_test_result VARCHAR2(4000);
BEGIN
DBMS_OUTPUT.PUT_LINE('=== PACK_JSON Test Suite ===');
DBMS_OUTPUT.PUT_LINE('');
-- Test 1: Basic parsing
v_test_result := test_basic_parsing();
DBMS_OUTPUT.PUT_LINE(v_test_result);
-- Test 2: Array parsing
v_test_result := test_array_parsing();
DBMS_OUTPUT.PUT_LINE(v_test_result);
-- Test 3: Nested objects
v_test_result := test_nested_objects();
DBMS_OUTPUT.PUT_LINE(v_test_result);
-- Test 4: Error handling
v_test_result := test_error_handling();
DBMS_OUTPUT.PUT_LINE(v_test_result);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('=== Test Suite Complete ===');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR in run_tests: ' || SQLERRM);
END run_tests;
END PACK_JSON;
/
-- ====================================================================
-- Grant-uri pentru utilizarea package-ului
-- ====================================================================
-- GRANT EXECUTE ON PACK_JSON TO PUBLIC;

View File

@@ -0,0 +1,367 @@
-- ====================================================================
-- P1-003: Package IMPORT_COMENZI pentru import comenzi web -> ROA
-- Sistem Import Comenzi Web -> ROA
-- ====================================================================
-- Package pentru importul comenzilor web cu mapari complexe SKU -> CODMAT
CREATE OR REPLACE PACKAGE PACK_IMPORT_COMENZI AS
-- Tipuri pentru returnarea rezultatelor
TYPE t_articol_result IS RECORD (
id_articol NUMBER,
codmat VARCHAR2(50),
cantitate_roa NUMBER,
pret_unitar NUMBER,
success NUMBER,
error_message VARCHAR2(4000)
);
TYPE t_articol_table IS TABLE OF t_articol_result;
-- Functie pentru gasirea/maparea articolelor ROA
FUNCTION gaseste_articol_roa(
p_sku IN VARCHAR2,
p_pret_web IN NUMBER DEFAULT NULL,
p_cantitate_web IN NUMBER DEFAULT 1
) RETURN t_articol_table PIPELINED;
-- Functie pentru importul complet al unei comenzi web
FUNCTION importa_comanda_web(
p_nr_comanda_ext IN VARCHAR2,
p_data_comanda IN DATE,
p_id_partener IN NUMBER,
p_json_articole IN CLOB, -- JSON array cu articolele
p_id_adresa_livrare IN NUMBER DEFAULT NULL,
p_observatii IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER; -- Returneaza ID_COMANDA sau -1 pentru eroare
END PACK_IMPORT_COMENZI;
/
-- ====================================================================
-- Package Body - Implementarea functiilor
-- ====================================================================
CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
-- Constante pentru configurare
c_id_gestiune CONSTANT NUMBER := 1;
c_id_sectie CONSTANT NUMBER := 1;
c_id_pol CONSTANT NUMBER := NULL;
c_id_util CONSTANT NUMBER := -3; -- Sistem
c_interna CONSTANT NUMBER := 0; -- Externe
-- Procedura interna pentru validarea seturilor
FUNCTION valideaza_set(p_sku IN VARCHAR2) RETURN BOOLEAN IS
v_suma_procent NUMBER := 0;
v_count_articole NUMBER := 0;
BEGIN
SELECT NVL(SUM(procent_pret), 0), COUNT(*)
INTO v_suma_procent, v_count_articole
FROM articole_terti
WHERE sku = p_sku
AND activ = 1;
-- Validari logice pentru seturi
IF v_count_articole > 1 THEN
-- Set compus - suma procentelor trebuie sa fie intre 95-105% (toleranta)
IF v_suma_procent < 95 OR v_suma_procent > 105 THEN
pINFO('WARN VALIDEAZA_SET ' || p_sku || ': Suma procente nelogica: ' || v_suma_procent || '%', 'IMPORT_COMENZI');
RETURN FALSE;
END IF;
ELSIF v_count_articole = 1 THEN
-- Reimpachetare - procentul trebuie sa fie 100%
IF v_suma_procent != 100 THEN
pINFO('WARN VALIDEAZA_SET ' || p_sku || ': Reimpachetare cu procent != 100%: ' || v_suma_procent || '%', 'IMPORT_COMENZI');
RETURN FALSE;
END IF;
END IF;
RETURN TRUE;
END valideaza_set;
-- ================================================================
-- Functia principala pentru gasirea articolelor ROA
-- ================================================================
FUNCTION gaseste_articol_roa(
p_sku IN VARCHAR2,
p_pret_web IN NUMBER DEFAULT NULL,
p_cantitate_web IN NUMBER DEFAULT 1
) RETURN t_articol_table PIPELINED IS
v_result t_articol_result;
v_found_mapping BOOLEAN := FALSE;
v_id_articol NUMBER;
-- Cursor pentru maparile din ARTICOLE_TERTI
CURSOR c_mapari IS
SELECT at.codmat, at.cantitate_roa, at.procent_pret,
na.id_articol
FROM articole_terti at
JOIN nom_articole na ON na.codmat = at.codmat
WHERE at.sku = p_sku
AND at.activ = 1
ORDER BY at.procent_pret DESC; -- Articolele principale primul
BEGIN
pINFO('GASESTE_ARTICOL ' || p_sku || ': Cautare articol pentru SKU: ' || p_sku, 'IMPORT_COMENZI');
-- Initializare rezultat
v_result.success := 0;
v_result.error_message := NULL;
-- STEP 1: Verifica maparile speciale din ARTICOLE_TERTI
FOR rec IN c_mapari LOOP
v_found_mapping := TRUE;
v_result.id_articol := rec.id_articol;
v_result.codmat := rec.codmat;
v_result.cantitate_roa := rec.cantitate_roa * p_cantitate_web;
-- Calculeaza pretul unitar pe baza procentului alocat
IF p_pret_web IS NOT NULL THEN
v_result.pret_unitar := (p_pret_web * rec.procent_pret / 100) / rec.cantitate_roa;
ELSE
-- Fara pret web, setam 0 (va fi necesar sa fie furnizat)
v_result.pret_unitar := 0;
END IF;
v_result.success := 1;
pINFO('GASESTE_ARTICOL ' || p_sku || ': Mapare gasita: ' || rec.codmat ||
', Cant: ' || v_result.cantitate_roa ||
', Pret: ' || v_result.pret_unitar, 'IMPORT_COMENZI');
PIPE ROW(v_result);
END LOOP;
-- STEP 2: Daca nu s-au gasit mapari speciale, cauta direct in nom_articole
IF NOT v_found_mapping THEN
BEGIN
SELECT id_articol, codmat
INTO v_result.id_articol, v_result.codmat
FROM nom_articole
WHERE codmat = p_sku;
v_result.cantitate_roa := p_cantitate_web;
-- Pentru cautare directa, foloseste pretul din web daca este furnizat
IF p_pret_web IS NOT NULL THEN
v_result.pret_unitar := p_pret_web;
END IF;
v_result.success := 1;
pINFO('GASESTE_ARTICOL ' || p_sku || ': Gasit direct in nomenclator: ' || v_result.codmat, 'IMPORT_COMENZI');
PIPE ROW(v_result);
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_result.success := 0;
v_result.error_message := 'SKU nu a fost gasit nici in ARTICOLE_TERTI, nici in nom_articole: ' || p_sku;
pINFO('ERROR GASESTE_ARTICOL ' || p_sku || ': ' || v_result.error_message, 'IMPORT_COMENZI');
PIPE ROW(v_result);
WHEN TOO_MANY_ROWS THEN
v_result.success := 0;
v_result.error_message := 'Multiple articole gasite pentru SKU: ' || p_sku;
pINFO('ERROR GASESTE_ARTICOL ' || p_sku || ': ' || v_result.error_message, 'IMPORT_COMENZI');
PIPE ROW(v_result);
END;
ELSE
-- Valideaza seturile dupa ce au fost returnate toate maparile
IF NOT valideaza_set(p_sku) THEN
pINFO('WARN GASESTE_ARTICOL ' || p_sku || ': Set cu configuratie suspecta - verifica procentele', 'IMPORT_COMENZI');
END IF;
END IF;
EXCEPTION
WHEN OTHERS THEN
v_result.success := 0;
v_result.error_message := 'Eroare neasteptata: ' || SQLERRM;
pINFO('ERROR GASESTE_ARTICOL ' || p_sku || ': Eroare neasteptata: ' || SQLERRM, 'IMPORT_COMENZI');
PIPE ROW(v_result);
END gaseste_articol_roa;
-- ================================================================
-- Functia pentru importul complet al unei comenzi web
-- ================================================================
FUNCTION importa_comanda_web(
p_nr_comanda_ext IN VARCHAR2,
p_data_comanda IN DATE,
p_id_partener IN NUMBER,
p_json_articole IN CLOB,
p_id_adresa_livrare IN NUMBER DEFAULT NULL,
p_observatii IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER IS
v_id_comanda NUMBER;
v_data_livrare DATE;
v_sku VARCHAR2(100);
v_cantitate_web NUMBER;
v_pret_web NUMBER;
v_articole_procesate NUMBER := 0;
v_articole_eroare NUMBER := 0;
v_start_time DATE;
v_json_pos NUMBER := 1;
v_json_end NUMBER;
v_json_item CLOB;
BEGIN
v_start_time := SYSDATE;
pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Incepere import comanda pentru partener: ' || p_id_partener, 'IMPORT_COMENZI');
-- Validari de baza
IF p_nr_comanda_ext IS NULL OR p_id_partener IS NULL THEN
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Parametri obligatorii lipsa', 'IMPORT_COMENZI');
RETURN -1;
END IF;
-- Verifica daca comanda nu exista deja
BEGIN
SELECT id_comanda INTO v_id_comanda
FROM comenzi
WHERE comanda_externa = p_nr_comanda_ext
AND sters = 0;
pINFO('WARN IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Comanda exista deja cu ID: ' || v_id_comanda, 'IMPORT_COMENZI');
RETURN v_id_comanda; -- Returneaza ID-ul comenzii existente
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL; -- Normal, comanda nu exista
END;
-- Calculeaza data de livrare (comanda + 1 zi)
v_data_livrare := p_data_comanda + 1;
-- STEP 1: Creeaza comanda folosind package-ul existent
BEGIN
v_id_comanda := PACK_COMENZI.adauga_comanda(
p_nr_comanda => p_nr_comanda_ext,
p_data_comanda => p_data_comanda,
p_id_partener => p_id_partener,
p_data_livrare => v_data_livrare,
p_id_gestiune => c_id_gestiune,
p_id_sectie => c_id_sectie,
p_interna => c_interna,
p_id_util => c_id_util,
p_comanda_externa => p_nr_comanda_ext,
p_id_adresa_livrare => p_id_adresa_livrare,
p_observatii => p_observatii
);
IF v_id_comanda IS NULL OR v_id_comanda <= 0 THEN
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': PACK_COMENZI.adauga_comanda a returnat ID invalid', 'IMPORT_COMENZI');
RETURN -1;
END IF;
pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Comanda creata cu ID: ' || v_id_comanda, 'IMPORT_COMENZI');
EXCEPTION
WHEN OTHERS THEN
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la crearea comenzii: ' || SQLERRM, 'IMPORT_COMENZI');
RETURN -1;
END;
-- STEP 2: Proceseaza articolele din JSON folosind PACK_JSON
-- Asteapta format JSON: [{"sku":"ABC","cantitate":1,"pret":10.5},{"sku":"DEF","cantitate":2,"pret":20.0}]
DECLARE
v_articol_json VARCHAR2(4000);
v_articol_count NUMBER := 0;
BEGIN
-- Parse JSON array folosind package-ul generic
FOR json_obj IN (
SELECT * FROM TABLE(PACK_JSON.parse_array(p_json_articole))
) LOOP
v_articol_count := v_articol_count + 1;
v_articol_json := json_obj.COLUMN_VALUE;
BEGIN
-- Extrage datele folosind functiile PACK_JSON
v_sku := PACK_JSON.get_string(v_articol_json, 'sku');
v_cantitate_web := PACK_JSON.get_number(v_articol_json, 'cantitate');
v_pret_web := PACK_JSON.get_number(v_articol_json, 'pret');
pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Procesez articol ' || v_articol_count || ': ' || v_sku || ', cant: ' || v_cantitate_web || ', pret: ' || v_pret_web, 'IMPORT_COMENZI');
-- STEP 3: Gaseste maparile pentru acest SKU
FOR art_rec IN (
SELECT * FROM TABLE(gaseste_articol_roa(v_sku, v_pret_web, v_cantitate_web))
) LOOP
IF art_rec.success = 1 THEN
-- Adauga articolul la comanda
BEGIN
PACK_COMENZI.adauga_articol_comanda(
p_id_comanda => v_id_comanda,
p_id_articol => art_rec.id_articol,
p_cantitate => art_rec.cantitate_roa,
p_pret => art_rec.pret_unitar,
p_id_pol => c_id_pol,
p_id_util => c_id_util
);
v_articole_procesate := v_articole_procesate + 1;
pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Articol adaugat: ' || art_rec.codmat ||
', cant: ' || art_rec.cantitate_roa ||
', pret: ' || art_rec.pret_unitar, 'IMPORT_COMENZI');
EXCEPTION
WHEN OTHERS THEN
v_articole_eroare := v_articole_eroare + 1;
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la adaugare articol ' || art_rec.codmat || ': ' || SQLERRM, 'IMPORT_COMENZI');
END;
ELSE
v_articole_eroare := v_articole_eroare + 1;
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': SKU nu a putut fi mapat: ' || v_sku || ' - ' || art_rec.error_message, 'IMPORT_COMENZI');
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
v_articole_eroare := v_articole_eroare + 1;
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la procesarea articolului ' || v_articol_count || ': ' || SQLERRM, 'IMPORT_COMENZI');
END;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la parsarea JSON: ' || SQLERRM, 'IMPORT_COMENZI');
RETURN -1;
END;
-- Verifica daca s-au procesat articole cu succes
IF v_articole_procesate = 0 THEN
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Niciun articol nu a fost procesat cu succes', 'IMPORT_COMENZI');
RETURN -1;
END IF;
-- Log sumar final
pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Import finalizat - ID comanda: ' || v_id_comanda ||
', Articole procesate: ' || v_articole_procesate ||
', Articole cu erori: ' || v_articole_eroare ||
', Timp procesare: ' || ROUND((SYSDATE - v_start_time) * 24 * 60 * 60, 2) || 's', 'IMPORT_COMENZI');
RETURN v_id_comanda;
EXCEPTION
WHEN OTHERS THEN
pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare neasteptata in importa_comanda_web: ' || SQLERRM, 'IMPORT_COMENZI');
RETURN -1;
END importa_comanda_web;
END PACK_IMPORT_COMENZI;
/
-- ====================================================================
-- Grant-uri pentru utilizarea package-ului
-- ====================================================================
-- GRANT EXECUTE ON PACK_IMPORT_COMENZI TO PUBLIC;