actualizare

This commit is contained in:
2026-03-10 15:23:12 +02:00
parent 6f988db1f9
commit 8e94c05901
3 changed files with 164 additions and 102 deletions

1
.gitignore vendored
View File

@@ -24,3 +24,4 @@ settings.ini
vfp/settings.ini
vfp/output/
vfp/*.json
*.~pck

75
SPECIFICATIE_CONTRACT.txt Normal file
View File

@@ -0,0 +1,75 @@
SPECIFICATIE PROIECT - IMPORT COMENZI WEB IN ROA ORACLE
Data: 5 martie 2026
================================================================================
DESCRIERE SCOP
================================================================================
Implementarea unui sistem automat de import a comenzilor de pe platforme web
(GoMag si altele) in sistemul ERP ROA Oracle. Sistemul va prelua comenzi,
va realiza mapari de articole, va converte unitati de masura si va crea
comenzi in ROA automat.
================================================================================
DELIVERABLES
================================================================================
1. Logica de import completa in baza de date ROA Oracle
2. Orchestrator automat (cron job) pentru sincronizare comenzi
3. Interfata web de configurare mapari SKU-uri
4. Suport pentru articole compuse (mapari complexe)
5. Conversii unitati de masura intre platforme
6. Documentatie tehnica si handover
7. Support 3 luni pentru bug fixes
================================================================================
EFORTURI SI COSTURI
================================================================================
Lucrat deja: 20h 1,200 EUR
De lucrat: 60h 3,600 EUR
Support 3 luni: 24h 1,440 EUR
TOTAL IMPLEMENTARE: 80h 4,800 EUR
TOTAL CU SUPPORT: 104h 6,240 EUR
Tarif orar: 60 EUR/h
================================================================================
INCLUS IN PRET
================================================================================
- Analiza si integrare cu baza de date client
- Testare completa cu date reale
- Integrare in sistemul ROA Oracle
- Validari si controale de integritate
- Documentation si training
- Support de 3 luni pentru probleme critice
================================================================================
CONDITII GENERALE
================================================================================
Duratie proiect: 2-4 saptamani
Payment terms: 50% avans, 50% la finalizare
Garantie: 3 luni (bug fixes gratuit)
Suport suplimentar: 60 EUR/h (dupa perioada garantie)
Buffer estimare: 50% (pentru integrare ROA + incertitudini)
================================================================================
RESPONSABILITATI CLIENT
================================================================================
- Acces la baza de date client si ROA Oracle
- Accesul la comenzile din platforma web
- Clarificarea logicii maparii articole compuse
- Testing si validare in mediu pilot
================================================================================

View File

@@ -1,40 +1,35 @@
-- ====================================================================
-- P1-004: Package PACK_JSON pentru parsing JSON generic
-- Sistem Import Comenzi Web → ROA
-- ====================================================================
CREATE OR REPLACE PACKAGE PACK_JSON AS
-- Tipuri pentru lucrul cu JSON
TYPE t_json_array IS TABLE OF VARCHAR2(4000);
TYPE t_json_key_value IS RECORD (
key_name VARCHAR2(100),
key_value VARCHAR2(4000),
key_type VARCHAR2(20) -- 'STRING', 'NUMBER', 'BOOLEAN', 'NULL'
);
TYPE t_json_object IS TABLE OF t_json_key_value;
-- Proprietate pentru tracking erori
g_last_error VARCHAR2(4000);
-- Functie pentru accesarea ultimei erori
FUNCTION get_last_error RETURN VARCHAR2;
-- Functie pentru resetarea erorii
PROCEDURE clear_error;
-- Main parsing functions
FUNCTION parse_array(p_json_array IN CLOB) RETURN t_json_array PIPELINED; -- Parse [{"a":1},{"b":2}]
FUNCTION get_string(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN VARCHAR2; -- Get "value"
FUNCTION get_string(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN VARCHAR2; -- Get "value"
FUNCTION get_number(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN NUMBER; -- Get 123.45
FUNCTION get_boolean(p_json_object IN VARCHAR2, p_key_name IN VARCHAR2) RETURN BOOLEAN; -- Get true/false
-- Advanced functions
-- Advanced functions
FUNCTION parse_object(p_json_object IN VARCHAR2) RETURN t_json_object PIPELINED; -- Parse to key-value pairs
FUNCTION clean(p_json IN CLOB) RETURN CLOB; -- Remove whitespace/formatting
-- Test functions
PROCEDURE run_tests; -- Run all built-in tests
FUNCTION test_basic_parsing RETURN VARCHAR2; -- Test basic JSON parsing
@@ -44,10 +39,6 @@ CREATE OR REPLACE PACKAGE PACK_JSON AS
END PACK_JSON;
/
-- ====================================================================
-- Package Body - Implementarea functiilor
-- ====================================================================
CREATE OR REPLACE PACKAGE BODY PACK_JSON AS
/*
PACK_JSON - Generic JSON Parser (Oracle 10g/11g/12c compatible)
@@ -57,12 +48,12 @@ USAGE:
FOR obj IN (SELECT * FROM TABLE(PACK_JSON.parse_array(json_clob))) LOOP
v_val := PACK_JSON.get_string(obj.COLUMN_VALUE, 'key');
END LOOP;
-- Get values from object: {"name":"John","age":25,"active":true}
v_name := PACK_JSON.get_string(json_obj, 'name'); -- Returns: John
v_age := PACK_JSON.get_number(json_obj, 'age'); -- Returns: 25
v_age := PACK_JSON.get_number(json_obj, 'age'); -- Returns: 25
v_active := PACK_JSON.get_boolean(json_obj, 'active'); -- Returns: TRUE
-- Error handling:
IF PACK_JSON.get_last_error() IS NOT NULL THEN
-- Handle error: PACK_JSON.get_last_error()
@@ -72,7 +63,7 @@ USAGE:
FUNCTIONS:
parse_array(clob) - Parse JSON array, returns table of objects
get_string(obj,key) - Extract string value from JSON object
get_number(obj,key) - Extract number value from JSON object
get_number(obj,key) - Extract number value from JSON object
get_boolean(obj,key) - Extract boolean value from JSON object
get_last_error() - Get last parsing error (NULL if no error)
clear_error() - Clear error state
@@ -85,7 +76,7 @@ FUNCTIONS:
BEGIN
RETURN g_last_error;
END get_last_error;
PROCEDURE clear_error IS
BEGIN
g_last_error := NULL;
@@ -100,9 +91,9 @@ FUNCTIONS:
v_clean CLOB;
BEGIN
-- Elimina spatii, tab-uri, newline-uri pentru parsing mai usor
v_clean := REPLACE(REPLACE(REPLACE(REPLACE(p_json,
v_clean := REPLACE(REPLACE(REPLACE(REPLACE(p_json,
CHR(10), ''), CHR(13), ''), CHR(9), ''), ' ', '');
RETURN v_clean;
END clean;
@@ -112,50 +103,50 @@ FUNCTIONS:
FUNCTION parse_array(
p_json_array IN CLOB
) RETURN t_json_array PIPELINED IS
v_json_clean CLOB;
v_articol_json VARCHAR2(4000);
v_start_pos NUMBER := 1;
v_end_pos NUMBER;
v_bracket_count NUMBER;
BEGIN
-- Reset error
g_last_error := NULL;
-- Curata JSON-ul
v_json_clean := clean(p_json_array);
-- Elimina bracket-urile exterioare [ ]
v_json_clean := TRIM(BOTH '[]' FROM v_json_clean);
-- Parse fiecare obiect JSON din array
LOOP
-- Gaseste inceputul obiectului JSON {
v_start_pos := INSTR(v_json_clean, '{', v_start_pos);
EXIT WHEN v_start_pos = 0;
-- Gaseste sfarsitul obiectului JSON } - ia in considerare nested objects
v_bracket_count := 1;
v_end_pos := v_start_pos;
WHILE v_bracket_count > 0 AND v_end_pos < LENGTH(v_json_clean) LOOP
v_end_pos := v_end_pos + 1;
IF SUBSTR(v_json_clean, v_end_pos, 1) = '{' THEN
v_bracket_count := v_bracket_count + 1;
ELSIF SUBSTR(v_json_clean, v_end_pos, 1) = '}' THEN
v_bracket_count := v_bracket_count - 1;
END IF;
END LOOP;
-- Extrage obiectul JSON curent
IF v_bracket_count = 0 THEN
v_articol_json := SUBSTR(v_json_clean, v_start_pos, v_end_pos - v_start_pos + 1);
PIPE ROW(v_articol_json);
-- Trece la urmatorul articol
v_start_pos := v_end_pos + 1;
ELSE
@@ -164,8 +155,8 @@ FUNCTIONS:
EXIT;
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la parsing array: ' || SQLERRM;
@@ -181,16 +172,16 @@ FUNCTIONS:
v_result VARCHAR2(4000);
BEGIN
-- Oracle 10g compatible: Extract string values
v_result := REGEXP_SUBSTR(p_json_object,
v_result := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":"[^"]*"');
IF v_result IS NOT NULL THEN
-- Remove key part and quotes manually
v_result := REGEXP_REPLACE(v_result, '^"' || p_key_name || '":"', '');
v_result := REGEXP_REPLACE(v_result, '"$', '');
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la extragere string pentru ' || p_key_name || ': ' || SQLERRM;
@@ -209,23 +200,23 @@ FUNCTIONS:
BEGIN
-- Oracle 10g compatible: Extract number values without subexpressions
-- Pattern: "key_name":123.45 (numeric value direct)
v_result_str := REGEXP_SUBSTR(p_json_object,
v_result_str := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":[0-9]+\.?[0-9]*');
IF v_result_str IS NOT NULL THEN
-- Extract just the number part after the colon
v_result_str := REGEXP_SUBSTR(v_result_str, '[0-9]+\.?[0-9]*');
END IF;
-- Daca nu gaseste, incearca cu quotes: "key_name":"123.45"
IF v_result_str IS NULL OR LENGTH(TRIM(v_result_str)) = 0 THEN
v_result_str := REGEXP_SUBSTR(p_json_object,
v_result_str := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":"[0-9]+\.?[0-9]*"');
IF v_result_str IS NOT NULL THEN
-- Extract number between quotes
v_result_str := REGEXP_SUBSTR(v_result_str, '[0-9]+\.?[0-9]*');
END IF;
END IF;
IF v_result_str IS NOT NULL AND LENGTH(TRIM(v_result_str)) > 0 THEN
BEGIN
v_result_str := TRIM(v_result_str);
@@ -243,9 +234,9 @@ FUNCTIONS:
END;
END;
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la extragere number pentru ' || p_key_name || ': ' || SQLERRM;
@@ -262,13 +253,13 @@ FUNCTIONS:
v_result_str VARCHAR2(100);
BEGIN
-- Oracle 10g compatible: Extract boolean values
v_result_str := REGEXP_SUBSTR(p_json_object,
v_result_str := REGEXP_SUBSTR(p_json_object,
'"' || p_key_name || '":(true|false)');
IF v_result_str IS NOT NULL THEN
-- Extract just the boolean value
v_result_str := REGEXP_REPLACE(v_result_str, '^"' || p_key_name || '":', '');
END IF;
IF v_result_str = 'true' THEN
RETURN TRUE;
ELSIF v_result_str = 'false' THEN
@@ -276,7 +267,7 @@ FUNCTIONS:
ELSE
RETURN NULL;
END IF;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la extragere boolean pentru ' || p_key_name || ': ' || SQLERRM;
@@ -289,7 +280,7 @@ FUNCTIONS:
FUNCTION parse_object(
p_json_object IN VARCHAR2
) RETURN t_json_object PIPELINED IS
v_clean_json VARCHAR2(4000);
v_key VARCHAR2(100);
v_value VARCHAR2(4000);
@@ -299,27 +290,27 @@ FUNCTIONS:
v_key_end NUMBER;
v_value_start NUMBER;
v_value_end NUMBER;
BEGIN
-- Curata JSON-ul si elimina { }
v_clean_json := TRIM(BOTH '{}' FROM REPLACE(p_json_object, ' ', ''));
-- Parse fiecare pereche key:value
WHILE v_pos < LENGTH(v_clean_json) LOOP
-- Gaseste cheia
v_key_start := INSTR(v_clean_json, '"', v_pos);
EXIT WHEN v_key_start = 0;
v_key_end := INSTR(v_clean_json, '"', v_key_start + 1);
EXIT WHEN v_key_end = 0;
v_key := SUBSTR(v_clean_json, v_key_start + 1, v_key_end - v_key_start - 1);
-- Gaseste valoarea
v_value_start := INSTR(v_clean_json, ':', v_key_end);
EXIT WHEN v_value_start = 0;
v_value_start := v_value_start + 1;
-- Determina tipul si extrage valoarea
IF SUBSTR(v_clean_json, v_value_start, 1) = '"' THEN
-- String value
@@ -331,7 +322,7 @@ FUNCTIONS:
-- Number, boolean sau null
v_value_end := NVL(INSTR(v_clean_json, ',', v_value_start), LENGTH(v_clean_json) + 1);
v_value := SUBSTR(v_clean_json, v_value_start, v_value_end - v_value_start);
IF v_value IN ('true', 'false') THEN
v_result.key_type := 'BOOLEAN';
ELSIF v_value = 'null' THEN
@@ -341,16 +332,16 @@ FUNCTIONS:
ELSE
v_result.key_type := 'UNKNOWN';
END IF;
v_pos := v_value_end + 1;
END IF;
v_result.key_name := v_key;
v_result.key_value := v_value;
PIPE ROW(v_result);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'Eroare la parsing obiect: ' || SQLERRM;
@@ -359,7 +350,7 @@ FUNCTIONS:
-- ================================================================
-- Functii de testare
-- ================================================================
FUNCTION test_basic_parsing RETURN VARCHAR2 IS
v_test_json VARCHAR2(1000) := '{"name":"John","age":25,"active":true,"score":98.5}';
v_name VARCHAR2(100);
@@ -369,29 +360,29 @@ FUNCTIONS:
v_result VARCHAR2(4000) := 'BASIC_PARSING: ';
BEGIN
clear_error();
v_name := get_string(v_test_json, 'name');
v_age := get_number(v_test_json, 'age');
v_active := get_boolean(v_test_json, 'active');
v_score := get_number(v_test_json, 'score');
-- Validate results
IF v_name = 'John' AND v_age = 25 AND v_active = TRUE AND v_score = 98.5 THEN
v_result := v_result || 'PASS - All values extracted correctly';
ELSE
v_result := v_result || 'FAIL - Values: name=' || v_name || ', age=' || v_age || ', score=' || v_score;
END IF;
IF get_last_error() IS NOT NULL THEN
v_result := v_result || ' ERROR: ' || get_last_error();
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'BASIC_PARSING: EXCEPTION - ' || SQLERRM;
END test_basic_parsing;
FUNCTION test_array_parsing RETURN VARCHAR2 IS
v_test_array CLOB := '[{"sku":"PROD1","price":10.5},{"sku":"PROD2","price":25.0}]';
v_count NUMBER := 0;
@@ -400,12 +391,12 @@ FUNCTIONS:
v_result VARCHAR2(4000) := 'ARRAY_PARSING: ';
BEGIN
clear_error();
FOR obj IN (SELECT * FROM TABLE(parse_array(v_test_array))) LOOP
v_count := v_count + 1;
v_sku := get_string(obj.COLUMN_VALUE, 'sku');
v_price := get_number(obj.COLUMN_VALUE, 'price');
IF v_count = 1 THEN
IF v_sku != 'PROD1' OR v_price != 10.5 THEN
RETURN v_result || 'FAIL - First object: sku=' || v_sku || ', price=' || v_price;
@@ -416,23 +407,23 @@ FUNCTIONS:
END IF;
END IF;
END LOOP;
IF v_count = 2 THEN
v_result := v_result || 'PASS - Parsed ' || v_count || ' objects correctly';
ELSE
v_result := v_result || 'FAIL - Expected 2 objects, got ' || v_count;
END IF;
IF get_last_error() IS NOT NULL THEN
v_result := v_result || ' ERROR: ' || get_last_error();
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'ARRAY_PARSING: EXCEPTION - ' || SQLERRM;
END test_array_parsing;
FUNCTION test_nested_objects RETURN VARCHAR2 IS
v_test_nested CLOB := '[{"order":{"id":123,"items":[{"sku":"A1","qty":2}],"total":25.50}},{"order":{"id":124,"items":[{"sku":"B1","qty":1},{"sku":"C1","qty":3}],"total":45.00}}]';
v_count NUMBER := 0;
@@ -443,16 +434,16 @@ FUNCTIONS:
v_order_json VARCHAR2(2000);
BEGIN
clear_error();
-- Test parsing array cu nested objects
FOR obj IN (SELECT * FROM TABLE(parse_array(v_test_nested))) LOOP
v_count := v_count + 1;
v_object := obj.COLUMN_VALUE;
-- Extrage nested object "order" (Oracle 10g compatible)
v_order_json := REGEXP_SUBSTR(v_object, '"order":\{[^}]+\}');
IF v_order_json IS NOT NULL THEN
-- Extract just the object part
-- Extract just the object part
v_order_json := REGEXP_REPLACE(v_order_json, '^"order":', '');
END IF;
IF v_order_json IS NULL THEN
@@ -461,11 +452,11 @@ FUNCTIONS:
-- Elimina "order": din fata
v_order_json := REGEXP_REPLACE(v_order_json, '^"order":', '');
END IF;
IF v_order_json IS NOT NULL THEN
v_order_id := get_number(v_order_json, 'id');
v_total := get_number(v_order_json, 'total');
IF v_count = 1 THEN
IF v_order_id != 123 OR v_total != 25.50 THEN
RETURN v_result || 'FAIL - First nested: id=' || v_order_id || ', total=' || v_total;
@@ -479,30 +470,30 @@ FUNCTIONS:
RETURN v_result || 'FAIL - Could not extract nested order object from: ' || SUBSTR(v_object, 1, 100);
END IF;
END LOOP;
IF v_count = 2 THEN
v_result := v_result || 'PASS - Parsed ' || v_count || ' nested objects correctly';
ELSE
v_result := v_result || 'FAIL - Expected 2 nested objects, got ' || v_count;
END IF;
IF get_last_error() IS NOT NULL THEN
v_result := v_result || ' ERROR: ' || get_last_error();
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'NESTED_OBJECTS: EXCEPTION - ' || SQLERRM;
END test_nested_objects;
FUNCTION test_error_handling RETURN VARCHAR2 IS
v_result VARCHAR2(4000) := 'ERROR_HANDLING: ';
v_invalid_json VARCHAR2(1000) := '{"broken":}';
v_value VARCHAR2(100);
BEGIN
clear_error();
-- Force an error by trying to parse malformed array
BEGIN
FOR obj IN (SELECT * FROM TABLE(parse_array('[{"incomplete":"object"'))) LOOP
@@ -513,52 +504,52 @@ FUNCTIONS:
-- This should trigger parse_array to set g_last_error
NULL;
END;
-- Alternative: try to get a string from NULL object
v_value := get_string(NULL, 'test');
IF get_last_error() IS NOT NULL THEN
v_result := v_result || 'PASS - Error properly captured: ' || SUBSTR(get_last_error(), 1, 100);
clear_error();
ELSE
v_result := v_result || 'FAIL - No error captured for invalid operations';
END IF;
-- Test error clearing
IF get_last_error() IS NULL THEN
v_result := v_result || ' - Error cleared successfully';
ELSE
v_result := v_result || ' - Error not cleared properly';
END IF;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RETURN 'ERROR_HANDLING: EXCEPTION - ' || SQLERRM;
END test_error_handling;
PROCEDURE run_tests IS
v_test_result VARCHAR2(4000);
BEGIN
DBMS_OUTPUT.PUT_LINE('=== PACK_JSON Test Suite ===');
DBMS_OUTPUT.PUT_LINE('');
-- Test 1: Basic parsing
v_test_result := test_basic_parsing();
DBMS_OUTPUT.PUT_LINE(v_test_result);
-- Test 2: Array parsing
-- Test 2: Array parsing
v_test_result := test_array_parsing();
DBMS_OUTPUT.PUT_LINE(v_test_result);
-- Test 3: Nested objects
v_test_result := test_nested_objects();
DBMS_OUTPUT.PUT_LINE(v_test_result);
-- Test 4: Error handling
v_test_result := test_error_handling();
DBMS_OUTPUT.PUT_LINE(v_test_result);
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE('=== Test Suite Complete ===');
EXCEPTION
@@ -568,8 +559,3 @@ FUNCTIONS:
END PACK_JSON;
/
-- ====================================================================
-- Grant-uri pentru utilizarea package-ului
-- ====================================================================
-- GRANT EXECUTE ON PACK_JSON TO PUBLIC;