-- ==================================================================== -- 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 -- ====================================================================