*-- Script Visual FoxPro 9 pentru accesul la GoMag API cu paginare completa *-- Autor: Claude AI *-- Data: 26.08.2025 SET SAFETY OFF SET EXACT ON SET CENTURY ON SET DELETED ON SET DATE DMY *-- 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, loSettings LOCAL llGetProducts, llGetOrders PRIVATE gcAppPath, loJsonData, gcLogFile, gnStartTime, gnProductsProcessed, gnOrdersProcessed gcAppPath = ADDBS(JUSTPATH(SYS(16,0))) SET DEFAULT TO (m.gcAppPath) lcPath = gcAppPath + 'nfjson;' SET PATH TO (m.lcPath) ADDITIVE SET PROCEDURE TO nfjsonread.prg ADDITIVE SET PROCEDURE TO utils.prg ADDITIVE SET PROCEDURE TO nfjsoncreate.prg ADDITIVE *-- 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 *-- Incarcarea setarilor din fisierul INI lcIniFile = gcAppPath + "settings.ini" *-- 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) 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 *-- 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") CATCH TO loError LogMessage("Eroare la crearea obiectului WinHttp: " + loError.Message, "ERROR", gcLogFile) RETURN .F. ENDTRY *-- Removed SET STEP ON for silent operation *-- 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 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 IF TYPE('loJsonData.products') = 'O' DO MergeProducts WITH loAllJsonData, loJsonData 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 *-- Eroare parsare JSON LogMessage("[PRODUCTS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) llHasMorePages = .F. ENDIF ELSE *-- Eroare HTTP LogMessage("[PRODUCTS] HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + " on page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) *-- Detalii despre eroare daca sunt disponibile TRY lcErrorResponse = loHttp.ResponseText IF !EMPTY(lcErrorResponse) LogMessage("[PRODUCTS] Error details: " + LEFT(lcErrorResponse, 200), "ERROR", gcLogFile) ENDIF CATCH LogMessage("[PRODUCTS] Could not read error details", "WARN", gcLogFile) ENDTRY llHasMorePages = .F. ENDIF CATCH TO loError *-- Script error in products section LogMessage("[PRODUCTS] Script Error #" + TRANSFORM(loError.ErrorNo) + ": " + loError.Message + " (Line: " + TRANSFORM(loError.LineNo) + ")", "ERROR", gcLogFile) 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 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' lnPropCount = AMEMBERS(laProducts, loAllJsonData.products, 0) gnProductsProcessed = lnPropCount ENDIF ENDIF ELSE LogMessage("[PRODUCTS] Skipped (disabled in settings)", "INFO", gcLogFile) ENDIF *-- SECTIUNEA COMENZI - se executa doar daca llGetOrders = .T. IF llGetOrders LogMessage("[ORDERS] Starting orders retrieval (last " + TRANSFORM(loSettings.OrderDaysBack) + " days from " + lcStartDateStr + ")", "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 *-- 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 LogMessage("[ORDERS] DEBUG: Analyzing JSON structure...", "DEBUG", gcLogFile) lnPropCount = AMEMBERS(laJsonProps, loJsonData, 0) FOR lnDebugIndex = 1 TO MIN(lnPropCount, 10) && Primele 10 proprietati lcPropName = laJsonProps(lnDebugIndex) lcPropType = TYPE('loJsonData.' + lcPropName) LogMessage("[ORDERS] Property: " + lcPropName + " (Type: " + lcPropType + ")", "DEBUG", gcLogFile) 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 LogMessage("[ORDERS] Total items: " + TRANSFORM(loAllOrderData.total) + " | Pages: " + TRANSFORM(loAllOrderData.pages), "INFO", gcLogFile) ENDIF *-- Adaugare comenzi din pagina curenta *-- API-ul GoMag returneaza un array direct de comenzi LOCAL llHasOrders, lnOrdersFound llHasOrders = .F. lnOrdersFound = 0 *-- Verificam daca JSON-ul contine proprietatea orders IF TYPE('loJsonData.orders') = 'O' *-- Numaram comenzile din obiectul orders LOCAL ARRAY laOrdersProps[1] LOCAL lnOrdersCount lnOrdersCount = AMEMBERS(laOrdersProps, loJsonData.orders, 0) IF lnOrdersCount > 0 DO MergeOrdersArray WITH loAllOrderData, loJsonData llHasOrders = .T. lnOrdersFound = lnOrdersCount LogMessage("[ORDERS] Found " + TRANSFORM(lnOrdersCount) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile) gnOrdersProcessed = gnOrdersProcessed + lnOrdersCount ENDIF ELSE *-- JSON-ul este direct un array de comenzi (backup logic) 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 LogMessage("[ORDERS] Found " + TRANSFORM(lnDirectProps) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile) gnOrdersProcessed = gnOrdersProcessed + lnDirectProps ENDIF ENDIF IF !llHasOrders LogMessage("[ORDERS] WARNING: No orders 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 comenzi IF !llHasOrders llHasMorePages = .F. ENDIF ENDIF lnCurrentPage = lnCurrentPage + 1 ELSE *-- Eroare parsare JSON pentru comenzi LogMessage("[ORDERS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) llHasMorePages = .F. ENDIF ELSE *-- Eroare HTTP pentru comenzi LogMessage("[ORDERS] HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + " on page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) *-- Detalii despre eroare daca sunt disponibile TRY lcErrorResponse = loHttp.ResponseText IF !EMPTY(lcErrorResponse) LogMessage("[ORDERS] Error details: " + LEFT(lcErrorResponse, 200), "ERROR", gcLogFile) ENDIF CATCH LogMessage("[ORDERS] Could not read error details", "WARN", gcLogFile) ENDTRY llHasMorePages = .F. ENDIF CATCH TO loError *-- Script error in orders section LogMessage("[ORDERS] Script Error #" + TRANSFORM(loError.ErrorNo) + ": " + loError.Message + " (Line: " + TRANSFORM(loError.LineNo) + ")", "ERROR", gcLogFile) 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 = lcOutputDir + "\gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" DO SaveOrdersArray WITH loAllOrderData, lcOrderJsonFileName LogMessage("[ORDERS] JSON saved: " + lcOrderJsonFileName, "INFO", gcLogFile) ENDIF ELSE LogMessage("[ORDERS] Skipped (disabled in settings)", "INFO", gcLogFile) ENDIF *-- Curatare loHttp = NULL *-- Inchidere logging cu statistici finale CloseLog(gnStartTime, gnProductsProcessed, gnOrdersProcessed, gcLogFile) *-- 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 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 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 LOCAL lnPropCount, lnIndex, lcPropName, loProduct *-- Verifica daca avem produse in pagina curenta IF TYPE('tloPageData.products') = 'O' *-- Itereaza prin toate produsele din pagina lnPropCount = AMEMBERS(laPageProducts, tloPageData.products, 0) FOR lnIndex = 1 TO lnPropCount lcPropName = laPageProducts(lnIndex) loProduct = EVALUATE('tloPageData.products.' + lcPropName) IF TYPE('loProduct') = 'O' *-- Adauga produsul la colectia principala ADDPROPERTY(tloAllData.products, lcPropName, loProduct) ENDIF ENDFOR ENDIF ENDPROC *-- Functie pentru unirea comenzilor din array direct (structura GoMag) PROCEDURE MergeOrdersArray PARAMETERS tloAllData, tloPageData LOCAL lnPropCount, lnIndex, lcPropName, loOrder *-- 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 = laPageOrders(lnIndex) loOrder = EVALUATE('tloPageData.orders.' + lcPropName) 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 *-- Adauga comanda la colectia principala ADDPROPERTY(tloAllData.orders, lcOrderId, loOrder) ENDIF ENDFOR ENDIF ENDPROC *-- 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