diff --git a/.gitignore b/.gitignore index 3a116be..1efc06b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ *.fxp -*.bak \ No newline at end of file +*.FXP +*.bak +*.BAK +*.csv +*.json +*.err +*.ERR diff --git a/CLAUDE.md b/CLAUDE.md index a4b8f23..7182902 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,6 +34,19 @@ Main script that handles: DO gomag-vending.prg ``` +### Running from Windows Command Line +Use the provided batch file for easy execution: +```cmd +run-gomag.bat +``` + +Or directly with Visual FoxPro executable: +```cmd +"C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" -T "path\to\gomag-vending-test.prg" +``` + +The batch file uses `%~dp0` to automatically detect the current directory, making it portable across different locations. + ### Testing Connectivity The script includes a `TestConnectivity()` function for internet connectivity testing. @@ -54,8 +67,9 @@ The script includes a `TestConnectivity()` function for internet connectivity te ## File Structure ``` / -├── gomag-vending.prg # Main application script -└── gomag_products_*.json # Generated API response files (timestamped) +├── gomag-vending-test.prg # Main application script +├── run-gomag.bat # Windows batch file for easy execution +└── gomag_products_*.json # Generated API response files (timestamped) ``` ## Configuration Requirements diff --git a/gomag-vending-test.prg b/gomag-vending-test.prg index c5b6809..ca04c9c 100644 --- a/gomag-vending-test.prg +++ b/gomag-vending-test.prg @@ -9,6 +9,10 @@ LOCAL laHeaders[10], lnHeaderCount Local lcApiShop, lcCsvFileName, lcErrorResponse, lcFileName, lcLogContent, lcLogFileName, lcPath Local lcStatusText, lnStatusCode, loError Local lnLimit, lnCurrentPage, llHasMorePages, loAllJsonData, lnTotalPages, lnTotalProducts +Local lcOrderApiUrl, loAllOrderData, lcOrderCsvFileName, lcOrderJsonFileName +Local ldStartDate, lcStartDateStr +Local lcIniFile, loSettings +LOCAL llGetProducts, llGetOrders PRIVATE gcAppPath, loJsonData @@ -18,19 +22,60 @@ SET DEFAULT TO (m.gcAppPath) lcPath = gcAppPath + 'nfjson;' SET PATH TO (m.lcPath) ADDITIVE -SET PROCEDURE TO nfjsonread.prg ADDITIVE +SET PROCEDURE TO nfjsonread.prg ADDITIVE +SET PROCEDURE TO utils.prg ADDITIVE -*-- Configurare API - MODIFICA aceste valori conform documentatiei GoMag -lcApiBaseUrl = "https://api.gomag.ro/api/v1/product/read/json?enabled=1" && URL de baza pentru lista de produse -lcApiKey = "4c5e46df8f6c4f054fe2787de7a13d4a" && Cheia ta API de la GoMag -lcApiShop = "https://www.coffeepoint.ro" && URL-ul magazinului tau (ex: http://yourdomain.gomag.ro) -lcUserAgent = "Mozilla/5.0" && User-Agent diferit de PostmanRuntime conform documentatiei -lcContentType = "application/json" -lnLimit = 100 && Numarul maxim de produse per pagina (1-100) +*-- Incarcarea setarilor din fisierul INI +lcIniFile = gcAppPath + "settings.ini" + +*-- Verificare existenta fisier INI +IF !CheckIniFile(lcIniFile) + ? "ATENTIE: Fisierul settings.ini nu a fost gasit!" + ? "Cream un fisier settings.ini implicit..." + IF CreateDefaultIni(lcIniFile) + ? "Fisier settings.ini creat cu succes." + ? "IMPORTANT: Modifica setarile din settings.ini (ApiKey, ApiShop) inainte de a rula scriptul din nou!" + RETURN .F. + ELSE + ? "EROARE: Nu s-a putut crea fisierul settings.ini!" + RETURN .F. + ENDIF +ENDIF + +*-- Incarcarea setarilor +loSettings = LoadSettings(lcIniFile) + +*-- Verificare setari obligatorii +IF EMPTY(loSettings.ApiKey) OR loSettings.ApiKey = "YOUR_API_KEY_HERE" + ? "EROARE: ApiKey nu este setat in settings.ini!" + RETURN .F. +ENDIF + +IF EMPTY(loSettings.ApiShop) OR "yourstore.gomag.ro" $ loSettings.ApiShop + ? "EROARE: ApiShop nu este setat corect in settings.ini!" + RETURN .F. +ENDIF + +*-- Configurare API din settings.ini +lcApiBaseUrl = loSettings.ApiBaseUrl +lcOrderApiUrl = loSettings.OrderApiUrl +lcApiKey = loSettings.ApiKey +lcApiShop = loSettings.ApiShop +lcUserAgent = loSettings.UserAgent +lcContentType = loSettings.ContentType +lnLimit = loSettings.Limit +llGetProducts = loSettings.GetProducts +llGetOrders = loSettings.GetOrders lnCurrentPage = 1 && Pagina de start llHasMorePages = .T. && Flag pentru paginare loAllJsonData = NULL && Obiect pentru toate datele +*-- Calculare data pentru ultimele X zile (din settings.ini) +ldStartDate = DATE() - loSettings.OrderDaysBack +lcStartDateStr = TRANSFORM(YEAR(ldStartDate)) + "-" + ; + RIGHT("0" + TRANSFORM(MONTH(ldStartDate)), 2) + "-" + ; + RIGHT("0" + TRANSFORM(DAY(ldStartDate)), 2) + *-- Verificare daca avem WinHttp disponibil TRY loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") @@ -38,15 +83,21 @@ CATCH TO loError ? "Eroare la crearea obiectului WinHttp: " + loError.Message RETURN .F. ENDTRY +SET STEP ON +*-- SECTIUNEA PRODUSE - se executa doar daca llGetProducts = .T. +IF llGetProducts + ? "=======================================" + ? "PRELUARE PRODUSE" + ? "=======================================" + + *-- Bucla pentru preluarea tuturor produselor (paginare) + loAllJsonData = CREATEOBJECT("Empty") + ADDPROPERTY(loAllJsonData, "products", CREATEOBJECT("Empty")) + ADDPROPERTY(loAllJsonData, "total", 0) + ADDPROPERTY(loAllJsonData, "pages", 0) + lnTotalProducts = 0 -*-- Bucla pentru preluarea tuturor produselor (paginare) -loAllJsonData = CREATEOBJECT("Empty") -ADDPROPERTY(loAllJsonData, "products", CREATEOBJECT("Empty")) -ADDPROPERTY(loAllJsonData, "total", 0) -ADDPROPERTY(loAllJsonData, "pages", 0) -lnTotalProducts = 0 - -DO WHILE llHasMorePages + DO WHILE llHasMorePages *-- Construire URL cu paginare lcApiUrl = lcApiBaseUrl + "&page=" + TRANSFORM(lnCurrentPage) + "&limit=" + TRANSFORM(lnLimit) @@ -159,22 +210,270 @@ DO WHILE llHasMorePages ENDDO -*-- Creare fisier CSV cu toate produsele -IF !ISNULL(loAllJsonData) AND TYPE('loAllJsonData.products') = 'O' - lcCsvFileName = "gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".csv" - DO CreateCsvFromJson WITH loAllJsonData, lcCsvFileName - ? "Fisier CSV creat: " + lcCsvFileName + *-- Salvare array JSON cu toate produsele + IF !ISNULL(loAllJsonData) AND TYPE('loAllJsonData.products') = 'O' + lcJsonFileName = "gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" + DO SaveProductsArray WITH loAllJsonData, lcJsonFileName + ? "Fisier JSON cu produse creat: " + lcJsonFileName + ENDIF - *-- Salvare si a datelor JSON complete - lcJsonFileName = "gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" - DO SaveCompleteJson WITH loAllJsonData, lcJsonFileName - ? "Fisier JSON complet creat: " + lcJsonFileName +ELSE + ? "SARIT PESTE PRELUAREA PRODUSELOR (llGetProducts = .F.)" +ENDIF + +*-- SECTIUNEA COMENZI - se executa doar daca llGetOrders = .T. +IF llGetOrders + ? "" + ? "=======================================" + ? "PRELUARE COMENZI DIN ULTIMELE " + TRANSFORM(loSettings.OrderDaysBack) + " ZILE" + ? "Data de start: " + lcStartDateStr + ? "=======================================" + + *-- Reinitializare pentru comenzi + lnCurrentPage = 1 + llHasMorePages = .T. + loAllOrderData = CREATEOBJECT("Empty") + ADDPROPERTY(loAllOrderData, "orders", CREATEOBJECT("Empty")) + ADDPROPERTY(loAllOrderData, "total", 0) + ADDPROPERTY(loAllOrderData, "pages", 0) + +*-- Bucla pentru preluarea comenzilor +DO WHILE llHasMorePages + *-- Construire URL cu paginare si filtrare pe data (folosind startDate conform documentatiei GoMag) + lcApiUrl = lcOrderApiUrl + "?startDate=" + lcStartDateStr + "&page=" + TRANSFORM(lnCurrentPage) + "&limit=" + TRANSFORM(lnLimit) + + ? "Preluare comenzi pagina " + TRANSFORM(lnCurrentPage) + "..." + + *-- Configurare request + TRY + *-- Initializare request GET + loHttp.Open("GET", lcApiUrl, .F.) + + *-- Setare headers conform documentatiei GoMag + loHttp.SetRequestHeader("User-Agent", lcUserAgent) + loHttp.SetRequestHeader("Content-Type", lcContentType) + loHttp.SetRequestHeader("Accept", "application/json") + loHttp.SetRequestHeader("Apikey", lcApiKey) && Header pentru API Key + loHttp.SetRequestHeader("ApiShop", lcApiShop) && Header pentru shop URL + + *-- Setari timeout + loHttp.SetTimeouts(30000, 30000, 30000, 30000) && 30 secunde pentru fiecare + + *-- Trimitere request + loHttp.Send() + + *-- Verificare status code + lnStatusCode = loHttp.Status + lcStatusText = loHttp.StatusText + + IF lnStatusCode = 200 + *-- Success - preluare raspuns + lcResponse = loHttp.ResponseText + + *-- Parsare JSON cu nfjson + SET PATH TO nfjson ADDITIVE + loJsonData = nfJsonRead(lcResponse) + + IF !ISNULL(loJsonData) + *-- Debug: Afisam structura JSON pentru prima pagina + IF lnCurrentPage = 1 + ? "DEBUG: Analiza structura JSON comenzi..." + lnPropCount = AMEMBERS(laJsonProps, loJsonData, 0) + FOR lnDebugIndex = 1 TO MIN(lnPropCount, 10) && Primele 10 proprietati + lcPropName = laJsonProps(lnDebugIndex) + lcPropType = TYPE('loJsonData.' + lcPropName) + ? " Proprietate: " + lcPropName + " (Tip: " + lcPropType + ")" + ENDFOR + ENDIF + + *-- Prima pagina - setam informatiile generale + IF lnCurrentPage = 1 + IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N' + loAllOrderData.total = VAL(TRANSFORM(loJsonData.total)) + ENDIF + IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' + loAllOrderData.pages = VAL(TRANSFORM(loJsonData.pages)) + ENDIF + ? "Total comenzi: " + TRANSFORM(loAllOrderData.total) + ? "Total pagini: " + TRANSFORM(loAllOrderData.pages) + ENDIF + + *-- Adaugare comenzi din pagina curenta + *-- API-ul GoMag returneaza un array direct de comenzi + LOCAL llHasOrders, lnOrdersFound + llHasOrders = .F. + lnOrdersFound = 0 + + *-- JSON-ul este direct un array de comenzi + lnDirectProps = AMEMBERS(laDirectProps, loJsonData, 0) + IF lnDirectProps > 0 + *-- Cream un obiect temporar cu structura asteptata + LOCAL loTempData + loTempData = CREATEOBJECT("Empty") + ADDPROPERTY(loTempData, "orders", loJsonData) + DO MergeOrdersArray WITH loAllOrderData, loTempData + llHasOrders = .T. + lnOrdersFound = lnDirectProps + ? " Gasit: " + TRANSFORM(lnDirectProps) + " comenzi in pagina " + TRANSFORM(lnCurrentPage) + ENDIF + + IF !llHasOrders + ? " ATENTIE: Nu s-au gasit comenzi in raspunsul JSON pentru pagina " + TRANSFORM(lnCurrentPage) + ENDIF + + *-- Verificare daca mai sunt pagini + IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' + lnTotalPages = VAL(TRANSFORM(loJsonData.pages)) + IF lnCurrentPage >= lnTotalPages + llHasMorePages = .F. + ENDIF + ELSE + *-- Daca nu avem info despre pagini, verificam daca sunt comenzi + IF !llHasOrders + llHasMorePages = .F. + ENDIF + ENDIF + + lnCurrentPage = lnCurrentPage + 1 + + ELSE + *-- Salvare raspuns JSON raw in caz de eroare de parsare + lcFileName = "gomag_order_error_page" + TRANSFORM(lnCurrentPage) + "_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" + STRTOFILE(lcResponse, lcFileName) + llHasMorePages = .F. + ENDIF + + ELSE + *-- Eroare HTTP - salvare in fisier de log + lcLogFileName = "gomag_order_error_page" + TRANSFORM(lnCurrentPage) + "_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".log" + lcLogContent = "HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + CHR(13) + CHR(10) + + *-- Incearca sa citesti raspunsul pentru detalii despre eroare + TRY + lcErrorResponse = loHttp.ResponseText + IF !EMPTY(lcErrorResponse) + lcLogContent = lcLogContent + "Error Details:" + CHR(13) + CHR(10) + lcErrorResponse + ENDIF + CATCH + lcLogContent = lcLogContent + "Could not read error details" + ENDTRY + + STRTOFILE(lcLogContent, lcLogFileName) + llHasMorePages = .F. + ENDIF + + CATCH TO loError + *-- Salvare erori in fisier de log pentru pagina curenta + lcLogFileName = "gomag_order_error_page" + TRANSFORM(lnCurrentPage) + "_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".log" + lcLogContent = "Script Error on page " + TRANSFORM(lnCurrentPage) + ":" + CHR(13) + CHR(10) +; + "Error Number: " + TRANSFORM(loError.ErrorNo) + CHR(13) + CHR(10) +; + "Error Message: " + loError.Message + CHR(13) + CHR(10) +; + "Error Line: " + TRANSFORM(loError.LineNo) + STRTOFILE(lcLogContent, lcLogFileName) + llHasMorePages = .F. + ENDTRY + + *-- Pauza scurta intre cereri pentru a evita rate limiting + IF llHasMorePages + INKEY(1) && Pauza de 1 secunda + ENDIF + +ENDDO + + *-- Salvare array JSON cu toate comenzile + IF !ISNULL(loAllOrderData) AND TYPE('loAllOrderData.orders') = 'O' + lcOrderJsonFileName = "gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" + DO SaveOrdersArray WITH loAllOrderData, lcOrderJsonFileName + ? "Fisier JSON cu comenzi creat: " + lcOrderJsonFileName + ENDIF + +ELSE + ? "SARIT PESTE PRELUAREA COMENZILOR (llGetOrders = .F.)" ENDIF *-- Curatare loHttp = NULL -*-- Functie pentru unirea produselor din toate paginile + +*-- Functie pentru salvarea array-ului de produse in format JSON +PROCEDURE SaveProductsArray +PARAMETERS tloAllData, tcFileName + +LOCAL lcJsonContent, lnPropCount, lnIndex, lcPropName, loProduct + +*-- Incepe array-ul JSON +lcJsonContent = "[" + CHR(13) + CHR(10) + +*-- Verifica daca avem produse +IF TYPE('tloAllData.products') = 'O' + lnPropCount = AMEMBERS(laProducts, tloAllData.products, 0) + + FOR lnIndex = 1 TO lnPropCount + lcPropName = laProducts(lnIndex) + loProduct = EVALUATE('tloAllData.products.' + lcPropName) + + IF TYPE('loProduct') = 'O' + *-- Adauga virgula pentru elementele anterioare + IF lnIndex > 1 + lcJsonContent = lcJsonContent + "," + CHR(13) + CHR(10) + ENDIF + + *-- Serializeaza produsul cu nfjsoncreate + SET PROCEDURE TO nfjsoncreate.prg ADDITIVE + lcProductJson = nfJsonCreate(loProduct, .F.) + lcJsonContent = lcJsonContent + " " + lcProductJson + ENDIF + ENDFOR +ENDIF + +*-- Inchide array-ul JSON +lcJsonContent = lcJsonContent + CHR(13) + CHR(10) + "]" + +*-- Salveaza fisierul +STRTOFILE(lcJsonContent, tcFileName) + +ENDPROC + +*-- Functie pentru salvarea array-ului de comenzi in format JSON +PROCEDURE SaveOrdersArray +PARAMETERS tloAllData, tcFileName + +LOCAL lcJsonContent, lnPropCount, lnIndex, lcPropName, loOrder + +*-- Incepe array-ul JSON +lcJsonContent = "[" + CHR(13) + CHR(10) + +*-- Verifica daca avem comenzi +IF TYPE('tloAllData.orders') = 'O' + lnPropCount = AMEMBERS(laOrders, tloAllData.orders, 0) + + FOR lnIndex = 1 TO lnPropCount + lcPropName = laOrders(lnIndex) + loOrder = EVALUATE('tloAllData.orders.' + lcPropName) + + IF TYPE('loOrder') = 'O' + *-- Adauga virgula pentru elementele anterioare + IF lnIndex > 1 + lcJsonContent = lcJsonContent + "," + CHR(13) + CHR(10) + ENDIF + + *-- Serializeaza comanda cu nfjsoncreate + SET PROCEDURE TO nfjsoncreate.prg ADDITIVE + lcOrderJson = nfJsonCreate(loOrder, .F.) + lcJsonContent = lcJsonContent + " " + lcOrderJson + ENDIF + ENDFOR +ENDIF + +*-- Inchide array-ul JSON +lcJsonContent = lcJsonContent + CHR(13) + CHR(10) + "]" + +*-- Salveaza fisierul +STRTOFILE(lcJsonContent, tcFileName) + +ENDPROC + +*-- Functie pentru unirea produselor din toate paginile (versiune simpla) PROCEDURE MergeProducts PARAMETERS tloAllData, tloPageData @@ -198,176 +497,58 @@ ENDIF ENDPROC -*-- Functie pentru salvarea datelor JSON complete -PROCEDURE SaveCompleteJson -PARAMETERS tloJsonData, tcFileName +*-- Functie pentru unirea comenzilor din array direct (structura GoMag) +PROCEDURE MergeOrdersArray +PARAMETERS tloAllData, tloPageData -LOCAL lcJsonContent +LOCAL lnPropCount, lnIndex, lcPropName, loOrder -*-- Construieste JSON simplu pentru salvare -lcJsonContent = '{' + CHR(13) + CHR(10) -lcJsonContent = lcJsonContent + ' "total": ' + TRANSFORM(tloJsonData.total) + ',' + CHR(13) + CHR(10) -lcJsonContent = lcJsonContent + ' "pages": ' + TRANSFORM(tloJsonData.pages) + ',' + CHR(13) + CHR(10) -lcJsonContent = lcJsonContent + ' "products": {' + CHR(13) + CHR(10) - -*-- Adauga produsele (versiune simplificata) -LOCAL lnPropCount, lnIndex, lcPropName, loProduct -lnPropCount = AMEMBERS(laProducts, tloJsonData.products, 0) - -FOR lnIndex = 1 TO lnPropCount - lcPropName = laProducts(lnIndex) - loProduct = EVALUATE('tloJsonData.products.' + lcPropName) - - IF TYPE('loProduct') = 'O' - lcJsonContent = lcJsonContent + ' "' + lcPropName + '": {' - - IF TYPE('loProduct.id') = 'C' - lcJsonContent = lcJsonContent + '"id": "' + loProduct.id + '",' - ENDIF - IF TYPE('loProduct.sku') = 'C' - lcJsonContent = lcJsonContent + '"sku": "' + loProduct.sku + '",' - ENDIF - IF TYPE('loProduct.name') = 'C' - lcJsonContent = lcJsonContent + '"name": "' + STRTRAN(loProduct.name, '"', '\"') + '",' - ENDIF - - *-- Elimina ultima virgula - IF RIGHT(lcJsonContent, 1) = ',' - lcJsonContent = LEFT(lcJsonContent, LEN(lcJsonContent) - 1) - ENDIF - - lcJsonContent = lcJsonContent + '}' - - IF lnIndex < lnPropCount - lcJsonContent = lcJsonContent + ',' - ENDIF - - lcJsonContent = lcJsonContent + CHR(13) + CHR(10) - ENDIF -ENDFOR - -lcJsonContent = lcJsonContent + ' }' + CHR(13) + CHR(10) -lcJsonContent = lcJsonContent + '}' + CHR(13) + CHR(10) - -STRTOFILE(lcJsonContent, tcFileName) - -ENDPROC - -*-- Functie pentru crearea fisierului CSV din datele JSON -PROCEDURE CreateCsvFromJson -PARAMETERS tloJsonData, tcCsvFileName - -LOCAL lcCsvContent, lcCsvHeader, lcCsvRow -LOCAL lnProductCount, lnIndex -LOCAL loProduct - -lcCsvContent = "" -lcCsvHeader = "ID,SKU,Name,Brand,Weight,Stock,Base_Price,Price,VAT_Included,Enabled,VAT,Currency,Ecotax" + CHR(13) + CHR(10) -lcCsvContent = lcCsvHeader - -*-- Verifica daca avem produse in raspuns -IF TYPE('tloJsonData.products') = 'O' - *-- Itereaza prin toate produsele - lnPropCount = AMEMBERS(laProducts, tloJsonData.products, 0) - - ? "Procesare " + TRANSFORM(lnPropCount) + " produse pentru CSV..." +*-- Verifica daca avem comenzi in pagina curenta +IF TYPE('tloPageData.orders') = 'O' + *-- Itereaza prin toate comenzile din pagina (array direct) + lnPropCount = AMEMBERS(laPageOrders, tloPageData.orders, 0) FOR lnIndex = 1 TO lnPropCount - lcPropName = laProducts(lnIndex) - loProduct = EVALUATE('tloJsonData.products.' + lcPropName) + lcPropName = laPageOrders(lnIndex) + loOrder = EVALUATE('tloPageData.orders.' + lcPropName) - IF TYPE('loProduct') = 'O' - *-- Extrage datele produsului - lcCsvRow = ; - IIF(TYPE('loProduct.id')='C', STRTRAN(loProduct.id, ',', ';'), '') + ',' +; - IIF(TYPE('loProduct.sku')='C', STRTRAN(loProduct.sku, ',', ';'), '') + ',' +; - IIF(TYPE('loProduct.name')='C', '"' + STRTRAN(STRTRAN(loProduct.name, '"', '""'), ',', ';') + '"', '') + ',' +; - IIF(TYPE('loProduct.brand')='C', STRTRAN(loProduct.brand, ',', ';'), '') + ',' +; - IIF(TYPE('loProduct.weight')='C', loProduct.weight, IIF(TYPE('loProduct.weight')='N', TRANSFORM(loProduct.weight), '')) + ',' +; - IIF(TYPE('loProduct.stock')='C', loProduct.stock, IIF(TYPE('loProduct.stock')='N', TRANSFORM(loProduct.stock), '')) + ',' +; - IIF(TYPE('loProduct.base_price')='C', loProduct.base_price, IIF(TYPE('loProduct.base_price')='N', TRANSFORM(loProduct.base_price), '')) + ',' +; - IIF(TYPE('loProduct.price')='C', loProduct.price, IIF(TYPE('loProduct.price')='N', TRANSFORM(loProduct.price), '')) + ',' +; - IIF(TYPE('loProduct.vat_included')='C', loProduct.vat_included, IIF(TYPE('loProduct.vat_included')='N', TRANSFORM(loProduct.vat_included), '')) + ',' +; - IIF(TYPE('loProduct.enabled')='C', loProduct.enabled, IIF(TYPE('loProduct.enabled')='N', TRANSFORM(loProduct.enabled), '')) + ',' +; - IIF(TYPE('loProduct.vat')='C', loProduct.vat, IIF(TYPE('loProduct.vat')='N', TRANSFORM(loProduct.vat), '')) + ',' +; - IIF(TYPE('loProduct.currency')='C', loProduct.currency, '') + ',' +; - IIF(TYPE('loProduct.ecotax')='C', loProduct.ecotax, IIF(TYPE('loProduct.ecotax')='N', TRANSFORM(loProduct.ecotax), '')) +; - CHR(13) + CHR(10) + IF TYPE('loOrder') = 'O' + *-- Folosim ID-ul comenzii ca nume proprietate, sau un index secvential + LOCAL lcOrderId + lcOrderId = "" + IF TYPE('loOrder.id') = 'C' + lcOrderId = "order_" + loOrder.id + ELSE + lcOrderId = "order_" + TRANSFORM(lnIndex) + ENDIF - lcCsvContent = lcCsvContent + lcCsvRow + *-- Adauga comanda la colectia principala + ADDPROPERTY(tloAllData.orders, lcOrderId, loOrder) ENDIF ENDFOR ENDIF -*-- Salvare fisier CSV -STRTOFILE(lcCsvContent, tcCsvFileName) -? "CSV salvat cu " + TRANSFORM(lnPropCount) + " produse" - ENDPROC -*-- Functii helper pentru testare (optionale) +*-- Functiile utilitare au fost mutate in utils.prg -*-- Test conectivitate internet -FUNCTION TestConnectivity -LOCAL loHttp, llResult - -llResult = .T. - -TRY - loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") - loHttp.Open("GET", "https://www.google.com", .F.) - loHttp.SetTimeouts(5000, 5000, 5000, 5000) - loHttp.Send() - - IF loHttp.Status != 200 - llResult = .F. - ENDIF - -CATCH - llResult = .F. -ENDTRY - -loHttp = NULL -RETURN llResult - -ENDFUNC - -*-- Functie pentru codificare URL -FUNCTION UrlEncode -PARAMETERS tcString - -LOCAL lcResult, lcChar, lnI - -lcResult = "" - -FOR lnI = 1 TO LEN(tcString) - lcChar = SUBSTR(tcString, lnI, 1) - - DO CASE - CASE ISALPHA(lcChar) OR ISDIGIT(lcChar) OR INLIST(lcChar, "-", "_", ".", "~") - lcResult = lcResult + lcChar - OTHERWISE - lcResult = lcResult + "%" + RIGHT("0" + TRANSFORM(ASC(lcChar), "@0"), 2) - ENDCASE -ENDFOR - -RETURN lcResult - -ENDFUNC - -*-- Scriptul cu paginare completa pentru preluarea tuturor produselor +*-- Scriptul cu paginare completa pentru preluarea tuturor produselor si comenzilor *-- Caracteristici principale: -*-- - Paginare automata pentru toate produsele (100 per pagina) +*-- - Paginare automata pentru toate produsele si comenzile *-- - Pauze intre cereri pentru respectarea rate limiting -*-- - Creare fisier CSV cu toate produsele -*-- - Salvare fisier JSON complet cu toate datele +*-- - Salvare JSON array-uri pure (fara metadata de paginare) +*-- - Utilizare nfjsoncreate pentru generare JSON corecta *-- - Logging separat pentru fiecare pagina in caz de eroare *-- - Afisare progres in timpul executiei *-- INSTRUCTIUNI DE UTILIZARE: -*-- 1. Modifica lcApiKey cu cheia ta API de la GoMag -*-- 2. Modifica lcApiShop cu URL-ul magazinului tau -*-- 3. Ruleaza scriptul - va prelua automat toate produsele -*-- 4. Verifica fisierele generate: CSV si JSON cu toate produsele +*-- 1. Modifica settings.ini cu setarile tale: +*-- - ApiKey: cheia ta API de la GoMag +*-- - ApiShop: URL-ul magazinului tau +*-- - GetProducts: 1 pentru a prelua produse, 0 pentru a sari peste +*-- - GetOrders: 1 pentru a prelua comenzi, 0 pentru a sari peste +*-- - OrderDaysBack: numarul de zile pentru preluarea comenzilor +*-- 2. Ruleaza scriptul - va prelua doar ce ai selectat +*-- 3. Verifica fisierele JSON generate cu array-uri pure -*-- Script completat cu paginare - verificati fisierele generate \ No newline at end of file +*-- Script optimizat cu salvare JSON array-uri - verificati fisierele generate \ No newline at end of file diff --git a/nfjson/nfjsonread.FXP b/nfjson/nfjsonread.FXP deleted file mode 100644 index e74ccdc..0000000 Binary files a/nfjson/nfjsonread.FXP and /dev/null differ diff --git a/run-gomag.bat b/run-gomag.bat new file mode 100644 index 0000000..53f81f7 --- /dev/null +++ b/run-gomag.bat @@ -0,0 +1,4 @@ +@echo off +cd /d "%~dp0" +"C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" -T "%~dp0gomag-vending-test.prg" +pause \ No newline at end of file diff --git a/settings.ini b/settings.ini new file mode 100644 index 0000000..00bbeea --- /dev/null +++ b/settings.ini @@ -0,0 +1,17 @@ +[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=0 +GetOrders=1 + +[FILTERS] +OrderDaysBack=7 \ No newline at end of file diff --git a/utils.prg b/utils.prg new file mode 100644 index 0000000..33aa03e --- /dev/null +++ b/utils.prg @@ -0,0 +1,174 @@ +*-- utils.prg - Utilitare pentru GoMag API +*-- Functii pentru citirea/scrierea fisierelor INI si alte utilitare +*-- Autor: Claude AI +*-- Data: 27.08.2025 + +*-- Functie pentru citirea fisierelor INI private +*-- Returneaza valoarea din sectiunea si intrarea specificata sau blank daca nu e gasita +FUNCTION ReadPini +PARAMETERS cSection, cEntry, cINIFile +LOCAL cDefault, cRetVal, nRetLen + +cDefault = "" +cRetVal = SPACE(255) +nRetLen = LEN(cRetVal) + +DECLARE INTEGER GetPrivateProfileString IN WIN32API ; + STRING cSection, ; + STRING cEntry, ; + STRING cDefault, ; + STRING @cRetVal, ; + INTEGER nRetLen, ; + STRING cINIFile + +nRetLen = GetPrivateProfileString(cSection, ; + cEntry, ; + cDefault, ; + @cRetVal, ; + nRetLen, ; + cINIFile) + +RETURN LEFT(cRetVal, nRetLen) +ENDFUNC + +*-- Functie pentru scrierea in fisierele INI private +*-- Returneaza .T. daca e successful, .F. daca nu +FUNCTION WritePini +PARAMETERS cSection, cEntry, cValue, cINIFile +LOCAL nRetVal + +DECLARE INTEGER WritePrivateProfileString IN WIN32API ; + STRING cSection, ; + STRING cEntry, ; + STRING cValue, ; + STRING cINIFile + +nRetVal = WritePrivateProfileString(cSection, ; + cEntry, ; + cValue, ; + cINIFile) + +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 +LOCAL loHttp, llResult + +llResult = .T. + +TRY + loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") + loHttp.Open("GET", "https://www.google.com", .F.) + loHttp.SetTimeouts(5000, 5000, 5000, 5000) + loHttp.Send() + + IF loHttp.Status != 200 + llResult = .F. + ENDIF + +CATCH + llResult = .F. +ENDTRY + +loHttp = NULL +RETURN llResult +ENDFUNC + +*-- Functie pentru codificare URL +FUNCTION UrlEncode +PARAMETERS tcString + +LOCAL lcResult, lcChar, lnI + +lcResult = "" + +FOR lnI = 1 TO LEN(tcString) + lcChar = SUBSTR(tcString, lnI, 1) + + DO CASE + CASE ISALPHA(lcChar) OR ISDIGIT(lcChar) OR INLIST(lcChar, "-", "_", ".", "~") + lcResult = lcResult + lcChar + OTHERWISE + lcResult = lcResult + "%" + RIGHT("0" + TRANSFORM(ASC(lcChar), "@0"), 2) + ENDCASE +ENDFOR + +RETURN lcResult +ENDFUNC + +*-- Functie pentru verificarea existentei fisierului INI +FUNCTION CheckIniFile +PARAMETERS cINIFile +LOCAL llExists + +TRY + llExists = FILE(cINIFile) +CATCH + llExists = .F. +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 \ No newline at end of file