-- ==================================================================== -- 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; -- Variabila package pentru ultima eroare (pentru orchestrator VFP) g_last_error VARCHAR2(4000); -- 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 FUNCTION importa_comanda( 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_id_adresa_facturare IN NUMBER DEFAULT NULL, p_observatii IN VARCHAR2 DEFAULT NULL ) RETURN NUMBER; -- Returneaza ID_COMANDA sau -1 pentru eroare -- Functii pentru managementul erorilor (similar cu PACK_JSON) FUNCTION get_last_error RETURN VARCHAR2; PROCEDURE clear_error; 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 := 2; -- Comenzi de la client (web) -- ================================================================ -- Functii helper 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; -- ================================================================ -- Functii interne -- ================================================================ -- 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 null; -- 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 -- ================================================================ FUNCTION importa_comanda( 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_id_adresa_facturare 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; -- Resetare eroare la inceputul procesarii clear_error; -- Validari de baza IF p_nr_comanda_ext IS NULL OR p_id_partener IS NULL THEN g_last_error := 'IMPORTA_COMANDA ' || NVL(p_nr_comanda_ext, 'NULL') || ': Parametri obligatorii lipsa'; 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 versiunea overloaded cu OUT parameter BEGIN -- Apeleaza procedura adauga_comanda care returneaza ID_COMANDA prin OUT PACK_COMENZI.adauga_comanda( V_NR_COMANDA => p_nr_comanda_ext, V_DATA_COMANDA => p_data_comanda, V_ID => p_id_partener, -- ID_PART V_DATA_LIVRARE => v_data_livrare, V_PROC_DISCOUNT => 0, -- Fara discount implicit V_INTERNA => c_interna, V_ID_UTIL => c_id_util, V_ID_SECTIE => c_id_sectie, V_ID_ADRESA_FACTURARE => p_id_adresa_facturare, V_ID_ADRESA_LIVRARE => p_id_adresa_livrare, V_ID_CODCLIENT => NULL, -- Nu folosim cod client V_COMANDA_EXTERNA => p_nr_comanda_ext, V_ID_CTR => NULL, -- Nu avem contract V_ID_COMANDA => v_id_comanda -- OUT parameter cu ID_COMANDA ); IF v_id_comanda IS NULL OR v_id_comanda <= 0 THEN g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': PACK_COMENZI.adauga_comanda a returnat ID invalid'; RETURN -1; END IF; -- pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Comanda creata cu ID: ' || v_id_comanda, 'IMPORT_COMENZI'); EXCEPTION WHEN OTHERS THEN g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la crearea comenzii: ' || SQLERRM; 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( V_ID_COMANDA => v_id_comanda, V_ID_ARTICOL => art_rec.id_articol, V_ID_POL => c_id_pol, V_CANTITATE => art_rec.cantitate_roa, V_PRET => art_rec.pret_unitar, V_ID_UTIL => c_id_util, V_ID_SECTIE => c_id_sectie ); 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 g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la parsarea JSON: ' || SQLERRM; RETURN -1; END; -- Verifica daca s-au procesat articole cu succes IF v_articole_procesate = 0 THEN g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Niciun articol nu a fost procesat cu succes'; 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 g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare neasteptata in importa_comanda: ' || SQLERRM; RETURN -1; END importa_comanda; END PACK_IMPORT_COMENZI; / -- ==================================================================== -- Grant-uri pentru utilizarea package-ului -- ==================================================================== -- GRANT EXECUTE ON PACK_IMPORT_COMENZI TO PUBLIC;