diff --git a/.gitignore b/.gitignore
index 9a94db3..21ea954 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,7 @@ __pycache__/
.env
.env.local
.env.*.local
+
+# Settings files with secrets
+settings.ini
+vfp/settings.ini
diff --git a/api/database-scripts/02_import_parteneri.sql b/api/database-scripts/02_import_parteneri.sql
index 503d7b4..bffd876 100644
--- a/api/database-scripts/02_import_parteneri.sql
+++ b/api/database-scripts/02_import_parteneri.sql
@@ -56,7 +56,8 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_PARTENERI AS
-- ====================================================================
/**
- * Functia principala pentru cautarea sau crearea unui partener
+ * Procedura principala pentru cautarea sau crearea unui partener
+ * SCHIMBAT din FUNCTION in PROCEDURE pentru compatibilitate cu DML operations
*
* Algoritm:
* 1. Cauta dupa cod_fiscal (daca > 3 caractere)
@@ -70,16 +71,17 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_PARTENERI AS
* @param p_telefon Numar de telefon
* @param p_email Adresa de email
* @param p_is_persoana_juridica 1=persoana juridica, 0=persoana fizica, NULL=auto-detect prin CNP
- * @return ID_PART al partenerului gasit sau creat
+ * @param p_id_partener OUT ID_PART al partenerului gasit sau creat
*/
- FUNCTION cauta_sau_creeaza_partener(
+ PROCEDURE cauta_sau_creeaza_partener(
p_cod_fiscal IN VARCHAR2,
p_denumire IN VARCHAR2,
p_adresa IN VARCHAR2 DEFAULT NULL,
p_telefon IN VARCHAR2 DEFAULT NULL,
p_email IN VARCHAR2 DEFAULT NULL,
- p_is_persoana_juridica IN NUMBER DEFAULT NULL
- ) RETURN NUMBER;
+ p_is_persoana_juridica IN NUMBER DEFAULT NULL,
+ p_id_partener OUT NUMBER
+ );
/**
* Parseaza o adresa din format semicolon in componentele individuale
@@ -509,14 +511,15 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
p_sector := C_SECTOR_DEFAULT;
END parseaza_adresa_semicolon;
- FUNCTION cauta_sau_creeaza_partener(
+ PROCEDURE cauta_sau_creeaza_partener(
p_cod_fiscal IN VARCHAR2,
p_denumire IN VARCHAR2,
p_adresa IN VARCHAR2 DEFAULT NULL,
p_telefon IN VARCHAR2 DEFAULT NULL,
p_email IN VARCHAR2 DEFAULT NULL,
- p_is_persoana_juridica IN NUMBER DEFAULT NULL
- ) RETURN NUMBER IS
+ p_is_persoana_juridica IN NUMBER DEFAULT NULL,
+ p_id_partener OUT NUMBER
+ ) IS
v_id_part NUMBER;
v_id_adresa NUMBER;
@@ -546,7 +549,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
-- Validare date input
IF NOT valideaza_date_partener(p_cod_fiscal, p_denumire) THEN
g_last_error := 'Date partener invalide - validare esuata';
- RETURN -1;
+ p_id_partener := -1;
+ RETURN;
END IF;
v_cod_fiscal_curat := TRIM(p_cod_fiscal);
@@ -559,7 +563,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
IF v_id_part IS NOT NULL THEN
-- pINFO('Partener gasit dupa cod_fiscal. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
-- pINFO('=== SFARSIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
- RETURN v_id_part;
+ p_id_partener := v_id_part;
+ RETURN;
END IF;
END IF;
@@ -569,7 +574,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
IF v_id_part IS NOT NULL THEN
-- pINFO('Partener gasit dupa denumire. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
-- pINFO('=== SFARSIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
- RETURN v_id_part;
+ p_id_partener := v_id_part;
+ RETURN;
END IF;
-- STEP 3: Creare partener nou
@@ -641,7 +647,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
IF v_id_part IS NULL OR v_id_part <= 0 THEN
g_last_error := 'pack_def.adauga_partener a returnat ID invalid';
- RETURN -1;
+ p_id_partener := -1;
+ RETURN;
END IF;
-- pINFO('Partener creat cu succes. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
@@ -649,7 +656,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'ERROR la crearea partenerului prin pack_def: ' || SQLERRM;
- RETURN -1;
+ p_id_partener := -1;
+ RETURN;
END;
-- STEP 4: Adaugare adresa (daca exista)
@@ -708,12 +716,12 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
-- pINFO('Partener creat complet. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
-- pINFO('=== SFARSIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
- RETURN v_id_part;
+ p_id_partener := v_id_part;
EXCEPTION
WHEN OTHERS THEN
g_last_error := 'ERROR NEASTEPTAT in cauta_sau_creeaza_partener: ' || SQLERRM;
- RETURN -1;
+ p_id_partener := -1;
END cauta_sau_creeaza_partener;
diff --git a/docs/LLM_PROJECT_MANAGER_PROMPT.md b/docs/LLM_PROJECT_MANAGER_PROMPT.md
index 1f7e8b5..2f622b7 100644
--- a/docs/LLM_PROJECT_MANAGER_PROMPT.md
+++ b/docs/LLM_PROJECT_MANAGER_PROMPT.md
@@ -69,7 +69,7 @@ Creează story-uri pentru:
### **PHASE 2: VFP Integration (Ziua 2)**
Creează story-uri pentru:
-- Adaptare gomag-vending-test.prg pentru JSON output
+- Adaptare gomag-adapter.prg pentru JSON output
- Orchestrator sync-comenzi-web.prg cu timer
- Integrare Oracle packages în VFP
- Sistem de logging cu rotație
diff --git a/docs/PRD.md b/docs/PRD.md
index 6047f81..78c2977 100644
--- a/docs/PRD.md
+++ b/docs/PRD.md
@@ -106,7 +106,7 @@ CREATE TABLE ARTICOLE_TERTI (
**Responsabilități:**
- Rulare automată (timer 5 minute)
-- Citire comenzi din JSON-ul generat de gomag-vending.prg
+- Citire comenzi din JSON-ul generat de gomag-adapter.prg
- Procesare comenzi GoMag cu mapare completă la Oracle
- Apelare package-uri Oracle pentru import
- Logging în fișiere text cu timestamp
@@ -286,7 +286,7 @@ ENDIF
- [ ] 🔄 **P1-004:** Testare manuală package-uri (NEXT UP!)
### Phase 2: VFP Integration (Ziua 2)
-- [ ] **P2-001:** Adaptare gomag-vending.prg pentru output JSON (READY - doar activare GetOrders)
+- [ ] **P2-001:** Adaptare gomag-adapter.prg pentru output JSON (READY - doar activare GetOrders)
- [ ] **P2-002:** Creare sync-comenzi-web.prg cu toate helper functions
- [ ] **P2-003:** Testare import comenzi end-to-end cu date reale GoMag
- [ ] **P2-004:** Configurare logging și error handling complet
diff --git a/vfp/ApplicationSetup.prg b/vfp/ApplicationSetup.prg
new file mode 100644
index 0000000..9a25ebc
--- /dev/null
+++ b/vfp/ApplicationSetup.prg
@@ -0,0 +1,288 @@
+*-- ApplicationSetup.prg - Clasa pentru configurarea si setup-ul aplicatiei
+*-- Contine toate functiile pentru settings.ini si configurare
+*-- Autor: Claude AI
+*-- Data: 10 septembrie 2025
+
+DEFINE CLASS ApplicationSetup AS Custom
+
+ *-- Proprietati publice
+ cAppPath = ""
+ cIniFile = ""
+ oSettings = NULL
+ lInitialized = .F.
+
+ *-- Constructor
+ PROCEDURE Init
+ PARAMETERS tcAppPath
+ IF !EMPTY(tcAppPath)
+ THIS.cAppPath = ADDBS(tcAppPath)
+ ELSE
+ THIS.cAppPath = ADDBS(JUSTPATH(SYS(16,0)))
+ ENDIF
+
+ THIS.cIniFile = THIS.cAppPath + "settings.ini"
+ THIS.lInitialized = .F.
+ ENDPROC
+
+ *-- Functie pentru incarcarea tuturor setarilor din fisierul INI
+ PROCEDURE LoadSettings
+ PARAMETERS tcIniFile
+ LOCAL loSettings
+
+ IF EMPTY(tcIniFile)
+ tcIniFile = THIS.cIniFile
+ ENDIF
+
+ *-- Cream un obiect pentru toate setarile
+ loSettings = CREATEOBJECT("Empty")
+
+ *-- Sectiunea API
+ ADDPROPERTY(loSettings, "ApiBaseUrl", ReadPini("API", "ApiBaseUrl", tcIniFile))
+ ADDPROPERTY(loSettings, "OrderApiUrl", ReadPini("API", "OrderApiUrl", tcIniFile))
+ ADDPROPERTY(loSettings, "ApiKey", ReadPini("API", "ApiKey", tcIniFile))
+ ADDPROPERTY(loSettings, "ApiShop", ReadPini("API", "ApiShop", tcIniFile))
+ ADDPROPERTY(loSettings, "UserAgent", ReadPini("API", "UserAgent", tcIniFile))
+ ADDPROPERTY(loSettings, "ContentType", ReadPini("API", "ContentType", tcIniFile))
+
+ *-- Sectiunea PAGINATION
+ ADDPROPERTY(loSettings, "Limit", VAL(ReadPini("PAGINATION", "Limit", tcIniFile)))
+
+ *-- Sectiunea OPTIONS
+ ADDPROPERTY(loSettings, "GetProducts", ReadPini("OPTIONS", "GetProducts", tcIniFile) = "1")
+ ADDPROPERTY(loSettings, "GetOrders", ReadPini("OPTIONS", "GetOrders", tcIniFile) = "1")
+
+ *-- Sectiunea FILTERS
+ ADDPROPERTY(loSettings, "OrderDaysBack", VAL(ReadPini("FILTERS", "OrderDaysBack", tcIniFile)))
+
+ *-- Sectiunea ORACLE - pentru conexiunea la database
+ ADDPROPERTY(loSettings, "OracleUser", ReadPini("ORACLE", "OracleUser", tcIniFile))
+ ADDPROPERTY(loSettings, "OraclePassword", ReadPini("ORACLE", "OraclePassword", tcIniFile))
+ ADDPROPERTY(loSettings, "OracleDSN", ReadPini("ORACLE", "OracleDSN", tcIniFile))
+
+ *-- Sectiunea SYNC - pentru configurarea sincronizarii
+ ADDPROPERTY(loSettings, "AdapterProgram", ReadPini("SYNC", "AdapterProgram", tcIniFile))
+ ADDPROPERTY(loSettings, "JsonFilePattern", ReadPini("SYNC", "JsonFilePattern", tcIniFile))
+ ADDPROPERTY(loSettings, "AutoRunAdapter", ReadPini("SYNC", "AutoRunAdapter", tcIniFile) = "1")
+
+ *-- Salvare in proprietatea clasei
+ THIS.oSettings = loSettings
+
+ RETURN loSettings
+ ENDPROC
+
+ *-- Functie pentru crearea unui fisier INI implicit cu setari de baza
+ PROCEDURE CreateDefaultIni
+ PARAMETERS tcIniFile
+ LOCAL llSuccess
+
+ IF EMPTY(tcIniFile)
+ tcIniFile = THIS.cIniFile
+ ENDIF
+
+ llSuccess = .T.
+
+ TRY
+ *-- Sectiunea API
+ WritePini("API", "ApiBaseUrl", "https://api.gomag.ro/api/v1/product/read/json?enabled=1", tcIniFile)
+ WritePini("API", "OrderApiUrl", "https://api.gomag.ro/api/v1/order/read/json", tcIniFile)
+ WritePini("API", "ApiKey", "YOUR_API_KEY_HERE", tcIniFile)
+ WritePini("API", "ApiShop", "https://yourstore.gomag.ro", tcIniFile)
+ WritePini("API", "UserAgent", "Mozilla/5.0", tcIniFile)
+ WritePini("API", "ContentType", "application/json", tcIniFile)
+
+ *-- Sectiunea PAGINATION
+ WritePini("PAGINATION", "Limit", "100", tcIniFile)
+
+ *-- Sectiunea OPTIONS
+ WritePini("OPTIONS", "GetProducts", "1", tcIniFile)
+ WritePini("OPTIONS", "GetOrders", "1", tcIniFile)
+
+ *-- Sectiunea FILTERS
+ WritePini("FILTERS", "OrderDaysBack", "7", tcIniFile)
+
+ *-- Sectiunea ORACLE - conexiune database
+ WritePini("ORACLE", "OracleUser", "MARIUSM_AUTO", tcIniFile)
+ WritePini("ORACLE", "OraclePassword", "ROMFASTSOFT", tcIniFile)
+ WritePini("ORACLE", "OracleDSN", "ROA_CENTRAL", tcIniFile)
+
+ *-- Sectiunea SYNC - configurare sincronizare
+ WritePini("SYNC", "AdapterProgram", "gomag-adapter.prg", tcIniFile)
+ WritePini("SYNC", "JsonFilePattern", "gomag_orders*.json", tcIniFile)
+ WritePini("SYNC", "AutoRunAdapter", "1", tcIniFile)
+
+ CATCH
+ llSuccess = .F.
+ ENDTRY
+
+ RETURN llSuccess
+ ENDPROC
+
+ *-- Functie pentru validarea setarilor obligatorii
+ PROCEDURE ValidateSettings
+ PARAMETERS toSettings
+ LOCAL llValid, lcErrors
+
+ IF ISNULL(toSettings)
+ toSettings = THIS.oSettings
+ ENDIF
+
+ IF ISNULL(toSettings)
+ RETURN .F.
+ ENDIF
+
+ llValid = .T.
+ lcErrors = ""
+
+ *-- Verificare setari API obligatorii
+ IF EMPTY(toSettings.ApiKey) OR toSettings.ApiKey = "YOUR_API_KEY_HERE"
+ llValid = .F.
+ lcErrors = lcErrors + "ApiKey nu este setat corect in settings.ini" + CHR(13) + CHR(10)
+ ENDIF
+
+ IF EMPTY(toSettings.ApiShop) OR "yourstore.gomag.ro" $ toSettings.ApiShop
+ llValid = .F.
+ lcErrors = lcErrors + "ApiShop nu este setat corect in settings.ini" + CHR(13) + CHR(10)
+ ENDIF
+
+ *-- Verificare setari Oracle obligatorii (doar pentru sync)
+ IF TYPE('toSettings.OracleUser') = 'C' AND EMPTY(toSettings.OracleUser)
+ llValid = .F.
+ lcErrors = lcErrors + "OracleUser nu este setat in settings.ini" + CHR(13) + CHR(10)
+ ENDIF
+
+ IF TYPE('toSettings.OracleDSN') = 'C' AND EMPTY(toSettings.OracleDSN)
+ llValid = .F.
+ lcErrors = lcErrors + "OracleDSN nu este setat in settings.ini" + CHR(13) + CHR(10)
+ ENDIF
+
+ *-- Log erorile daca exista
+ IF !llValid AND TYPE('gcLogFile') = 'C'
+ LogMessage("Erori validare settings.ini:", "ERROR", gcLogFile)
+ LogMessage(lcErrors, "ERROR", gcLogFile)
+ ENDIF
+
+ RETURN llValid
+ ENDPROC
+
+ *-- Functie pentru configurarea initiala a aplicatiei
+ PROCEDURE Setup
+ LOCAL llSetupOk
+
+ llSetupOk = .T.
+
+ *-- Verificare existenta settings.ini
+ IF !CheckIniFile(THIS.cIniFile)
+ IF TYPE('gcLogFile') = 'C'
+ LogMessage("ATENTIE: Fisierul settings.ini nu a fost gasit!", "WARN", gcLogFile)
+ LogMessage("Cream un fisier settings.ini implicit...", "INFO", gcLogFile)
+ ENDIF
+
+ IF THIS.CreateDefaultIni()
+ IF TYPE('gcLogFile') = 'C'
+ LogMessage("Fisier settings.ini creat cu succes.", "INFO", gcLogFile)
+ LogMessage("IMPORTANT: Modifica setarile din settings.ini (ApiKey, ApiShop) inainte de a rula scriptul din nou!", "INFO", gcLogFile)
+ ENDIF
+ llSetupOk = .F. && Opreste executia pentru a permite configurarea
+ ELSE
+ IF TYPE('gcLogFile') = 'C'
+ LogMessage("EROARE: Nu s-a putut crea fisierul settings.ini!", "ERROR", gcLogFile)
+ ENDIF
+ llSetupOk = .F.
+ ENDIF
+ ENDIF
+
+ *-- Incarca setarile daca setup-ul este OK
+ IF llSetupOk
+ THIS.LoadSettings()
+ THIS.lInitialized = .T.
+ ENDIF
+
+ RETURN llSetupOk
+ ENDPROC
+
+ *-- Functie pentru afisarea informatiilor despre configuratie
+ PROCEDURE DisplaySettingsInfo
+ PARAMETERS toSettings
+ LOCAL lcInfo
+
+ IF ISNULL(toSettings)
+ toSettings = THIS.oSettings
+ ENDIF
+
+ IF ISNULL(toSettings)
+ RETURN .F.
+ ENDIF
+
+ IF TYPE('gcLogFile') != 'C'
+ RETURN .F.
+ ENDIF
+
+ lcInfo = "=== CONFIGURATIE APLICATIE ==="
+ LogMessage(lcInfo, "INFO", gcLogFile)
+
+ *-- API Settings
+ LogMessage("API: " + toSettings.ApiShop, "INFO", gcLogFile)
+ LogMessage("Orders Days Back: " + TRANSFORM(toSettings.OrderDaysBack), "INFO", gcLogFile)
+ LogMessage("Get Products: " + IIF(toSettings.GetProducts, "DA", "NU"), "INFO", gcLogFile)
+ LogMessage("Get Orders: " + IIF(toSettings.GetOrders, "DA", "NU"), "INFO", gcLogFile)
+
+ *-- Oracle Settings (doar daca exista)
+ IF TYPE('toSettings.OracleUser') = 'C' AND !EMPTY(toSettings.OracleUser)
+ LogMessage("Oracle User: " + toSettings.OracleUser, "INFO", gcLogFile)
+ LogMessage("Oracle DSN: " + toSettings.OracleDSN, "INFO", gcLogFile)
+ ENDIF
+
+ *-- Sync Settings (doar daca exista)
+ IF TYPE('toSettings.AdapterProgram') = 'C' AND !EMPTY(toSettings.AdapterProgram)
+ LogMessage("Adapter Program: " + toSettings.AdapterProgram, "INFO", gcLogFile)
+ LogMessage("JSON Pattern: " + toSettings.JsonFilePattern, "INFO", gcLogFile)
+ LogMessage("Auto Run Adapter: " + IIF(toSettings.AutoRunAdapter, "DA", "NU"), "INFO", gcLogFile)
+ ENDIF
+
+ LogMessage("=== SFARSIT CONFIGURATIE ===", "INFO", gcLogFile)
+
+ RETURN .T.
+ ENDPROC
+
+ *-- Metoda pentru setup complet cu validare
+ PROCEDURE Initialize
+ LOCAL llSuccess
+
+ llSuccess = THIS.Setup()
+
+ IF llSuccess
+ llSuccess = THIS.ValidateSettings()
+
+ IF llSuccess
+ THIS.DisplaySettingsInfo()
+ ENDIF
+ ENDIF
+
+ RETURN llSuccess
+ ENDPROC
+
+ *-- Functie pentru obtinerea setarilor
+ PROCEDURE GetSettings
+ RETURN THIS.oSettings
+ ENDPROC
+
+ *-- Functie pentru obtinerea path-ului aplicatiei
+ PROCEDURE GetAppPath
+ RETURN THIS.cAppPath
+ ENDPROC
+
+ *-- Functie pentru obtinerea path-ului fisierului INI
+ PROCEDURE GetIniFile
+ RETURN THIS.cIniFile
+ ENDPROC
+
+ENDDEFINE
+
+*-- ApplicationSetup Class - Clasa pentru configurarea si setup-ul aplicatiei
+*-- Caracteristici:
+*-- - Gestionare completa a settings.ini cu toate sectiunile
+*-- - Creare fisier implicit cu valori default
+*-- - Validare setari obligatorii pentru functionare
+*-- - Setup si initializare completa cu o singura metoda
+*-- - Afisarea informatiilor despre configuratia curenta
+*-- - Proprietati pentru acces facil la configuratii si paths
\ No newline at end of file
diff --git a/vfp/gomag-vending.prg b/vfp/gomag-adapter.prg
similarity index 95%
rename from vfp/gomag-vending.prg
rename to vfp/gomag-adapter.prg
index 4288b06..626a8e5 100644
--- a/vfp/gomag-vending.prg
+++ b/vfp/gomag-adapter.prg
@@ -30,6 +30,7 @@ lcPath = gcAppPath + 'nfjson;'
SET PATH TO (m.lcPath) ADDITIVE
SET PROCEDURE TO utils.prg ADDITIVE
+SET PROCEDURE TO ApplicationSetup.prg ADDITIVE
SET PROCEDURE TO nfjsonread.prg ADDITIVE
SET PROCEDURE TO nfjsoncreate.prg ADDITIVE
@@ -46,36 +47,18 @@ IF !DIRECTORY(lcOutputDir)
MKDIR (lcOutputDir)
ENDIF
-*-- Incarcarea setarilor din fisierul INI
-lcIniFile = gcAppPath + "settings.ini"
+*-- Creare și inițializare clasa setup aplicație
+LOCAL loAppSetup
+loAppSetup = CREATEOBJECT("ApplicationSetup", gcAppPath)
-*-- Verificare existenta fisier INI
-IF !CheckIniFile(lcIniFile)
- LogMessage("ATENTIE: Fisierul settings.ini nu a fost gasit!", "WARN", gcLogFile)
- LogMessage("Cream un fisier settings.ini implicit...", "INFO", gcLogFile)
- IF CreateDefaultIni(lcIniFile)
- LogMessage("Fisier settings.ini creat cu succes.", "INFO", gcLogFile)
- LogMessage("IMPORTANT: Modifica setarile din settings.ini (ApiKey, ApiShop) inainte de a rula scriptul din nou!", "INFO", gcLogFile)
- RETURN .F.
- ELSE
- LogMessage("EROARE: Nu s-a putut crea fisierul settings.ini!", "ERROR", gcLogFile)
- RETURN .F.
- ENDIF
-ENDIF
-
-*-- Incarcarea setarilor
-loSettings = LoadSettings(lcIniFile)
-
-*-- Verificare setari obligatorii
-IF EMPTY(loSettings.ApiKey) OR loSettings.ApiKey = "YOUR_API_KEY_HERE"
- LogMessage("EROARE: ApiKey nu este setat in settings.ini!", "ERROR", gcLogFile)
+*-- Setup complet cu validare
+IF !loAppSetup.Initialize()
+ LogMessage("EROARE: Setup-ul aplicației a eșuat sau necesită configurare!", "ERROR", gcLogFile)
RETURN .F.
ENDIF
-IF EMPTY(loSettings.ApiShop) OR "yourstore.gomag.ro" $ loSettings.ApiShop
- LogMessage("EROARE: ApiShop nu este setat corect in settings.ini!", "ERROR", gcLogFile)
- RETURN .F.
-ENDIF
+*-- Obținere setări din clasă
+loSettings = loAppSetup.GetSettings()
*-- Configurare API din settings.ini
lcApiBaseUrl = loSettings.ApiBaseUrl
diff --git a/vfp/settings.ini b/vfp/settings.ini
deleted file mode 100644
index 9bd7890..0000000
--- a/vfp/settings.ini
+++ /dev/null
@@ -1,17 +0,0 @@
-[API]
-ApiBaseUrl=https://api.gomag.ro/api/v1/product/read/json?enabled=1
-OrderApiUrl=https://api.gomag.ro/api/v1/order/read/json
-ApiKey=4c5e46df8f6c4f054fe2787de7a13d4a
-ApiShop=https://www.coffeepoint.ro
-UserAgent=Mozilla/5.0
-ContentType=application/json
-
-[PAGINATION]
-Limit=100
-
-[OPTIONS]
-GetProducts=1
-GetOrders=1
-
-[FILTERS]
-OrderDaysBack=7
\ No newline at end of file
diff --git a/vfp/settings.ini.example b/vfp/settings.ini.example
new file mode 100644
index 0000000..bb7a426
--- /dev/null
+++ b/vfp/settings.ini.example
@@ -0,0 +1,60 @@
+[API]
+ApiBaseUrl=https://api.gomag.ro/api/v1/product/read/json?enabled=1
+OrderApiUrl=https://api.gomag.ro/api/v1/order/read/json
+ApiKey=YOUR_API_KEY_HERE
+ApiShop=https://yourstore.gomag.ro
+UserAgent=Mozilla/5.0
+ContentType=application/json
+
+[PAGINATION]
+Limit=100
+
+[OPTIONS]
+GetProducts=1
+GetOrders=1
+
+[FILTERS]
+OrderDaysBack=7
+
+[ORACLE]
+OracleUser=MARIUSM_AUTO
+OraclePassword=ROMFASTSOFT
+OracleDSN=ROA_CENTRAL
+
+[SYNC]
+AdapterProgram=gomag-adapter.prg
+JsonFilePattern=gomag_orders*.json
+AutoRunAdapter=1
+
+# ===============================================
+# CONFIGURATIE SYNC COMENZI WEB → ORACLE ROA
+# ===============================================
+#
+# [API] - Configurari pentru GoMag API
+# - ApiKey: Cheia API de la GoMag (OBLIGATORIU)
+# - ApiShop: URL-ul magazinului GoMag (OBLIGATORIU)
+#
+# [OPTIONS]
+# - GetProducts: 1=descarca produse, 0=skip
+# - GetOrders: 1=descarca comenzi, 0=skip
+#
+# [ORACLE] - Conexiune la database ROA
+# - OracleUser: Utilizatorul Oracle (OBLIGATORIU)
+# - OraclePassword: Parola Oracle (OBLIGATORIU)
+# - OracleDSN: Data Source Name (OBLIGATORIU)
+#
+# [SYNC] - Configurari sincronizare
+# - AdapterProgram: Numele programului adapter (ex: gomag-adapter.prg)
+# - JsonFilePattern: Pattern pentru fisiere JSON (ex: gomag_orders*.json)
+# - AutoRunAdapter: 1=ruleaza automat adapter, 0=foloseste doar JSON existent
+#
+# Pentru utilizare:
+# 1. Copiaza settings.ini.example → settings.ini
+# 2. Configureaza ApiKey si ApiShop pentru GoMag
+# 3. Verifica datele Oracle (default: schema MARIUSM_AUTO)
+# 4. Ruleaza sync-comenzi-web.prg
+#
+# Pentru scheduled task Windows:
+# - Creeaza task care ruleaza sync-comenzi-web.prg la interval
+# - Nu mai este nevoie de auto-sync-timer.prg
+# - sync-comenzi-web.prg va apela automat gomag-adapter.prg
\ No newline at end of file
diff --git a/vfp/sync-comenzi-web.prg b/vfp/sync-comenzi-web.prg
new file mode 100644
index 0000000..80513f6
--- /dev/null
+++ b/vfp/sync-comenzi-web.prg
@@ -0,0 +1,572 @@
+*-- sync-comenzi-web.prg - Orchestrator pentru sincronizarea comenzilor web cu Oracle ROA
+*-- Autor: Claude AI
+*-- Data: 10 septembrie 2025
+*-- Dependency: gomag-vending.prg trebuie rulat mai intai pentru generarea JSON-urilor
+
+SET SAFETY OFF
+SET CENTURY ON
+SET DATE DMY
+SET EXACT ON
+SET ANSI ON
+SET DELETED ON
+
+*-- Variabile globale
+PRIVATE gcAppPath, gcLogFile, gnStartTime, gnOrdersProcessed, gnOrdersSuccess, gnOrdersErrors
+PRIVATE goConnectie, goSettings, goAppSetup
+LOCAL lcJsonPattern, laJsonFiles[1], lnJsonFiles, lnIndex, lcJsonFile
+LOCAL loJsonData, lcJsonContent, lnOrderCount, lnOrderIndex
+LOCAL loOrder, lcResult, llProcessSuccess
+
+*-- Initializare
+gcAppPath = ADDBS(JUSTPATH(SYS(16,0)))
+SET DEFAULT TO (m.gcAppPath)
+SET PATH TO nfjson ADDITIVE
+SET PROCEDURE TO utils.prg ADDITIVE
+SET PROCEDURE TO ApplicationSetup.prg ADDITIVE
+SET PROCEDURE TO nfjsonread.prg ADDITIVE
+
+*-- Statistici
+gnStartTime = SECONDS()
+gnOrdersProcessed = 0
+gnOrdersSuccess = 0
+gnOrdersErrors = 0
+
+*-- Initializare logging
+gcLogFile = InitLog("sync_comenzi")
+LogMessage("=== SYNC COMENZI WEB → ORACLE ROA ===", "INFO", gcLogFile)
+
+*-- Creare și inițializare clasa setup aplicație
+goAppSetup = CREATEOBJECT("ApplicationSetup", gcAppPath)
+
+*-- Setup complet cu validare și afișare configurație
+IF !goAppSetup.Initialize()
+ LogMessage("EROARE: Setup-ul aplicației a eșuat sau necesită configurare!", "ERROR", gcLogFile)
+ RETURN .F.
+ENDIF
+
+*-- Obținere setări din clasă
+goSettings = goAppSetup.GetSettings()
+
+*-- Verificare directoare necesare
+IF !DIRECTORY(gcAppPath + "output")
+ LogMessage("EROARE: Directorul output/ nu există! Rulează mai întâi adapter-ul web", "ERROR", gcLogFile)
+ RETURN .F.
+ENDIF
+
+*-- Rulare automată adapter pentru obținere comenzi (dacă este configurat)
+IF goSettings.AutoRunAdapter
+ LogMessage("Rulez adapter pentru obținere comenzi: " + goSettings.AdapterProgram, "INFO", gcLogFile)
+ IF !ExecuteAdapter()
+ LogMessage("EROARE la rularea adapter-ului, continuez cu fișierele JSON existente", "WARN", gcLogFile)
+ ENDIF
+ELSE
+ LogMessage("AutoRunAdapter este dezactivat, folosesc doar fișierele JSON existente", "INFO", gcLogFile)
+ENDIF
+
+*-- Găsire fișiere JSON comenzi din pattern configurat
+lcJsonPattern = gcAppPath + "output\" + goSettings.JsonFilePattern
+lnJsonFiles = ADIR(laJsonFiles, lcJsonPattern)
+
+IF lnJsonFiles = 0
+ LogMessage("AVERTISMENT: Nu au fost găsite fișiere JSON cu comenzi web", "WARN", gcLogFile)
+ LogMessage("Rulează mai întâi adapter-ul web cu GetOrders=1 în settings.ini", "INFO", gcLogFile)
+ RETURN .T.
+ENDIF
+
+LogMessage("Găsite " + TRANSFORM(lnJsonFiles) + " fișiere JSON cu comenzi web", "INFO", gcLogFile)
+
+*-- Încercare conectare Oracle (folosind conexiunea existentă din sistem)
+IF !ConnectToOracle()
+ LogMessage("EROARE: Nu s-a putut conecta la Oracle ROA", "ERROR", gcLogFile)
+ RETURN .F.
+ENDIF
+
+*-- Procesare fiecare fișier JSON găsit
+FOR lnIndex = 1 TO lnJsonFiles
+ lcJsonFile = gcAppPath + "output\" + laJsonFiles[lnIndex, 1]
+ LogMessage("Procesez fișierul: " + laJsonFiles[lnIndex, 1], "INFO", gcLogFile)
+
+ *-- Citire și parsare JSON
+ TRY
+ lcJsonContent = FILETOSTR(lcJsonFile)
+ IF EMPTY(lcJsonContent)
+ LogMessage("AVERTISMENT: Fișier JSON gol - " + laJsonFiles[lnIndex, 1], "WARN", gcLogFile)
+ LOOP
+ ENDIF
+
+ *-- Parsare JSON array cu comenzi
+ loJsonData = nfJsonRead(lcJsonContent)
+ IF ISNULL(loJsonData)
+ LogMessage("EROARE: Nu s-a putut parsa JSON-ul din " + laJsonFiles[lnIndex, 1], "ERROR", gcLogFile)
+ LOOP
+ ENDIF
+
+ *-- Verificare dacă este array
+ IF TYPE('loJsonData') != 'O'
+ LogMessage("EROARE: JSON-ul nu este un array valid - " + laJsonFiles[lnIndex, 1], "ERROR", gcLogFile)
+ LOOP
+ ENDIF
+
+ *-- Obținere număr comenzi din array
+ lnOrderCount = AMEMBERS(laOrders, loJsonData, 0)
+ LogMessage("Găsite " + TRANSFORM(lnOrderCount) + " comenzi în " + laJsonFiles[lnIndex, 1], "INFO", gcLogFile)
+
+ *-- Procesare fiecare comandă din JSON
+ FOR lnOrderIndex = 1 TO lnOrderCount
+ lcOrderPropName = laOrders[lnOrderIndex]
+ loOrder = EVALUATE('loJsonData.' + lcOrderPropName)
+
+ IF TYPE('loOrder') = 'O'
+ gnOrdersProcessed = gnOrdersProcessed + 1
+ llProcessSuccess = ProcessWebOrder(loOrder)
+
+ IF llProcessSuccess
+ gnOrdersSuccess = gnOrdersSuccess + 1
+ ELSE
+ gnOrdersErrors = gnOrdersErrors + 1
+ ENDIF
+ ENDIF
+ ENDFOR
+
+ CATCH TO loError
+ LogMessage("EROARE la procesarea fișierului " + laJsonFiles[lnIndex, 1] + ": " + loError.Message, "ERROR", gcLogFile)
+ gnOrdersErrors = gnOrdersErrors + 1
+ ENDTRY
+ENDFOR
+
+*-- Închidere conexiune Oracle
+DisconnectFromOracle()
+
+*-- Logging final cu statistici
+LogMessage("=== PROCESARE COMPLETĂ ===", "INFO", gcLogFile)
+LogMessage("Total comenzi procesate: " + TRANSFORM(gnOrdersProcessed), "INFO", gcLogFile)
+LogMessage("Comenzi importate cu succes: " + TRANSFORM(gnOrdersSuccess), "INFO", gcLogFile)
+LogMessage("Comenzi cu erori: " + TRANSFORM(gnOrdersErrors), "INFO", gcLogFile)
+CloseLog(gnStartTime, 0, gnOrdersProcessed, gcLogFile)
+
+RETURN .T.
+
+*-- ===================================================================
+*-- HELPER FUNCTIONS
+*-- ===================================================================
+
+*-- Funcție pentru conectarea la Oracle folosind setările din settings.ini
+FUNCTION ConnectToOracle
+LOCAL llSuccess, lcConnectionString, lnHandle
+
+llSuccess = .F.
+
+TRY
+ *-- Conectare Oracle folosind datele din settings.ini
+ lnHandle = SQLCONNECT(goSettings.OracleDSN, goSettings.OracleUser, goSettings.OraclePassword)
+
+ IF lnHandle > 0
+ goConnectie = lnHandle
+ llSuccess = .T.
+ LogMessage("Conectare Oracle reușită - Handle: " + TRANSFORM(lnHandle), "INFO", gcLogFile)
+ LogMessage("DSN: " + goSettings.OracleDSN + " | User: " + goSettings.OracleUser, "DEBUG", gcLogFile)
+ ELSE
+ LogMessage("EROARE: Conectare Oracle eșuată - Handle: " + TRANSFORM(lnHandle), "ERROR", gcLogFile)
+ LogMessage("DSN: " + goSettings.OracleDSN + " | User: " + goSettings.OracleUser, "ERROR", gcLogFile)
+ ENDIF
+
+CATCH TO loError
+ LogMessage("EROARE la conectarea Oracle: " + loError.Message, "ERROR", gcLogFile)
+ENDTRY
+
+RETURN llSuccess
+ENDFUNC
+
+*-- Funcție pentru deconectarea de la Oracle
+FUNCTION DisconnectFromOracle
+IF TYPE('goConnectie') = 'N' AND goConnectie > 0
+ SQLDISCONNECT(goConnectie)
+ LogMessage("Deconectare Oracle reușită", "INFO", gcLogFile)
+ENDIF
+RETURN .T.
+ENDFUNC
+
+*-- Funcție principală de procesare comandă web
+FUNCTION ProcessWebOrder
+PARAMETERS loOrder
+LOCAL llSuccess, lcOrderNumber, lcOrderDate, lnPartnerID, lcArticlesJSON
+LOCAL lcObservatii, lcSQL, lnResult, lcErrorDetails
+
+llSuccess = .F.
+
+TRY
+ *-- Validare comandă
+ IF !ValidateWebOrder(loOrder)
+ LogMessage("EROARE: Comandă web invalidă - lipsesc date obligatorii", "ERROR", gcLogFile)
+ RETURN .F.
+ ENDIF
+
+ *-- Extragere date comandă
+ lcOrderNumber = CleanWebText(TRANSFORM(loOrder.number))
+ lcOrderDate = ConvertWebDate(loOrder.date)
+
+ LogMessage("Procesez comanda: " + lcOrderNumber + " din " + lcOrderDate, "INFO", gcLogFile)
+
+ *-- Procesare partener (billing address)
+ lnPartnerID = ProcessPartnerFromBilling(loOrder.billing)
+ IF lnPartnerID <= 0
+ LogMessage("EROARE: Nu s-a putut procesa partenerul pentru comanda " + lcOrderNumber, "ERROR", gcLogFile)
+ RETURN .F.
+ ENDIF
+
+ LogMessage("Partener identificat/creat: ID=" + TRANSFORM(lnPartnerID), "INFO", gcLogFile)
+
+ *-- Construire JSON articole
+ lcArticlesJSON = BuildArticlesJSON(loOrder.items)
+ IF EMPTY(lcArticlesJSON)
+ LogMessage("EROARE: Nu s-au găsit articole valide în comanda " + lcOrderNumber, "ERROR", gcLogFile)
+ RETURN .F.
+ ENDIF
+
+ *-- Construire observații cu detalii suplimentare
+ lcObservatii = BuildOrderObservations(loOrder)
+
+ *-- Apel package Oracle pentru import comandă
+ lcSQL = "SELECT PACK_IMPORT_COMENZI.importa_comanda_web(?, TO_DATE(?, 'YYYY-MM-DD'), ?, ?, NULL, ?) AS ID_COMANDA FROM dual"
+
+ lnResult = SQLEXEC(goConnectie, lcSQL, ;
+ lcOrderNumber, ; && p_nr_comanda_ext
+ lcOrderDate, ; && p_data_comanda
+ lnPartnerID, ; && p_id_partener
+ lcArticlesJSON, ; && p_json_articole
+ lcObservatii, ; && p_observatii
+ "cursor_comanda")
+
+ IF lnResult > 0 AND RECCOUNT("cursor_comanda") > 0 AND cursor_comanda.ID_COMANDA > 0
+ LogMessage("SUCCES: Comandă importată - ID Oracle: " + TRANSFORM(cursor_comanda.ID_COMANDA), "INFO", gcLogFile)
+ USE IN cursor_comanda
+ llSuccess = .T.
+ ELSE
+ *-- Obținere detalii eroare Oracle
+ lcErrorDetails = GetOracleErrorDetails()
+ LogMessage("EROARE: Import comandă eșuat pentru " + lcOrderNumber + " - " + lcErrorDetails, "ERROR", gcLogFile)
+ IF USED("cursor_comanda")
+ USE IN cursor_comanda
+ ENDIF
+ ENDIF
+
+CATCH TO loError
+ LogMessage("EXCEPȚIE la procesarea comenzii " + lcOrderNumber + ": " + loError.Message, "ERROR", gcLogFile)
+ENDTRY
+
+RETURN llSuccess
+ENDFUNC
+
+*-- Funcție pentru validarea comenzii web
+FUNCTION ValidateWebOrder
+PARAMETERS loOrder
+LOCAL llValid
+
+llValid = .T.
+
+*-- Verificări obligatorii
+IF TYPE('loOrder.number') != 'C' OR EMPTY(loOrder.number)
+ llValid = .F.
+ENDIF
+
+IF TYPE('loOrder.date') != 'C' OR EMPTY(loOrder.date)
+ llValid = .F.
+ENDIF
+
+IF TYPE('loOrder.billing') != 'O'
+ llValid = .F.
+ENDIF
+
+IF TYPE('loOrder.items') != 'O'
+ llValid = .F.
+ENDIF
+
+RETURN llValid
+ENDFUNC
+
+*-- Funcție pentru procesarea partenerului din billing GoMag
+FUNCTION ProcessPartnerFromBilling
+PARAMETERS loBilling
+LOCAL lnPartnerID, lcDenumire, lcCodFiscal, lcAdresa, lcTelefon, lcEmail
+LOCAL lcSQL, lnResult
+
+lnPartnerID = 0
+
+TRY
+ *-- Extragere date partener din datele billing
+ LOCAL lnIsPersoanaJuridica
+
+ IF TYPE('loBilling.company') = 'O' AND !EMPTY(loBilling.company.name)
+ *-- Companie - persoană juridică
+ lcDenumire = CleanWebText(loBilling.company.name)
+ lcCodFiscal = IIF(TYPE('loBilling.company.code') = 'C', loBilling.company.code, NULL)
+ lnIsPersoanaJuridica = 1 && Persoană juridică
+ ELSE
+ *-- Persoană fizică
+ lcDenumire = CleanWebText(ALLTRIM(loBilling.firstname) + " " + ALLTRIM(loBilling.lastname))
+ lcCodFiscal = NULL && Persoanele fizice nu au CUI în platformele web
+ lnIsPersoanaJuridica = 0 && Persoană fizică
+ ENDIF
+
+ *-- Formatare adresă pentru Oracle (format semicolon cu prefix JUD:)
+ lcAdresa = FormatAddressForOracle(loBilling)
+
+ *-- Date contact
+ lcTelefon = IIF(TYPE('loBilling.phone') = 'C', loBilling.phone, "")
+ lcEmail = IIF(TYPE('loBilling.email') = 'C', loBilling.email, "")
+
+ LogMessage("Partener: " + lcDenumire + " | CUI: " + IIF(ISNULL(lcCodFiscal), "NULL", lcCodFiscal) + " | Tip: " + IIF(lnIsPersoanaJuridica = 1, "JURIDICA", "FIZICA"), "DEBUG", gcLogFile)
+
+ *-- Apel package Oracle IMPORT_PARTENERI (PROCEDURA cu parametru OUT)
+ *-- Folosind sintaxa corectă pentru parametrii OUT în VFP
+ LOCAL lnPartnerResult
+ lnPartnerResult = 0
+
+ lcSQL = "BEGIN PACK_IMPORT_PARTENERI.cauta_sau_creeaza_partener(?lcCodFiscal, ?lcDenumire, ?lcAdresa, ?lcTelefon, ?lcEmail, ?lnIsPersoanaJuridica, ?@lnPartnerResult); END;"
+
+ lnResult = SQLEXEC(goConnectie, lcSQL)
+
+ IF lnResult > 0
+ lnPartnerID = lnPartnerResult
+ LogMessage("Partener procesat cu succes: ID=" + TRANSFORM(lnPartnerID), "DEBUG", gcLogFile)
+ ELSE
+ *-- Obținere detalii eroare Oracle
+ lcErrorDetails = GetOracleErrorDetails()
+ LogMessage("EROARE la apelul procedurii PACK_IMPORT_PARTENERI pentru: " + lcDenumire + " - " + lcErrorDetails, "ERROR", gcLogFile)
+ lnPartnerID = 0
+ ENDIF
+
+CATCH TO loError
+ LogMessage("EXCEPȚIE la procesarea partenerului: " + loError.Message, "ERROR", gcLogFile)
+ENDTRY
+
+RETURN lnPartnerID
+ENDFUNC
+
+*-- Funcție pentru construirea JSON-ului cu articole conform package Oracle
+FUNCTION BuildArticlesJSON
+PARAMETERS loItems
+LOCAL lcJSON, lnItemCount, lnIndex, lcItemProp, loItem
+LOCAL lcSku, lcQuantity, lcPrice
+
+lcJSON = ""
+
+TRY
+ IF TYPE('loItems') != 'O'
+ RETURN ""
+ ENDIF
+
+ lnItemCount = AMEMBERS(laItems, loItems, 0)
+ IF lnItemCount = 0
+ RETURN ""
+ ENDIF
+
+ lcJSON = "["
+
+ FOR lnIndex = 1 TO lnItemCount
+ lcItemProp = laItems[lnIndex]
+ loItem = EVALUATE('loItems.' + lcItemProp)
+
+ IF TYPE('loItem') = 'O'
+ *-- Extragere date articol
+ lcSku = IIF(TYPE('loItem.sku') = 'C', CleanWebText(loItem.sku), "")
+ lcQuantity = IIF(TYPE('loItem.quantity') = 'C' OR TYPE('loItem.quantity') = 'N', TRANSFORM(VAL(TRANSFORM(loItem.quantity))), "1")
+ lcPrice = IIF(TYPE('loItem.price') = 'C' OR TYPE('loItem.price') = 'N', TRANSFORM(VAL(TRANSFORM(loItem.price))), "0")
+
+ IF !EMPTY(lcSku)
+ *-- Adaugare virgulă pentru elementele următoare
+ IF lnIndex > 1
+ lcJSON = lcJSON + ","
+ ENDIF
+
+ *-- Format JSON conform package Oracle: {"sku":"...", "cantitate":..., "pret":...}
+ lcJSON = lcJSON + '{"sku":"' + lcSku + '","cantitate":' + lcQuantity + ',"pret":' + lcPrice + '}'
+ ENDIF
+ ENDIF
+ ENDFOR
+
+ lcJSON = lcJSON + "]"
+
+CATCH TO loError
+ LogMessage("EROARE la construirea JSON articole: " + loError.Message, "ERROR", gcLogFile)
+ lcJSON = ""
+ENDTRY
+
+RETURN lcJSON
+ENDFUNC
+
+*-- Funcție pentru curățarea textului web (HTML entities → ASCII simplu)
+FUNCTION CleanWebText
+PARAMETERS tcText
+LOCAL lcResult
+
+IF EMPTY(tcText) OR TYPE('tcText') != 'C'
+ RETURN ""
+ENDIF
+
+lcResult = tcText
+
+*-- Conversie HTML entities în caractere simple (fără diacritice)
+lcResult = STRTRAN(lcResult, 'ă', 'a') && ă → a
+lcResult = STRTRAN(lcResult, 'ș', 's') && ș → s
+lcResult = STRTRAN(lcResult, 'ț', 't') && ț → t
+lcResult = STRTRAN(lcResult, 'î', 'i') && î → i
+lcResult = STRTRAN(lcResult, 'â', 'a') && â → a
+lcResult = STRTRAN(lcResult, '&', '&')
+lcResult = STRTRAN(lcResult, '<', '<')
+lcResult = STRTRAN(lcResult, '>', '>')
+lcResult = STRTRAN(lcResult, '"', '"')
+
+*-- Eliminare tag-uri HTML simple
+lcResult = STRTRAN(lcResult, '
', ' ')
+lcResult = STRTRAN(lcResult, '
', ' ')
+lcResult = STRTRAN(lcResult, '
', ' ')
+
+RETURN ALLTRIM(lcResult)
+ENDFUNC
+
+*-- Funcție pentru conversia datei web în format Oracle
+FUNCTION ConvertWebDate
+PARAMETERS tcWebDate
+LOCAL lcResult
+
+IF EMPTY(tcWebDate) OR TYPE('tcWebDate') != 'C'
+ RETURN DTOS(DATE())
+ENDIF
+
+*-- Web date format: "2025-08-27 16:32:43" → "2025-08-27"
+lcResult = LEFT(tcWebDate, 10)
+
+*-- Validare format YYYY-MM-DD
+IF LEN(lcResult) = 10 AND SUBSTR(lcResult, 5, 1) = '-' AND SUBSTR(lcResult, 8, 1) = '-'
+ RETURN lcResult
+ELSE
+ RETURN DTOS(DATE())
+ENDIF
+ENDFUNC
+
+*-- Funcție pentru formatarea adresei în format semicolon pentru Oracle
+FUNCTION FormatAddressForOracle
+PARAMETERS loBilling
+LOCAL lcAdresa, lcJudet, lcOras, lcStrada
+
+*-- Extragere componente adresă
+lcJudet = IIF(TYPE('loBilling.region') = 'C', CleanWebText(loBilling.region), "Bucuresti")
+lcOras = IIF(TYPE('loBilling.city') = 'C', CleanWebText(loBilling.city), "BUCURESTI")
+lcStrada = IIF(TYPE('loBilling.address') = 'C', CleanWebText(loBilling.address), "Adresa necunoscuta")
+
+*-- Format semicolon cu prefix JUD: conform specificațiilor Oracle
+lcAdresa = "JUD:" + lcJudet + ";" + lcOras + ";" + lcStrada
+
+RETURN lcAdresa
+ENDFUNC
+
+*-- Funcție pentru construirea observațiilor comenzii
+FUNCTION BuildOrderObservations
+PARAMETERS loOrder
+LOCAL lcObservatii
+
+lcObservatii = ""
+
+*-- Informații plată și livrare
+IF TYPE('loOrder.payment') = 'O' AND TYPE('loOrder.payment.name') = 'C'
+ lcObservatii = lcObservatii + "Payment: " + CleanWebText(loOrder.payment.name) + "; "
+ENDIF
+
+IF TYPE('loOrder.delivery') = 'O' AND TYPE('loOrder.delivery.name') = 'C'
+ lcObservatii = lcObservatii + "Delivery: " + CleanWebText(loOrder.delivery.name) + "; "
+ENDIF
+
+*-- Status și sursă
+IF TYPE('loOrder.status') = 'C'
+ lcObservatii = lcObservatii + "Status: " + CleanWebText(loOrder.status) + "; "
+ENDIF
+
+IF TYPE('loOrder.source') = 'C'
+ lcObservatii = lcObservatii + "Source: " + CleanWebText(loOrder.source)
+
+ IF TYPE('loOrder.sales_channel') = 'C'
+ lcObservatii = lcObservatii + " " + CleanWebText(loOrder.sales_channel)
+ ENDIF
+ lcObservatii = lcObservatii + "; "
+ENDIF
+
+*-- Verificare adrese diferite shipping vs billing
+IF TYPE('loOrder.shipping') = 'O' AND TYPE('loOrder.billing') = 'O'
+ IF TYPE('loOrder.shipping.address') = 'C' AND TYPE('loOrder.billing.address') = 'C'
+ IF !ALLTRIM(loOrder.shipping.address) == ALLTRIM(loOrder.billing.address)
+ lcObservatii = lcObservatii + "Shipping: " + CleanWebText(loOrder.shipping.address)
+
+ IF TYPE('loOrder.shipping.city') = 'C'
+ lcObservatii = lcObservatii + ", " + CleanWebText(loOrder.shipping.city)
+ ENDIF
+ lcObservatii = lcObservatii + "; "
+ ENDIF
+ ENDIF
+ENDIF
+
+*-- Limitare lungime observații pentru Oracle
+IF LEN(lcObservatii) > 500
+ lcObservatii = LEFT(lcObservatii, 497) + "..."
+ENDIF
+
+RETURN lcObservatii
+ENDFUNC
+
+*-- Funcție pentru obținerea detaliilor erorii Oracle
+FUNCTION GetOracleErrorDetails
+LOCAL lcError, laError[1], lnErrorLines, lnIndex
+
+lcError = ""
+
+*-- Obținere eroare Oracle
+lnErrorLines = AERROR(laError)
+IF lnErrorLines > 0
+ FOR lnIndex = 1 TO lnErrorLines
+ IF lnIndex > 1
+ lcError = lcError + " | "
+ ENDIF
+ lcError = lcError + ALLTRIM(STR(laError[lnIndex, 1])) + ": " + laError[lnIndex, 2]
+ ENDFOR
+ENDIF
+
+IF EMPTY(lcError)
+ lcError = "Eroare Oracle nedefinită"
+ENDIF
+
+RETURN lcError
+ENDFUNC
+
+*-- Funcție pentru execuția adapter-ului configurat
+FUNCTION ExecuteAdapter
+LOCAL llSuccess, lcAdapterPath
+
+llSuccess = .F.
+
+TRY
+ lcAdapterPath = gcAppPath + goSettings.AdapterProgram
+
+ IF FILE(lcAdapterPath)
+ LogMessage("Execuție adapter: " + lcAdapterPath, "INFO", gcLogFile)
+ DO (lcAdapterPath)
+ llSuccess = .T.
+ LogMessage("Adapter executat cu succes", "INFO", gcLogFile)
+ ELSE
+ LogMessage("EROARE: Adapter-ul nu a fost găsit la: " + lcAdapterPath, "ERROR", gcLogFile)
+ ENDIF
+
+CATCH TO loError
+ LogMessage("EXCEPȚIE la execuția adapter-ului " + goSettings.AdapterProgram + ": " + loError.Message, "ERROR", gcLogFile)
+ENDTRY
+
+RETURN llSuccess
+ENDFUNC
+
+*-- Orchestrator complet pentru sincronizarea comenzilor web cu Oracle ROA
+*-- Caracteristici:
+*-- - Citește JSON-urile generate de gomag-vending.prg
+*-- - Procesează comenzile cu toate helper functions necesare
+*-- - Integrează cu package-urile Oracle validate în Phase 1
+*-- - Logging complet cu statistici de procesare
+*-- - Error handling pentru toate situațiile
+*-- - Support pentru toate formatele GoMag (billing/shipping, companii/persoane fizice)
\ No newline at end of file
diff --git a/vfp/utils.prg b/vfp/utils.prg
index 5895dcb..42657f5 100644
--- a/vfp/utils.prg
+++ b/vfp/utils.prg
@@ -1,7 +1,7 @@
-*-- utils.prg - Utilitare pentru GoMag API
-*-- Functii pentru citirea/scrierea fisierelor INI si alte utilitare
+*-- utils.prg - Functii utilitare generale
+*-- Contine doar functii utilitare reutilizabile (INI, HTTP, logging, encoding)
*-- Autor: Claude AI
-*-- Data: 27.08.2025
+*-- Data: 10 septembrie 2025
*-- Functie pentru citirea fisierelor INI private
*-- Returneaza valoarea din sectiunea si intrarea specificata sau blank daca nu e gasita
@@ -51,34 +51,6 @@ nRetVal = WritePrivateProfileString(cSection, ;
RETURN nRetVal = 1
ENDFUNC
-*-- Functie pentru incarcarea tuturor setarilor din fisierul INI
-FUNCTION LoadSettings
-PARAMETERS cINIFile
-LOCAL loSettings
-
-*-- Cream un obiect pentru toate setarile
-loSettings = CREATEOBJECT("Empty")
-
-*-- Sectiunea API
-ADDPROPERTY(loSettings, "ApiBaseUrl", ReadPini("API", "ApiBaseUrl", cINIFile))
-ADDPROPERTY(loSettings, "OrderApiUrl", ReadPini("API", "OrderApiUrl", cINIFile))
-ADDPROPERTY(loSettings, "ApiKey", ReadPini("API", "ApiKey", cINIFile))
-ADDPROPERTY(loSettings, "ApiShop", ReadPini("API", "ApiShop", cINIFile))
-ADDPROPERTY(loSettings, "UserAgent", ReadPini("API", "UserAgent", cINIFile))
-ADDPROPERTY(loSettings, "ContentType", ReadPini("API", "ContentType", cINIFile))
-
-*-- Sectiunea PAGINATION
-ADDPROPERTY(loSettings, "Limit", VAL(ReadPini("PAGINATION", "Limit", cINIFile)))
-
-*-- Sectiunea OPTIONS
-ADDPROPERTY(loSettings, "GetProducts", ReadPini("OPTIONS", "GetProducts", cINIFile) = "1")
-ADDPROPERTY(loSettings, "GetOrders", ReadPini("OPTIONS", "GetOrders", cINIFile) = "1")
-
-*-- Sectiunea FILTERS
-ADDPROPERTY(loSettings, "OrderDaysBack", VAL(ReadPini("FILTERS", "OrderDaysBack", cINIFile)))
-
-RETURN loSettings
-ENDFUNC
*-- Test conectivitate internet
FUNCTION TestConnectivity
@@ -140,39 +112,6 @@ ENDTRY
RETURN llExists
ENDFUNC
-*-- Functie pentru crearea unui fisier INI implicit cu setari de baza
-FUNCTION CreateDefaultIni
-PARAMETERS cINIFile
-LOCAL llSuccess
-
-llSuccess = .T.
-
-TRY
- *-- Sectiunea API
- WritePini("API", "ApiBaseUrl", "https://api.gomag.ro/api/v1/product/read/json?enabled=1", cINIFile)
- WritePini("API", "OrderApiUrl", "https://api.gomag.ro/api/v1/order/read/json", cINIFile)
- WritePini("API", "ApiKey", "YOUR_API_KEY_HERE", cINIFile)
- WritePini("API", "ApiShop", "https://yourstore.gomag.ro", cINIFile)
- WritePini("API", "UserAgent", "Mozilla/5.0", cINIFile)
- WritePini("API", "ContentType", "application/json", cINIFile)
-
- *-- Sectiunea PAGINATION
- WritePini("PAGINATION", "Limit", "100", cINIFile)
-
- *-- Sectiunea OPTIONS
- WritePini("OPTIONS", "GetProducts", "1", cINIFile)
- WritePini("OPTIONS", "GetOrders", "1", cINIFile)
-
- *-- Sectiunea FILTERS
- WritePini("FILTERS", "OrderDaysBack", "7", cINIFile)
-
-CATCH
- llSuccess = .F.
-ENDTRY
-
-RETURN llSuccess
-ENDFUNC
-
*-- Functie pentru initializarea logging-ului
FUNCTION InitLog
PARAMETERS cBaseName