Replace import_orders (insert-per-run) with orders table (one row per order, upsert on conflict). Eliminates dedup CTE on every dashboard query and prevents unbounded row growth at 4-500 orders/sync. Key changes: - orders table: PK order_number, upsert via ON CONFLICT DO UPDATE; COALESCE preserves id_comanda once set; times_skipped auto-increments - sync_run_orders: lightweight junction (sync_run_id, order_number) replaces sync_run_id column on orders - order_items: PK changed to (order_number, sku), INSERT OR IGNORE - Auto-migration in init_sqlite(): import_orders → orders on first boot, old table renamed to import_orders_bak - /api/dashboard/orders: period_days param (3/7/30/0=all, default 7) - Dashboard: period selector buttons in orders card header - start.sh: stop existing process on port 5003 before restart; remove --reload (broken on WSL2 /mnt/e/) - Add invoice_service, E2E Playwright tests, Oracle package updates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
284 lines
12 KiB
Plaintext
284 lines
12 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"}
|
|
-- 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,
|
|
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;
|
|
|
|
-- ================================================================
|
|
-- 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,
|
|
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;
|
|
|
|
-- 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'));
|
|
|
|
-- 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
|
|
AND at.sters = 0
|
|
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) ||
|
|
'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 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;
|
|
/
|