*-- Script Visual FoxPro 9 pentru accesul la GoMag API cu paginare completa *-- Autor: Claude AI *-- Data: 26.08.2025 SET SAFETY OFF SET CENTURY ON SET DATE DMY SET EXACT ON SET ANSI ON SET DELETED ON *-- Setari principale LOCAL lcApiBaseUrl, lcApiUrl, lcApiKey, lcUserAgent, lcContentType LOCAL loHttp, lcResponse, lcJsonResponse 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 LOCAL llGetProducts, llGetOrders PRIVATE loJsonData, gcLogFile, gnStartTime, gnProductsProcessed, gnOrdersProcessed *-- Initializare logging si statistici gnStartTime = SECONDS() gnProductsProcessed = 0 gnOrdersProcessed = 0 gcLogFile = InitLog("gomag_sync") *-- Cream directorul output daca nu existe LOCAL lcOutputDir lcOutputDir = gcAppPath + "output" IF !DIRECTORY(lcOutputDir) MKDIR (lcOutputDir) ENDIF *-- Creare si initializare clasa setup aplicatie LOCAL loAppSetup loAppSetup = CREATEOBJECT("ApplicationSetup", gcAppPath) *-- Setup complet cu validare IF !loAppSetup.Initialize() LogMessage("EROARE: Setup-ul aplicatiei a esuat sau necesita configurare!", "ERROR", gcLogFile) RETURN .F. ENDIF *-- Configurare API din settings.ini lcApiBaseUrl = goSettings.ApiBaseUrl lcOrderApiUrl = goSettings.OrderApiUrl lcApiKey = goSettings.ApiKey lcApiShop = goSettings.ApiShop lcUserAgent = goSettings.UserAgent lcContentType = goSettings.ContentType lnLimit = goSettings.Limit llGetProducts = goSettings.GetProducts llGetOrders = goSettings.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() - goSettings.OrderDaysBack lcStartDateStr = TRANSFORM(YEAR(ldStartDate)) + "-" + ; RIGHT("0" + TRANSFORM(MONTH(ldStartDate)), 2) + "-" + ; RIGHT("0" + TRANSFORM(DAY(ldStartDate)), 2) ******************************************* *-- Sterg fisiere JSON comenzi anterioare lcDirJson = gcAppPath + "output\" lcJsonPattern = m.lcDirJson + goSettings.JsonFilePattern lnJsonFiles = ADIR(laJsonFiles, lcJsonPattern) FOR lnFile = 1 TO m.lnJsonFiles lcFile = m.lcDirJson + laJsonFiles[m.lnFile,1] IF FILE(m.lcFile) DELETE FILE (m.lcFile) ENDIF ENDFOR ******************************************* *-- Verificare daca avem WinHttp disponibil TRY loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") CATCH TO loError LogMessage("Eroare la crearea obiectului WinHttp: " + loError.Message, "ERROR", gcLogFile) RETURN .F. ENDTRY *-- SECTIUNEA PRODUSE - se executa doar daca llGetProducts = .T. IF llGetProducts LogMessage("[PRODUCTS] Starting product retrieval", "INFO", gcLogFile) *-- 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 *-- Construire URL cu paginare lcApiUrl = lcApiBaseUrl + "&page=" + TRANSFORM(lnCurrentPage) + "&limit=" + TRANSFORM(lnLimit) LogMessage("[PRODUCTS] Page " + TRANSFORM(lnCurrentPage) + " fetching...", "INFO", gcLogFile) *-- 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) *-- Prima pagina - setam informatiile generale IF lnCurrentPage = 1 LogMessage("[PRODUCTS] Analyzing JSON structure...", "INFO", gcLogFile) LOCAL ARRAY laJsonProps[1] lnPropCount = AMEMBERS(laJsonProps, loJsonData, 0) FOR lnDebugIndex = 1 TO MIN(lnPropCount, 10) && Primele 10 proprietati lcPropName = laJsonProps(lnDebugIndex) lcPropType = TYPE('loJsonData.' + lcPropName) LogMessage("[PRODUCTS] Property: " + lcPropName + " (Type: " + lcPropType + ")", "DEBUG", gcLogFile) ENDFOR IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N' loAllJsonData.total = VAL(TRANSFORM(loJsonData.total)) ENDIF IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' loAllJsonData.pages = VAL(TRANSFORM(loJsonData.pages)) ENDIF LogMessage("[PRODUCTS] Total items: " + TRANSFORM(loAllJsonData.total) + " | Pages: " + TRANSFORM(loAllJsonData.pages), "INFO", gcLogFile) ENDIF *-- Adaugare produse din pagina curenta LOCAL llHasProducts, lnProductsFound llHasProducts = .F. lnProductsFound = 0 IF TYPE('loJsonData.products') = 'O' *-- Numaram produsele din obiectul products lnProductsFound = AMEMBERS(laProductsPage, loJsonData.products, 0) IF lnProductsFound > 0 DO MergeProducts WITH loAllJsonData, loJsonData llHasProducts = .T. LogMessage("[PRODUCTS] Found: " + TRANSFORM(lnProductsFound) + " products in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile) gnProductsProcessed = gnProductsProcessed + lnProductsFound ENDIF ENDIF IF !llHasProducts LogMessage("[PRODUCTS] WARNING: No products found in JSON response for page " + TRANSFORM(lnCurrentPage), "WARN", gcLogFile) 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 produse IF TYPE('loJsonData.products') != 'O' llHasMorePages = .F. ENDIF ENDIF lnCurrentPage = lnCurrentPage + 1 ELSE *-- Salvare raspuns JSON raw in caz de eroare de parsare lcFileName = "gomag_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_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_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 IF llHasMorePages INKEY(1) && Pauza de 10 secunde pentru a evita "Limitele API depasite" ENDIF ENDDO *-- Salvare array JSON cu toate produsele IF !ISNULL(loAllJsonData) AND TYPE('loAllJsonData.products') = 'O' lcJsonFileName = lcOutputDir + "\gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" DO SaveProductsArray WITH loAllJsonData, lcJsonFileName LogMessage("[PRODUCTS] JSON saved: " + lcJsonFileName, "INFO", gcLogFile) *-- Calculam numarul de produse procesate IF TYPE('loAllJsonData.products') = 'O' LOCAL ARRAY laProducts[1] lnPropCount = AMEMBERS(laProducts, loAllJsonData.products, 0) gnProductsProcessed = lnPropCount ENDIF ENDIF ELSE LogMessage("[PRODUCTS] Skipped product retrieval (llGetProducts = .F.)", "INFO", gcLogFile) ENDIF *-- SECTIUNEA COMENZI - se executa doar daca llGetOrders = .T. IF llGetOrders LogMessage("[ORDERS] =======================================", "INFO", gcLogFile) LogMessage("[ORDERS] RETRIEVING ORDERS FROM LAST " + TRANSFORM(goSettings.OrderDaysBack) + " DAYS", "INFO", gcLogFile) LogMessage("[ORDERS] Start date: " + lcStartDateStr, "INFO", gcLogFile) LogMessage("[ORDERS] =======================================", "INFO", gcLogFile) *-- 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) LogMessage("[ORDERS] Page " + TRANSFORM(lnCurrentPage) + " fetching...", "INFO", gcLogFile) *-- 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 *-- SALVARE DIRECTA: Salveaza raspunsul RAW exact cum vine din API, pe pagini lcOrderJsonFileName = lcOutputDir + "\gomag_orders_page" + TRANSFORM(lnCurrentPage) + "_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" STRTOFILE(lcResponse, lcOrderJsonFileName) LogMessage("[ORDERS] JSON RAW salvat: " + lcOrderJsonFileName, "INFO", gcLogFile) *-- Parsare JSON pentru obtinerea numarului de pagini SET PATH TO nfjson ADDITIVE loOrdersJsonData = nfJsonRead(lcResponse) IF !ISNULL(loOrdersJsonData) *-- Extragere informatii paginare din JSON procesat IF lnCurrentPage = 1 IF TYPE('loOrdersJsonData.total') = 'C' OR TYPE('loOrdersJsonData.total') = 'N' LOCAL lnTotalOrders lnTotalOrders = VAL(TRANSFORM(loOrdersJsonData.total)) LogMessage("[ORDERS] Total orders: " + TRANSFORM(lnTotalOrders), "INFO", gcLogFile) ENDIF ENDIF IF TYPE('loOrdersJsonData.pages') = 'C' OR TYPE('loOrdersJsonData.pages') = 'N' lnTotalPages = VAL(TRANSFORM(loOrdersJsonData.pages)) IF lnCurrentPage = 1 LogMessage("[ORDERS] Total pages: " + TRANSFORM(lnTotalPages), "INFO", gcLogFile) ENDIF IF lnCurrentPage >= lnTotalPages llHasMorePages = .F. LogMessage("[ORDERS] Reached last page (" + TRANSFORM(lnCurrentPage) + "/" + TRANSFORM(lnTotalPages) + ")", "INFO", gcLogFile) ENDIF ELSE *-- Fallback: verificare daca mai sunt comenzi in pagina IF TYPE('loOrdersJsonData.orders') != 'O' llHasMorePages = .F. LogMessage("[ORDERS] No orders found in response, stopping pagination", "INFO", gcLogFile) ENDIF ENDIF *-- Numarare comenzi din pagina curenta IF TYPE('loOrdersJsonData.orders') = 'O' LOCAL lnOrdersInPage lnOrdersInPage = AMEMBERS(laOrdersPage, loOrdersJsonData.orders, 0) gnOrdersProcessed = gnOrdersProcessed + lnOrdersInPage LogMessage("[ORDERS] Found " + TRANSFORM(lnOrdersInPage) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile) ENDIF ELSE *-- Eroare la parsarea JSON LogMessage("[ORDERS] ERROR: Could not parse JSON response for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) llHasMorePages = .F. ENDIF lnCurrentPage = lnCurrentPage + 1 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 IF llHasMorePages INKEY(1) && Pauza de 10 secunde pentru a evita "Limitele API depasite" ENDIF ENDDO LogMessage("[ORDERS] JSON files salvate pe pagini separate in directorul output/", "INFO", gcLogFile) LogMessage("[ORDERS] Total orders processed: " + TRANSFORM(gnOrdersProcessed), "INFO", gcLogFile) ELSE LogMessage("[ORDERS] Skipped order retrieval (llGetOrders = .F.)", "INFO", gcLogFile) ENDIF *-- Curatare loHttp = NULL *-- Inchidere logging cu statistici finale CloseLog(gnStartTime, gnProductsProcessed, gnOrdersProcessed, gcLogFile) *-- Functiile utilitare au fost mutate in utils.prg *-- Scriptul cu paginare completa pentru preluarea tuturor produselor si comenzilor *-- Caracteristici principale: *-- - Paginare automata pentru toate produsele si comenzile *-- - Pauze intre cereri pentru respectarea rate limiting *-- - 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 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 optimizat cu salvare JSON array-uri - verificati fisierele generate