This commit is contained in:
2026-03-11 12:32:25 +02:00
parent 8e94c05901
commit 69841872d1
6 changed files with 5781 additions and 1121 deletions

View File

@@ -1,28 +1,55 @@
-- ====================================================================
-- PACK_IMPORT_COMENZI
-- Package pentru importul comenzilor din platforme web (GoMag, etc.)
-- in sistemul ROA Oracle.
--
-- Dependinte:
-- Packages: PACK_COMENZI (adauga_comanda, adauga_articol_comanda)
-- pljson (pljson_list, pljson) - instalat in CONTAFIN_ORACLE,
-- accesat prin PUBLIC SYNONYM
-- Tabele: ARTICOLE_TERTI (mapari SKU -> CODMAT)
-- NOM_ARTICOLE (nomenclator articole ROA)
-- COMENZI (verificare duplicat comanda_externa)
--
-- Proceduri publice:
--
-- importa_comanda(...)
-- Importa o comanda completa: creeaza comanda + adauga articolele.
-- p_json_articole accepta:
-- - array JSON: [{"sku":"X","quantity":"1","price":"10","vat":"19"}, ...]
-- - obiect JSON: {"sku":"X","quantity":"1","price":"10","vat":"19"}
-- Valorile sku, quantity, price, vat sunt extrase ca STRING si convertite.
-- Daca comanda exista deja (comanda_externa), nu se dubleaza.
-- La eroare ridica RAISE_APPLICATION_ERROR(-20001, mesaj).
-- Returneaza v_id_comanda (OUT) = ID-ul comenzii create.
--
-- Logica cautare articol per SKU:
-- 1. Mapari speciale din ARTICOLE_TERTI (reimpachetare, seturi compuse)
-- - un SKU poate avea mai multe randuri (set) cu procent_pret
-- 2. Fallback: cautare directa in NOM_ARTICOLE dupa CODMAT = SKU
--
-- get_last_error / clear_error
-- Management erori pentru orchestratorul VFP.
--
-- Exemplu utilizare:
-- DECLARE
-- v_id NUMBER;
-- BEGIN
-- PACK_IMPORT_COMENZI.importa_comanda(
-- p_nr_comanda_ext => '479317993',
-- p_data_comanda => SYSDATE,
-- p_id_partener => 1424,
-- p_json_articole => '[{"sku":"5941623003366","quantity":"1.00","price":"40.99","vat":"21"}]',
-- p_id_pol => 39,
-- v_id_comanda => v_id);
-- DBMS_OUTPUT.PUT_LINE('ID comanda: ' || v_id);
-- END;
-- ====================================================================
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,
ptva 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,
p_ptva IN NUMBER)
RETURN t_articol_table
PIPELINED;
-- Procedura pentru importul complet al unei comenzi
PROCEDURE importa_comanda(p_nr_comanda_ext IN VARCHAR2,
p_data_comanda IN DATE,
@@ -34,7 +61,7 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_COMENZI AS
p_id_sectie IN NUMBER DEFAULT NULL,
v_id_comanda OUT NUMBER);
-- Functii pentru managementul erorilor (similar cu PACK_JSON)
-- Functii pentru managementul erorilor (pentru orchestrator VFP)
FUNCTION get_last_error RETURN VARCHAR2;
PROCEDURE clear_error;
@@ -43,7 +70,6 @@ END PACK_IMPORT_COMENZI;
CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
-- Constante pentru configurare
-- Nota: c_id_pol, c_id_gestiune, c_id_sectie sunt acum parametri ai procedurii importa_comanda
c_id_util CONSTANT NUMBER := -3; -- Sistem
c_interna CONSTANT NUMBER := 2; -- Comenzi de la client (web)
@@ -61,155 +87,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
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,
p_ptva IN NUMBER)
RETURN t_articol_table
PIPELINED IS
v_result t_articol_result;
v_found_mapping BOOLEAN := FALSE;
-- 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.ptva := p_ptva;
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.ptva := p_ptva;
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
-- Procedura principala pentru importul unei comenzi
-- ================================================================
PROCEDURE importa_comanda(p_nr_comanda_ext IN VARCHAR2,
p_data_comanda IN DATE,
@@ -224,32 +102,33 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
v_sku VARCHAR2(100);
v_cantitate_web NUMBER;
v_pret_web NUMBER;
v_vat NUMBER;
v_articole_procesate NUMBER := 0;
v_articole_eroare NUMBER := 0;
v_start_time DATE;
v_vat NUMBER;
v_articol_json VARCHAR2(4000);
v_articol_count NUMBER := 0;
v_articole_table t_articol_table;
v_articol_idx NUMBER;
art_rec t_articol_result;
l_json_articole CLOB := p_json_articole;
v_articol_count NUMBER := 0;
-- Variabile pentru cautare articol
v_found_mapping BOOLEAN;
v_id_articol NUMBER;
v_codmat VARCHAR2(50);
v_cantitate_roa NUMBER;
v_pret_unitar NUMBER;
-- pljson
l_json_articole CLOB := p_json_articole;
v_json_arr pljson_list;
v_json_obj pljson;
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';
GOTO SFARSIT;
END IF;
-- Verifica daca comanda nu exista deja
BEGIN
SELECT id_comanda
@@ -257,142 +136,146 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
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');
if v_id_comanda is not null then
IF v_id_comanda IS NOT NULL THEN
GOTO sfarsit;
end if;
END IF;
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
-- Apeleaza procedura adauga_comanda care returneaza ID_COMANDA prin OUT
-- STEP 1: Creeaza comanda
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_ID => p_id_partener,
V_DATA_LIVRARE => v_data_livrare,
V_PROC_DISCOUNT => 0, -- Fara discount implicit
V_PROC_DISCOUNT => 0,
V_INTERNA => c_interna,
V_ID_UTIL => c_id_util,
V_ID_SECTIE => p_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_ID_CODCLIENT => NULL,
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
);
V_ID_CTR => NULL,
V_ID_COMANDA => v_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';
GOTO sfarsit;
END IF;
-- pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Comanda creata cu ID: ' || v_id_comanda, 'IMPORT_COMENZI');
-- 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}]
-- Parse JSON array folosind package-ul generic
FOR json_obj IN (SELECT *
FROM TABLE(PACK_JSON.parse_array(l_json_articole))) LOOP
-- STEP 2: Proceseaza articolele din JSON folosind pljson
-- Suporta atat array "[{...},{...}]" cat si obiect singular "{...}"
IF LTRIM(l_json_articole) LIKE '[%' THEN
v_json_arr := pljson_list(l_json_articole);
ELSE
v_json_arr := pljson_list('[' || l_json_articole || ']');
END IF;
FOR i IN 1 .. v_json_arr.count LOOP
v_articol_count := v_articol_count + 1;
v_articol_json := json_obj.COLUMN_VALUE;
v_json_obj := pljson(v_json_arr.get(i));
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, 'quantity');
v_pret_web := PACK_JSON.get_number(v_articol_json, 'price');
v_vat := PACK_JSON.get_number(v_articol_json, 'vat');
-- 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
-- Apeleaza functia si stocheaza rezultatele
SELECT *
BULK COLLECT
INTO v_articole_table
FROM TABLE(gaseste_articol_roa(v_sku,
v_pret_web,
v_cantitate_web,
v_vat));
-- Itereaza prin rezultate
IF v_articole_table.COUNT > 0 THEN
FOR v_articol_idx IN 1 .. v_articole_table.COUNT LOOP
art_rec := v_articole_table(v_articol_idx);
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 => p_id_pol,
V_CANTITATE => art_rec.cantitate_roa,
V_PRET => art_rec.pret_unitar,
V_ID_UTIL => c_id_util,
V_ID_SECTIE => p_id_sectie,
V_PTVA => art_rec.ptva);
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;
g_last_error := g_last_error || CHR(10) ||
'ERROR IMPORTA_COMANDA ' ||
p_nr_comanda_ext ||
': Eroare la adaugare articol ' ||
art_rec.codmat || ': ' || SQLERRM;
-- pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la adaugare articol ' || art_rec.codmat || ': ' || SQLERRM, 'IMPORT_COMENZI');
END;
ELSE
-- Extrage datele folosind pljson (valorile vin ca string din json magazin web)
v_sku := v_json_obj.get_string('sku');
v_cantitate_web := TO_NUMBER(v_json_obj.get_string('quantity'));
v_pret_web := TO_NUMBER(v_json_obj.get_string('price'));
v_vat := TO_NUMBER(v_json_obj.get_string('vat'));
-- STEP 3: Gaseste articolele ROA pentru acest SKU
-- Cauta mai intai in ARTICOLE_TERTI (mapari speciale / seturi)
v_found_mapping := FALSE;
FOR rec IN (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 = v_sku
AND at.activ = 1
ORDER BY at.procent_pret DESC) LOOP
v_found_mapping := TRUE;
v_cantitate_roa := rec.cantitate_roa * v_cantitate_web;
v_pret_unitar := CASE WHEN v_pret_web IS NOT NULL
THEN (v_pret_web * rec.procent_pret / 100) / rec.cantitate_roa
ELSE 0
END;
BEGIN
PACK_COMENZI.adauga_articol_comanda(V_ID_COMANDA => v_id_comanda,
V_ID_ARTICOL => rec.id_articol,
V_ID_POL => p_id_pol,
V_CANTITATE => v_cantitate_roa,
V_PRET => v_pret_unitar,
V_ID_UTIL => c_id_util,
V_ID_SECTIE => p_id_sectie,
V_PTVA => v_vat);
v_articole_procesate := v_articole_procesate + 1;
EXCEPTION
WHEN OTHERS THEN
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'ERROR IMPORTA_COMANDA ' ||
p_nr_comanda_ext ||
': SKU nu a putut fi mapat: ' || v_sku ||
' - ' || art_rec.error_message;
-- 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; -- End v_articol_idx loop
ELSE
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'WARN IMPORTA_COMANDA ' || p_nr_comanda_ext ||
': Niciun articol gasit pentru SKU: ' ||
v_sku;
-- pINFO('WARN IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Niciun articol gasit pentru SKU: ' || v_sku, 'IMPORT_COMENZI');
g_last_error := g_last_error || CHR(10) ||
'Eroare adaugare articol ' || rec.codmat || ': ' || SQLERRM;
END;
END LOOP;
-- Daca nu s-a gasit mapare, cauta direct in NOM_ARTICOLE
IF NOT v_found_mapping THEN
BEGIN
SELECT id_articol, codmat
INTO v_id_articol, v_codmat
FROM nom_articole
WHERE codmat = v_sku;
v_pret_unitar := NVL(v_pret_web, 0);
PACK_COMENZI.adauga_articol_comanda(V_ID_COMANDA => v_id_comanda,
V_ID_ARTICOL => v_id_articol,
V_ID_POL => p_id_pol,
V_CANTITATE => v_cantitate_web,
V_PRET => v_pret_unitar,
V_ID_UTIL => c_id_util,
V_ID_SECTIE => p_id_sectie,
V_PTVA => v_vat);
v_articole_procesate := v_articole_procesate + 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'SKU negasit in ARTICOLE_TERTI si NOM_ARTICOLE: ' || v_sku;
WHEN TOO_MANY_ROWS THEN
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'Multiple articole gasite pentru SKU: ' || v_sku;
WHEN OTHERS THEN
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'Eroare adaugare articol ' || v_sku || ' (CODMAT: ' || v_codmat || '): ' || SQLERRM;
END;
END IF;
END; -- End DECLARE block pentru v_articole_table
END; -- End BEGIN block pentru articol individual
END LOOP;
-- Verifica daca s-au procesat articole cu succes
IF v_articole_procesate = 0 THEN
g_last_error := g_last_error || CHR(10) || 'IMPORTA_COMANDA ' ||
p_nr_comanda_ext ||
': Niciun articol nu a fost procesat cu succes';
END IF;
<<SFARSIT>>
IF g_last_error IS NOT NULL THEN
RAISE_APPLICATION_ERROR(-20001, g_last_error);
END IF;
END importa_comanda;
END PACK_IMPORT_COMENZI;