Files
gomag-vending/api/database-scripts/06_pack_import_comenzi.pck
Claude Agent c534a972a9 feat: multi-gestiune stock verification setting
Replace single-select gestiune dropdown with multi-select checkboxes.
Settings stores comma-separated IDs, Python builds IN clause with bind
variables, Oracle PL/SQL splits CSV via REGEXP_SUBSTR for stock lookup.
Empty selection = all warehouses (unchanged behavior).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 16:15:40 +00:00

352 lines
15 KiB
Plaintext

-- ====================================================================
-- 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"}
-- Optional per articol: "id_pol":"5" — politica de pret specifica
-- (pentru transport/discount cu politica separata de cea a comenzii)
-- 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
-- Variabila package pentru ultima eroare (pentru orchestrator VFP)
g_last_error VARCHAR2(4000);
-- Procedura pentru importul complet al unei comenzi
PROCEDURE 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_id_pol IN NUMBER DEFAULT NULL,
p_id_sectie IN NUMBER DEFAULT NULL,
p_id_gestiune IN VARCHAR2 DEFAULT NULL,
v_id_comanda OUT NUMBER);
-- Functii pentru managementul erorilor (pentru orchestrator VFP)
FUNCTION get_last_error RETURN VARCHAR2;
PROCEDURE clear_error;
END PACK_IMPORT_COMENZI;
/
CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
-- Constante pentru configurare
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;
-- ================================================================
-- Functie helper: selecteaza id_articol corect pentru un CODMAT
-- Prioritate: sters=0 AND inactiv=0, preferinta stoc, MAX(id_articol) fallback
-- ================================================================
FUNCTION resolve_id_articol(p_codmat IN VARCHAR2, p_id_gest IN VARCHAR2) RETURN NUMBER IS
v_result NUMBER;
BEGIN
IF p_id_gest IS NOT NULL THEN
-- Cu gestiuni specifice (CSV: "1,3") — split in subquery pentru IN clause
BEGIN
SELECT id_articol INTO v_result FROM (
SELECT na.id_articol
FROM nom_articole na
WHERE na.codmat = p_codmat AND na.sters = 0 AND na.inactiv = 0
ORDER BY
CASE WHEN EXISTS (
SELECT 1 FROM stoc s
WHERE s.id_articol = na.id_articol
AND s.id_gestiune IN (
SELECT TO_NUMBER(REGEXP_SUBSTR(p_id_gest, '[^,]+', 1, LEVEL))
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT(p_id_gest, ',') + 1
)
AND s.an = EXTRACT(YEAR FROM SYSDATE)
AND s.luna = EXTRACT(MONTH FROM SYSDATE)
AND s.cants + s.cant - s.cante > 0
) THEN 0 ELSE 1 END,
na.id_articol DESC
) WHERE ROWNUM = 1;
EXCEPTION WHEN NO_DATA_FOUND THEN v_result := NULL;
END;
ELSE
-- Fara gestiune — cauta stoc in orice gestiune
BEGIN
SELECT id_articol INTO v_result FROM (
SELECT na.id_articol
FROM nom_articole na
WHERE na.codmat = p_codmat AND na.sters = 0 AND na.inactiv = 0
ORDER BY
CASE WHEN EXISTS (
SELECT 1 FROM stoc s
WHERE s.id_articol = na.id_articol
AND s.an = EXTRACT(YEAR FROM SYSDATE)
AND s.luna = EXTRACT(MONTH FROM SYSDATE)
AND s.cants + s.cant - s.cante > 0
) THEN 0 ELSE 1 END,
na.id_articol DESC
) WHERE ROWNUM = 1;
EXCEPTION WHEN NO_DATA_FOUND THEN v_result := NULL;
END;
END IF;
RETURN v_result;
END resolve_id_articol;
-- ================================================================
-- Procedura principala pentru importul unei comenzi
-- ================================================================
PROCEDURE 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_id_pol IN NUMBER DEFAULT NULL,
p_id_sectie IN NUMBER DEFAULT NULL,
p_id_gestiune IN VARCHAR2 DEFAULT NULL,
v_id_comanda OUT NUMBER) IS
v_data_livrare DATE;
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_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;
v_id_pol_articol NUMBER; -- id_pol per articol (din JSON), prioritar fata de p_id_pol
-- pljson
l_json_articole CLOB := p_json_articole;
v_json_arr pljson_list;
v_json_obj pljson;
BEGIN
-- 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
INTO v_id_comanda
FROM comenzi
WHERE comanda_externa = p_nr_comanda_ext
AND sters = 0;
IF v_id_comanda IS NOT NULL THEN
GOTO sfarsit;
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
PACK_COMENZI.adauga_comanda(V_NR_COMANDA => p_nr_comanda_ext,
V_DATA_COMANDA => p_data_comanda,
V_ID => p_id_partener,
V_DATA_LIVRARE => v_data_livrare,
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,
V_COMANDA_EXTERNA => p_nr_comanda_ext,
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;
-- 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_json_obj := pljson(v_json_arr.get(i));
BEGIN
-- 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'));
-- id_pol per articol (optional, pentru transport/discount cu politica separata)
BEGIN
v_id_pol_articol := TO_NUMBER(v_json_obj.get_string('id_pol'));
EXCEPTION
WHEN OTHERS THEN v_id_pol_articol := NULL;
END;
-- 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
FROM articole_terti at
WHERE at.sku = v_sku
AND at.activ = 1
AND at.sters = 0
ORDER BY at.procent_pret DESC) LOOP
v_found_mapping := TRUE;
v_id_articol := resolve_id_articol(rec.codmat, p_id_gestiune);
IF v_id_articol IS NULL THEN
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'Articol activ negasit pentru CODMAT: ' || rec.codmat;
CONTINUE;
END IF;
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 => v_id_articol,
V_ID_POL => NVL(v_id_pol_articol, 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) ||
'Eroare adaugare articol ' || rec.codmat || ': ' || SQLERRM;
END;
END LOOP;
-- Daca nu s-a gasit mapare, cauta direct in NOM_ARTICOLE via resolve_id_articol
IF NOT v_found_mapping THEN
v_id_articol := resolve_id_articol(v_sku, p_id_gestiune);
IF v_id_articol IS NULL THEN
v_articole_eroare := v_articole_eroare + 1;
g_last_error := g_last_error || CHR(10) ||
'SKU negasit in ARTICOLE_TERTI si NOM_ARTICOLE (activ): ' || v_sku;
ELSE
v_codmat := v_sku;
v_pret_unitar := NVL(v_pret_web, 0);
BEGIN
PACK_COMENZI.adauga_articol_comanda(V_ID_COMANDA => v_id_comanda,
V_ID_ARTICOL => v_id_articol,
V_ID_POL => NVL(v_id_pol_articol, 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 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 IF;
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;
/