- Remove nfjson/nfjsonread.FXP from git tracking - Add Python cache patterns (__pycache__/, *.py[cod], *$py.class) - Add environment file patterns (.env, .env.local, .env.*.local) - Reorganize project structure with VFP files moved to vfp/ directory - Add comprehensive database scripts and documentation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
463 lines
18 KiB
SQL
463 lines
18 KiB
SQL
-- ====================================================================
|
|
-- 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
|
|
-- ==================================================================== |