From fd48ca480bfe00a365628b981afb7f6ef11883a4 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Wed, 27 Aug 2025 16:26:21 +0300 Subject: [PATCH] Fix orders counting bug and improve logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed incorrect order count in logging (counted JSON properties instead of actual orders) - Added proper array declaration for AMEMBERS() function - Added comprehensive logging system with timestamps and levels - Added silent operation mode (removed SET STEP ON) - Added output directory creation - Enhanced error handling with proper logging instead of file dumps - Added statistics tracking for products and orders processed 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .gitignore | 1 + gomag-vending-test.prg | 179 +++++++++++++++++++++++------------------ settings.ini | 2 +- utils.prg | 90 +++++++++++++++++++++ 4 files changed, 191 insertions(+), 81 deletions(-) diff --git a/.gitignore b/.gitignore index 1efc06b..e3c3304 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.json *.err *.ERR +*.log diff --git a/gomag-vending-test.prg b/gomag-vending-test.prg index ca04c9c..c0c4d9b 100644 --- a/gomag-vending-test.prg +++ b/gomag-vending-test.prg @@ -2,6 +2,12 @@ *-- 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 @@ -13,7 +19,7 @@ Local lcOrderApiUrl, loAllOrderData, lcOrderCsvFileName, lcOrderJsonFileName Local ldStartDate, lcStartDateStr Local lcIniFile, loSettings LOCAL llGetProducts, llGetOrders -PRIVATE gcAppPath, loJsonData +PRIVATE gcAppPath, loJsonData, gcLogFile, gnStartTime, gnProductsProcessed, gnOrdersProcessed @@ -24,20 +30,34 @@ 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) - ? "ATENTIE: Fisierul settings.ini nu a fost gasit!" - ? "Cream un fisier settings.ini implicit..." + LogMessage("ATENTIE: Fisierul settings.ini nu a fost gasit!", "WARN", gcLogFile) + LogMessage("Cream un fisier settings.ini implicit...", "INFO", gcLogFile) IF CreateDefaultIni(lcIniFile) - ? "Fisier settings.ini creat cu succes." - ? "IMPORTANT: Modifica setarile din settings.ini (ApiKey, ApiShop) inainte de a rula scriptul din nou!" + 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 - ? "EROARE: Nu s-a putut crea fisierul settings.ini!" + LogMessage("EROARE: Nu s-a putut crea fisierul settings.ini!", "ERROR", gcLogFile) RETURN .F. ENDIF ENDIF @@ -47,12 +67,12 @@ 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!" + LogMessage("EROARE: ApiKey nu este setat in settings.ini!", "ERROR", gcLogFile) RETURN .F. ENDIF IF EMPTY(loSettings.ApiShop) OR "yourstore.gomag.ro" $ loSettings.ApiShop - ? "EROARE: ApiShop nu este setat corect in settings.ini!" + LogMessage("EROARE: ApiShop nu este setat corect in settings.ini!", "ERROR", gcLogFile) RETURN .F. ENDIF @@ -80,15 +100,13 @@ lcStartDateStr = TRANSFORM(YEAR(ldStartDate)) + "-" + ; TRY loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") CATCH TO loError - ? "Eroare la crearea obiectului WinHttp: " + loError.Message + LogMessage("Eroare la crearea obiectului WinHttp: " + loError.Message, "ERROR", gcLogFile) RETURN .F. ENDTRY -SET STEP ON +*-- Removed SET STEP ON for silent operation *-- SECTIUNEA PRODUSE - se executa doar daca llGetProducts = .T. IF llGetProducts - ? "=======================================" - ? "PRELUARE PRODUSE" - ? "=======================================" + LogMessage("[PRODUCTS] Starting product retrieval", "INFO", gcLogFile) *-- Bucla pentru preluarea tuturor produselor (paginare) loAllJsonData = CREATEOBJECT("Empty") @@ -101,7 +119,7 @@ IF llGetProducts *-- Construire URL cu paginare lcApiUrl = lcApiBaseUrl + "&page=" + TRANSFORM(lnCurrentPage) + "&limit=" + TRANSFORM(lnLimit) - ? "Preluare pagina " + TRANSFORM(lnCurrentPage) + "..." + LogMessage("[PRODUCTS] Page " + TRANSFORM(lnCurrentPage) + " fetching...", "INFO", gcLogFile) *-- Configurare request TRY @@ -142,8 +160,7 @@ IF llGetProducts IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' loAllJsonData.pages = VAL(TRANSFORM(loJsonData.pages)) ENDIF - ? "Total produse: " + TRANSFORM(loAllJsonData.total) - ? "Total pagini: " + TRANSFORM(loAllJsonData.pages) + LogMessage("[PRODUCTS] Total items: " + TRANSFORM(loAllJsonData.total) + " | Pages: " + TRANSFORM(loAllJsonData.pages), "INFO", gcLogFile) ENDIF *-- Adaugare produse din pagina curenta @@ -167,39 +184,31 @@ IF llGetProducts 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) + *-- Eroare parsare JSON + LogMessage("[PRODUCTS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) 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) + *-- Eroare HTTP + LogMessage("[PRODUCTS] HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + " on page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) - *-- Incearca sa citesti raspunsul pentru detalii despre eroare + *-- Detalii despre eroare daca sunt disponibile TRY lcErrorResponse = loHttp.ResponseText IF !EMPTY(lcErrorResponse) - lcLogContent = lcLogContent + "Error Details:" + CHR(13) + CHR(10) + lcErrorResponse + LogMessage("[PRODUCTS] Error details: " + LEFT(lcErrorResponse, 200), "ERROR", gcLogFile) ENDIF CATCH - lcLogContent = lcLogContent + "Could not read error details" + LogMessage("[PRODUCTS] Could not read error details", "WARN", gcLogFile) 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) + *-- Script error in products section + LogMessage("[PRODUCTS] Script Error #" + TRANSFORM(loError.ErrorNo) + ": " + loError.Message + " (Line: " + TRANSFORM(loError.LineNo) + ")", "ERROR", gcLogFile) llHasMorePages = .F. ENDTRY @@ -212,22 +221,23 @@ ENDDO *-- Salvare array JSON cu toate produsele IF !ISNULL(loAllJsonData) AND TYPE('loAllJsonData.products') = 'O' - lcJsonFileName = "gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" + lcJsonFileName = lcOutputDir + "\gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" DO SaveProductsArray WITH loAllJsonData, lcJsonFileName - ? "Fisier JSON cu produse creat: " + 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 - ? "SARIT PESTE PRELUAREA PRODUSELOR (llGetProducts = .F.)" + LogMessage("[PRODUCTS] Skipped (disabled in settings)", "INFO", gcLogFile) ENDIF *-- SECTIUNEA COMENZI - se executa doar daca llGetOrders = .T. IF llGetOrders - ? "" - ? "=======================================" - ? "PRELUARE COMENZI DIN ULTIMELE " + TRANSFORM(loSettings.OrderDaysBack) + " ZILE" - ? "Data de start: " + lcStartDateStr - ? "=======================================" + LogMessage("[ORDERS] Starting orders retrieval (last " + TRANSFORM(loSettings.OrderDaysBack) + " days from " + lcStartDateStr + ")", "INFO", gcLogFile) *-- Reinitializare pentru comenzi lnCurrentPage = 1 @@ -242,7 +252,7 @@ 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) + "..." + LogMessage("[ORDERS] Page " + TRANSFORM(lnCurrentPage) + " fetching...", "INFO", gcLogFile) *-- Configurare request TRY @@ -277,12 +287,12 @@ DO WHILE llHasMorePages IF !ISNULL(loJsonData) *-- Debug: Afisam structura JSON pentru prima pagina IF lnCurrentPage = 1 - ? "DEBUG: Analiza structura JSON comenzi..." + 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) - ? " Proprietate: " + lcPropName + " (Tip: " + lcPropType + ")" + LogMessage("[ORDERS] Property: " + lcPropName + " (Type: " + lcPropType + ")", "DEBUG", gcLogFile) ENDFOR ENDIF @@ -294,8 +304,7 @@ DO WHILE llHasMorePages 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) + LogMessage("[ORDERS] Total items: " + TRANSFORM(loAllOrderData.total) + " | Pages: " + TRANSFORM(loAllOrderData.pages), "INFO", gcLogFile) ENDIF *-- Adaugare comenzi din pagina curenta @@ -304,21 +313,38 @@ DO WHILE llHasMorePages 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) + *-- 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 - ? " ATENTIE: Nu s-au gasit comenzi in raspunsul JSON pentru pagina " + TRANSFORM(lnCurrentPage) + LogMessage("[ORDERS] WARNING: No orders found in JSON response for page " + TRANSFORM(lnCurrentPage), "WARN", gcLogFile) ENDIF *-- Verificare daca mai sunt pagini @@ -337,39 +363,31 @@ DO WHILE llHasMorePages 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) + *-- Eroare parsare JSON pentru comenzi + LogMessage("[ORDERS] ERROR: JSON parsing failed for page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) 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) + *-- Eroare HTTP pentru comenzi + LogMessage("[ORDERS] HTTP Error " + TRANSFORM(lnStatusCode) + ": " + lcStatusText + " on page " + TRANSFORM(lnCurrentPage), "ERROR", gcLogFile) - *-- Incearca sa citesti raspunsul pentru detalii despre eroare + *-- Detalii despre eroare daca sunt disponibile TRY lcErrorResponse = loHttp.ResponseText IF !EMPTY(lcErrorResponse) - lcLogContent = lcLogContent + "Error Details:" + CHR(13) + CHR(10) + lcErrorResponse + LogMessage("[ORDERS] Error details: " + LEFT(lcErrorResponse, 200), "ERROR", gcLogFile) ENDIF CATCH - lcLogContent = lcLogContent + "Could not read error details" + LogMessage("[ORDERS] Could not read error details", "WARN", gcLogFile) 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) + *-- Script error in orders section + LogMessage("[ORDERS] Script Error #" + TRANSFORM(loError.ErrorNo) + ": " + loError.Message + " (Line: " + TRANSFORM(loError.LineNo) + ")", "ERROR", gcLogFile) llHasMorePages = .F. ENDTRY @@ -382,18 +400,21 @@ ENDDO *-- Salvare array JSON cu toate comenzile IF !ISNULL(loAllOrderData) AND TYPE('loAllOrderData.orders') = 'O' - lcOrderJsonFileName = "gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" + lcOrderJsonFileName = lcOutputDir + "\gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" DO SaveOrdersArray WITH loAllOrderData, lcOrderJsonFileName - ? "Fisier JSON cu comenzi creat: " + lcOrderJsonFileName + LogMessage("[ORDERS] JSON saved: " + lcOrderJsonFileName, "INFO", gcLogFile) ENDIF ELSE - ? "SARIT PESTE PRELUAREA COMENZILOR (llGetOrders = .F.)" + 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 @@ -419,7 +440,6 @@ IF TYPE('tloAllData.products') = 'O' ENDIF *-- Serializeaza produsul cu nfjsoncreate - SET PROCEDURE TO nfjsoncreate.prg ADDITIVE lcProductJson = nfJsonCreate(loProduct, .F.) lcJsonContent = lcJsonContent + " " + lcProductJson ENDIF @@ -458,7 +478,6 @@ IF TYPE('tloAllData.orders') = 'O' ENDIF *-- Serializeaza comanda cu nfjsoncreate - SET PROCEDURE TO nfjsoncreate.prg ADDITIVE lcOrderJson = nfJsonCreate(loOrder, .F.) lcJsonContent = lcJsonContent + " " + lcOrderJson ENDIF diff --git a/settings.ini b/settings.ini index 00bbeea..9bd7890 100644 --- a/settings.ini +++ b/settings.ini @@ -10,7 +10,7 @@ ContentType=application/json Limit=100 [OPTIONS] -GetProducts=0 +GetProducts=1 GetOrders=1 [FILTERS] diff --git a/utils.prg b/utils.prg index 33aa03e..5895dcb 100644 --- a/utils.prg +++ b/utils.prg @@ -171,4 +171,94 @@ CATCH ENDTRY RETURN llSuccess +ENDFUNC + +*-- Functie pentru initializarea logging-ului +FUNCTION InitLog +PARAMETERS cBaseName +LOCAL lcLogFile, lcStartTime, lcLogHeader, lcLogDir + +*-- Cream directorul log daca nu existe +lcLogDir = gcAppPath + "log" +IF !DIRECTORY(lcLogDir) + MKDIR (lcLogDir) +ENDIF + +*-- Generam numele fisierului log cu timestamp in directorul log +lcStartTime = DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") +lcLogFile = lcLogDir + "\" + cBaseName + "_" + lcStartTime + ".log" + +*-- Header pentru log +lcLogHeader = "[" + TIME() + "] [START] === GoMag Sync Started ===" + CHR(13) + CHR(10) +lcLogHeader = lcLogHeader + "[" + TIME() + "] [INFO ] Date: " + DTOC(DATE()) + " | VFP: " + VERSION() + CHR(13) + CHR(10) + +*-- Cream fisierul log +STRTOFILE(lcLogHeader, lcLogFile) + +RETURN lcLogFile +ENDFUNC + +*-- Functie pentru logging cu nivel si timestamp +FUNCTION LogMessage +PARAMETERS cMessage, cLevel, cLogFile +LOCAL lcTimeStamp, lcLogEntry, lcExistingContent + +*-- Setam nivel implicit daca nu e specificat +IF EMPTY(cLevel) + cLevel = "INFO " +ELSE + *-- Formatam nivelul pentru a avea 5 caractere + cLevel = LEFT(cLevel + " ", 5) +ENDIF + +*-- Cream timestamp-ul +lcTimeStamp = TIME() + +*-- Formatam mesajul pentru log +lcLogEntry = "[" + lcTimeStamp + "] [" + cLevel + "] " + cMessage + CHR(13) + CHR(10) + +*-- Adaugam la fisierul existent +lcExistingContent = "" +IF FILE(cLogFile) + lcExistingContent = FILETOSTR(cLogFile) +ENDIF + +STRTOFILE(lcExistingContent + lcLogEntry, cLogFile) + +RETURN .T. +ENDFUNC + +*-- Functie pentru inchiderea logging-ului cu statistici +FUNCTION CloseLog +PARAMETERS nStartTime, nProductsCount, nOrdersCount, cLogFile +LOCAL lcEndTime, lcDuration, lcStatsEntry + +lcEndTime = TIME() +*-- Calculam durata in secunde +nDuration = SECONDS() - nStartTime + +*-- Formatam statisticile finale +lcStatsEntry = "[" + lcEndTime + "] [END ] === GoMag Sync Completed ===" + CHR(13) + CHR(10) +lcStatsEntry = lcStatsEntry + "[" + lcEndTime + "] [STATS] Duration: " + TRANSFORM(INT(nDuration)) + "s" + +IF nProductsCount > 0 + lcStatsEntry = lcStatsEntry + " | Products: " + TRANSFORM(nProductsCount) +ENDIF + +IF nOrdersCount > 0 + lcStatsEntry = lcStatsEntry + " | Orders: " + TRANSFORM(nOrdersCount) +ENDIF + +lcStatsEntry = lcStatsEntry + CHR(13) + CHR(10) + +*-- Adaugam la log +LOCAL lcExistingContent +lcExistingContent = "" +IF FILE(cLogFile) + lcExistingContent = FILETOSTR(cLogFile) +ENDIF + +STRTOFILE(lcExistingContent + lcStatsEntry, cLogFile) + +RETURN .T. ENDFUNC \ No newline at end of file