Initial commit with current state
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
*.fxp
|
||||||
|
*.FXP
|
||||||
|
*.bak
|
||||||
|
*.BAK
|
||||||
|
*.csv
|
||||||
|
/log.*
|
||||||
|
/output/*.json
|
||||||
|
*.err
|
||||||
|
*.ERR
|
||||||
|
*.log
|
||||||
130
CLAUDE.md
Normal file
130
CLAUDE.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a Visual FoxPro 9 project that interfaces with the GoMag e-commerce API. The application retrieves both product and order data from GoMag's REST API endpoints with full pagination support and comprehensive error handling.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Main Application**: `gomag-vending.prg` - Primary Visual FoxPro script with pagination support
|
||||||
|
- **Utility Module**: `utils.prg` - INI file handling, logging, and helper functions
|
||||||
|
- **JSON Library**: `nfjson/` - Third-party JSON parsing library for VFP
|
||||||
|
- **Technology**: Visual FoxPro 9 with WinHttp.WinHttpRequest.5.1 for HTTP requests
|
||||||
|
- **API Integration**: GoMag REST API v1 for products and orders management
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### gomag-vending.prg
|
||||||
|
Main application script with:
|
||||||
|
- Complete pagination support for products and orders
|
||||||
|
- Configurable API settings via INI file
|
||||||
|
- Comprehensive error handling and logging
|
||||||
|
- Rate limiting compliance (1-second delays between requests)
|
||||||
|
- JSON array output generation for both products and orders
|
||||||
|
|
||||||
|
### utils.prg
|
||||||
|
Utility functions module containing:
|
||||||
|
- INI file operations (`ReadPini`, `WritePini`, `LoadSettings`)
|
||||||
|
- Logging system (`InitLog`, `LogMessage`, `CloseLog`)
|
||||||
|
- Connectivity testing (`TestConnectivity`)
|
||||||
|
- URL encoding utilities (`UrlEncode`)
|
||||||
|
- Default configuration creation (`CreateDefaultIni`)
|
||||||
|
|
||||||
|
### settings.ini
|
||||||
|
Configuration file with sections:
|
||||||
|
- `[API]` - API endpoints, credentials, and headers
|
||||||
|
- `[PAGINATION]` - Page size limits
|
||||||
|
- `[OPTIONS]` - Feature toggles for products/orders retrieval
|
||||||
|
- `[FILTERS]` - Date range filters for orders
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
### Running the Application
|
||||||
|
```foxpro
|
||||||
|
DO gomag-vending.prg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running from Windows Command Line
|
||||||
|
Use the provided batch file:
|
||||||
|
```cmd
|
||||||
|
run-gomag.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
Direct execution with Visual FoxPro:
|
||||||
|
```cmd
|
||||||
|
"C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" -T "gomag-vending.prg"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Management
|
||||||
|
|
||||||
|
The application uses `settings.ini` for all configuration. Key settings:
|
||||||
|
|
||||||
|
### Required Configuration
|
||||||
|
- `ApiKey` - Your GoMag API key
|
||||||
|
- `ApiShop` - Your shop URL (e.g., "https://yourstore.gomag.ro")
|
||||||
|
|
||||||
|
### Feature Control
|
||||||
|
- `GetProducts` - Set to "1" to retrieve products, "0" to skip
|
||||||
|
- `GetOrders` - Set to "1" to retrieve orders, "0" to skip
|
||||||
|
- `OrderDaysBack` - Number of days back to retrieve orders (default: 7)
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
- `Limit` - Records per page (default: 100, max recommended for GoMag API)
|
||||||
|
|
||||||
|
## API Integration Details
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
- Header-based authentication using `Apikey` and `ApiShop` headers
|
||||||
|
- User-Agent must differ from "PostmanRuntime"
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
- Products: `https://api.gomag.ro/api/v1/product/read/json?enabled=1`
|
||||||
|
- Orders: `https://api.gomag.ro/api/v1/order/read/json`
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
- 1-second pause between paginated requests
|
||||||
|
- No specific READ request limitations
|
||||||
|
- POST requests limited to ~1 request per second (Leaky Bucket)
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/
|
||||||
|
├── gomag-vending.prg # Main application
|
||||||
|
├── utils.prg # Utility functions
|
||||||
|
├── settings.ini # Configuration file
|
||||||
|
├── run-gomag.bat # Windows launcher
|
||||||
|
├── nfjson/ # JSON parsing library
|
||||||
|
│ ├── nfjsoncreate.prg
|
||||||
|
│ └── nfjsonread.prg
|
||||||
|
├── log/ # Generated log files
|
||||||
|
├── output/ # Generated JSON files
|
||||||
|
└── products-example.json # Sample API response
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output Files
|
||||||
|
|
||||||
|
### Generated Files
|
||||||
|
- `log/gomag_sync_YYYYMMDD_HHMMSS.log` - Detailed execution logs
|
||||||
|
- `output/gomag_all_products_YYYYMMDD_HHMMSS.json` - Complete product array
|
||||||
|
- `output/gomag_orders_last7days_YYYYMMDD_HHMMSS.json` - Orders array
|
||||||
|
|
||||||
|
### Error Files
|
||||||
|
- `gomag_error_pageN_*.json` - Raw API responses for failed parsing
|
||||||
|
- `gomag_error_pageN_*.log` - HTTP error details with status codes
|
||||||
|
|
||||||
|
## Key Functions
|
||||||
|
|
||||||
|
### Main Application (gomag-vending.prg)
|
||||||
|
- `SaveProductsArray` - Converts paginated product data to JSON array
|
||||||
|
- `SaveOrdersArray` - Converts paginated order data to JSON array
|
||||||
|
- `MergeProducts` - Combines products from multiple pages
|
||||||
|
- `MergeOrdersArray` - Combines orders from multiple pages
|
||||||
|
|
||||||
|
### Utilities (utils.prg)
|
||||||
|
- `LoadSettings` - Loads complete INI configuration into object
|
||||||
|
- `InitLog`/`LogMessage`/`CloseLog` - Comprehensive logging system
|
||||||
|
- `TestConnectivity` - Internet connection verification
|
||||||
|
- `CreateDefaultIni` - Generates template configuration file
|
||||||
602
gomag-vending.prg
Normal file
602
gomag-vending.prg
Normal file
@@ -0,0 +1,602 @@
|
|||||||
|
*-- 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, 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 utils.prg ADDITIVE
|
||||||
|
SET PROCEDURE TO nfjsonread.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
|
||||||
|
*-- 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
|
||||||
|
|
||||||
|
*-- 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'
|
||||||
|
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(loSettings.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
|
||||||
|
|
||||||
|
*-- 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] 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("[ORDERS] Property: " + lcPropName + " (Type: " + lcPropType + ")", "DEBUG", gcLogFile)
|
||||||
|
ENDFOR
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
*-- Prima pagina - setam informatiile generale din metadata
|
||||||
|
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 orders: " + TRANSFORM(loAllOrderData.total), "INFO", gcLogFile)
|
||||||
|
LogMessage("[ORDERS] Total pages: " + TRANSFORM(loAllOrderData.pages), "INFO", gcLogFile)
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
*-- Adaugare comenzi din pagina curenta
|
||||||
|
*-- API-ul GoMag returneaza obiect cu metadata si orders
|
||||||
|
LOCAL llHasOrders, lnOrdersFound
|
||||||
|
llHasOrders = .F.
|
||||||
|
lnOrdersFound = 0
|
||||||
|
|
||||||
|
*-- Verificam daca avem obiectul orders
|
||||||
|
IF TYPE('loJsonData.orders') = 'O'
|
||||||
|
*-- Numaram comenzile din obiectul orders
|
||||||
|
lnOrdersFound = AMEMBERS(laOrdersPage, loJsonData.orders, 0)
|
||||||
|
IF lnOrdersFound > 0
|
||||||
|
*-- Mergem comenzile din pagina curenta
|
||||||
|
DO MergeOrdersArray WITH loAllOrderData, loJsonData
|
||||||
|
llHasOrders = .T.
|
||||||
|
LogMessage("[ORDERS] Found: " + TRANSFORM(lnOrdersFound) + " orders in page " + TRANSFORM(lnCurrentPage), "INFO", gcLogFile)
|
||||||
|
gnOrdersProcessed = gnOrdersProcessed + lnOrdersFound
|
||||||
|
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 folosind metadata
|
||||||
|
IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N'
|
||||||
|
lnTotalPages = VAL(TRANSFORM(loJsonData.pages))
|
||||||
|
IF lnCurrentPage >= lnTotalPages
|
||||||
|
llHasMorePages = .F.
|
||||||
|
ENDIF
|
||||||
|
ELSE
|
||||||
|
*-- Fallback: verifica daca am primit mai putin decat limita
|
||||||
|
IF !llHasOrders OR lnOrdersFound < lnLimit
|
||||||
|
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 = lcOutputDir + "\gomag_orders_last7days_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json"
|
||||||
|
DO SaveOrdersArray WITH loAllOrderData, lcOrderJsonFileName
|
||||||
|
LogMessage("[ORDERS] JSON file created: " + lcOrderJsonFileName, "INFO", gcLogFile)
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
ELSE
|
||||||
|
LogMessage("[ORDERS] Skipped order retrieval (llGetOrders = .F.)", "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
|
||||||
381
nfjson/nfjsoncreate.prg
Normal file
381
nfjson/nfjsoncreate.prg
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
*-------------------------------------------------------------------
|
||||||
|
* Created by Marco Plaza @vfp2Nofox
|
||||||
|
* ver 1.100 - 24/02/2016
|
||||||
|
* enabled collection processing
|
||||||
|
* ver 1.101 - 24/02/2016
|
||||||
|
* solved indentation on nested collections
|
||||||
|
* ver 1.110 -11/03/2016
|
||||||
|
* -added support for collections inside arrays
|
||||||
|
* -user can pass aMemembersFlag value
|
||||||
|
* ( since Json is intended for DTO creation default value is 'U' )
|
||||||
|
* check amembers topic on vfp help file for usage
|
||||||
|
* changed cr to crlf
|
||||||
|
* Added Json validation ; throws error for invalid Json.
|
||||||
|
* ver 1.120
|
||||||
|
* encode control characters ( chr(0) ~ chr(31) )
|
||||||
|
*-----------------------------------------------------------------------
|
||||||
|
Parameters ovfp,FormattedOutput,nonullarrayitem,crootName,aMembersFlag
|
||||||
|
|
||||||
|
#Define crlf Chr(13)+Chr(10)
|
||||||
|
|
||||||
|
Private All
|
||||||
|
|
||||||
|
aMembersFlag = Evl(m.aMembersFlag,'U')
|
||||||
|
|
||||||
|
esarray = Type('oVfp',1) = 'A'
|
||||||
|
esobjeto = Vartype(m.ovfp) = 'O'
|
||||||
|
|
||||||
|
If !m.esarray And !m.esobjeto
|
||||||
|
Error 'must supply a vfp object/array'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
_nivel = Iif( Cast(m.formattedOutput As l ) , 1, -1)
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case esarray
|
||||||
|
|
||||||
|
ojson = Createobject('empty')
|
||||||
|
|
||||||
|
AddProperty(ojson,'array(1)')
|
||||||
|
Acopy(ovfp,ojson.Array)
|
||||||
|
cjson = procobject(ojson,.F.,m.nonullarrayitem,m.aMembersFlag)
|
||||||
|
cjson = Substr( m.cjson,At('[',m.cjson))
|
||||||
|
|
||||||
|
|
||||||
|
Case Type('oVfp.BaseClass')='C' And ovfp.BaseClass = 'Collection'
|
||||||
|
cjson = procobject(ovfp,.T.,m.nonullarrayitem,m.aMembersFlag)
|
||||||
|
|
||||||
|
crootName = Evl(m.crootName,'collection')
|
||||||
|
cjson = '{"'+m.crootName+collTagName(ovfp)+'": '+cjson+'}'+Iif(FormattedOutput,crlf,'')+'}'
|
||||||
|
|
||||||
|
Otherwise
|
||||||
|
cjson = '{'+procobject(ovfp,.F.,m.nonullarrayitem,m.aMembersFlag)+'}'
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
Return Ltrim(cjson)
|
||||||
|
|
||||||
|
*----------------------------------------
|
||||||
|
Function collTagName(thiscoll)
|
||||||
|
*----------------------------------------
|
||||||
|
Return Iif( m.thiscoll.Count > 0 And !Empty( m.thiscoll.GetKey(1) ), '_kv_collection','_kl_collection' )
|
||||||
|
|
||||||
|
*----------------------------------------------------------------------------------
|
||||||
|
Function procobject(obt,iscollection,nonullarrayitem,aMembersFlag)
|
||||||
|
*----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
If Isnull(obt)
|
||||||
|
Return 'null'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Private All Except _nivel
|
||||||
|
|
||||||
|
este = ''
|
||||||
|
|
||||||
|
xtabs = nivel(2)
|
||||||
|
|
||||||
|
bc = Iif(Type('m.obt.class')='C',m.obt.Class,'?')
|
||||||
|
|
||||||
|
iscollection = bc = 'Collection'
|
||||||
|
|
||||||
|
If m.iscollection
|
||||||
|
|
||||||
|
|
||||||
|
este = este+'{ '+xtabs
|
||||||
|
xtabs = nivel(2)
|
||||||
|
este = este+'"collectionitems": ['+xtabs
|
||||||
|
|
||||||
|
procCollection(obt,m.nonullarrayitem,m.aMembersFlag)
|
||||||
|
|
||||||
|
xtabs = nivel(-2)
|
||||||
|
este = este+xtabs+']'
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
Amembers(am,m.obt,0,m.aMembersFlag)
|
||||||
|
|
||||||
|
If Vartype(m.am) = 'U'
|
||||||
|
xtabs=m.nivel(-2)
|
||||||
|
Return ''
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
nm = Alen(am)
|
||||||
|
|
||||||
|
For x1 = 1 To m.nm
|
||||||
|
|
||||||
|
Var = Lower(am(m.x1))
|
||||||
|
|
||||||
|
este = m.este+Iif(m.x1>1,',','')+m.xtabs
|
||||||
|
|
||||||
|
este = m.este+["]+Strtran(m.var,'_vfpsafe_','')+[":]
|
||||||
|
|
||||||
|
esobjeto = Type('m.obt.&Var')='O'
|
||||||
|
|
||||||
|
If Type('m.obt.&var') = 'U'
|
||||||
|
este = m.este+["unable to evaluate expression"]
|
||||||
|
Loop
|
||||||
|
Endif
|
||||||
|
|
||||||
|
esarray = Type('m.obt.&Var',1) = 'A'
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
|
||||||
|
Case m.esarray
|
||||||
|
|
||||||
|
procarray(obt,m.var,m.nonullarrayitem)
|
||||||
|
|
||||||
|
Case m.esobjeto
|
||||||
|
|
||||||
|
thiso=m.obt.&Var
|
||||||
|
|
||||||
|
bc = Iif(Type('m.thiso.class')='C',m.thiso.Class,'?')
|
||||||
|
|
||||||
|
If bc = 'Collection'
|
||||||
|
|
||||||
|
este = Rtrim(m.este,1,'":')+ collTagName( m.thiso )+'":'
|
||||||
|
|
||||||
|
este = m.este+procobject(m.obt.&Var,.T.,m.nonullarrayitem,m.aMembersFlag)+[}]
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
este = m.este+[{]+procobject(m.obt.&Var,.F.,m.nonullarrayitem,m.aMembersFlag)+[}]
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Otherwise
|
||||||
|
|
||||||
|
|
||||||
|
este = este+concatval(m.obt.&Var)
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
xtabs = nivel(-2)
|
||||||
|
este = este+m.xtabs
|
||||||
|
|
||||||
|
|
||||||
|
Return m.este
|
||||||
|
|
||||||
|
|
||||||
|
*----------------------------------------------------
|
||||||
|
Procedure procarray(obt,arrayName,nonullarrayitem)
|
||||||
|
*----------------------------------------------------
|
||||||
|
nrows = Alen(m.obt.&arrayName,1)
|
||||||
|
ncols = Alen(m.obt.&arrayName,2)
|
||||||
|
bidim = m.ncols > 0
|
||||||
|
ncols = Iif(m.ncols=0,m.nrows,m.ncols)
|
||||||
|
titems = Alen(m.obt.&arrayName)
|
||||||
|
|
||||||
|
xtabs=nivel(2)
|
||||||
|
|
||||||
|
este = m.este+'['+m.xtabs
|
||||||
|
nelem = 1
|
||||||
|
|
||||||
|
Do While nelem <= m.titems
|
||||||
|
|
||||||
|
este = este+Iif(m.nelem>1,','+m.xtabs,'')
|
||||||
|
|
||||||
|
If m.bidim
|
||||||
|
xtabs = nivel(2)
|
||||||
|
este = m.este+'['+m.xtabs
|
||||||
|
Endif
|
||||||
|
|
||||||
|
For pn = m.nelem To m.nelem+m.ncols-1
|
||||||
|
|
||||||
|
elem = m.obt.&arrayName( m.pn )
|
||||||
|
|
||||||
|
este = m.este+Iif(m.pn>m.nelem,','+m.xtabs,'')
|
||||||
|
|
||||||
|
If Vartype(m.elem) # 'O'
|
||||||
|
|
||||||
|
If m.nelem+m.ncols-1 = 1 And Isnull(m.elem) And m.nonullarrayitem
|
||||||
|
|
||||||
|
este = m.este+""
|
||||||
|
|
||||||
|
Else
|
||||||
|
este = m.este+concatval(m.elem)
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
|
||||||
|
bc = Iif(Type('m.elem.class')='C',m.elem.Class,'?')
|
||||||
|
|
||||||
|
If bc = 'Collection'
|
||||||
|
|
||||||
|
este = m.este+' { "collection'+ collTagName( m.elem )+'":'
|
||||||
|
|
||||||
|
este = m.este+procobject(m.elem ,.T.,m.nonullarrayitem,m.aMembersFlag)
|
||||||
|
|
||||||
|
este = este + '}'+m.xtabs+'}'
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
este = m.este+[{]+procobject(m.elem ,.F.,m.nonullarrayitem,m.aMembersFlag)+[}]
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
nelem = m.pn
|
||||||
|
|
||||||
|
If m.bidim
|
||||||
|
xtabs=nivel(-2)
|
||||||
|
este = m.este+m.xtabs+']'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Enddo
|
||||||
|
|
||||||
|
|
||||||
|
xtabs=nivel(-2)
|
||||||
|
|
||||||
|
este = m.este+m.xtabs+']'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*-----------------------------
|
||||||
|
Function nivel(N)
|
||||||
|
*-----------------------------
|
||||||
|
If m._nivel = -1
|
||||||
|
Return ''
|
||||||
|
Else
|
||||||
|
_nivel= m._nivel+m.n
|
||||||
|
Return crlf+Replicate(' ',m._nivel)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
*-----------------------------
|
||||||
|
Function concatval(valor)
|
||||||
|
*-----------------------------
|
||||||
|
|
||||||
|
#Define specialChars ["\/]+Chr(127)+Chr(12)+Chr(10)+Chr(13)+Chr(9)+Chr(0)+Chr(1)+Chr(2)+Chr(3)+Chr(4)+Chr(5)+Chr(6)+Chr(7)+Chr(8)+Chr(9)+Chr(10)+Chr(11)+Chr(12)+Chr(13)+Chr(14)+Chr(15)+Chr(16)+Chr(17)+Chr(18)+Chr(19)+Chr(20)+Chr(21)+Chr(22)+Chr(23)+Chr(24)+Chr(25)+Chr(26)+Chr(27)+Chr(28)+Chr(29)+Chr(30)+Chr(31)
|
||||||
|
|
||||||
|
If Isnull(m.valor)
|
||||||
|
|
||||||
|
Return 'null'
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
|
||||||
|
tvar = Vartype(m.valor)
|
||||||
|
** no cambiar el orden de ejecuci<63>n!
|
||||||
|
Do Case
|
||||||
|
Case m.tvar $ 'FBYINQ'
|
||||||
|
vc = Rtrim(Cast( m.valor As c(32)))
|
||||||
|
Case m.tvar = 'L'
|
||||||
|
vc = Iif(m.valor,'true','false')
|
||||||
|
Case m.tvar $ 'DT'
|
||||||
|
vc = ["]+Ttoc(m.valor,3)+["]
|
||||||
|
Case mustEncode(m.valor)
|
||||||
|
vc = ["]+escapeandencode(m.valor)+["]
|
||||||
|
Case m.tvar $ 'CVM'
|
||||||
|
vc = ["]+Rtrim(m.valor)+["]
|
||||||
|
Case m.tvar $ 'GQW'
|
||||||
|
vc = ["]+Strconv(m.valor,13)+["]
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
Return m.vc
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
*-----------------------------------
|
||||||
|
Function mustEncode(valor)
|
||||||
|
*-----------------------------------
|
||||||
|
Return Len(Chrtran(m.valor,specialChars,'')) <> Len(m.valor)
|
||||||
|
|
||||||
|
*-------------------------------
|
||||||
|
Function escapeandencode(valun)
|
||||||
|
*-------------------------------
|
||||||
|
valun = Strtran(m.valun,'\','\\')
|
||||||
|
valun = Strtran(m.valun,'"','\"')
|
||||||
|
*valun = Strtran(m.valun,'/','\/')
|
||||||
|
|
||||||
|
If !mustEncode(m.valun)
|
||||||
|
Return
|
||||||
|
Endif
|
||||||
|
|
||||||
|
valun = Strtran(m.valun,Chr(127),'\b')
|
||||||
|
valun = Strtran(m.valun,Chr(12),'\f')
|
||||||
|
valun = Strtran(m.valun,Chr(10),'\n')
|
||||||
|
valun = Strtran(m.valun,Chr(13),'\r')
|
||||||
|
valun = Strtran(m.valun,Chr(9),'\t')
|
||||||
|
|
||||||
|
If !mustEncode(m.valun)
|
||||||
|
Return
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Local x
|
||||||
|
For x = 0 To 31
|
||||||
|
valun = Strtran(m.valun,Chr(m.x),'\u'+Right(Transform(m.x,'@0'),4))
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
Return Rtrim(m.valun)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*---------------------------------------------------------------
|
||||||
|
Function procCollection(obt,nonullArrayItems,aMembersFlag )
|
||||||
|
*---------------------------------------------------------------
|
||||||
|
|
||||||
|
Local iscollection
|
||||||
|
|
||||||
|
With obt
|
||||||
|
|
||||||
|
nm = .Count
|
||||||
|
|
||||||
|
conllave = .Count > 0 And !Empty(.GetKey(1))
|
||||||
|
|
||||||
|
For x1 = 1 To .Count
|
||||||
|
|
||||||
|
If conllave
|
||||||
|
elem = Createobject('empty')
|
||||||
|
AddProperty(elem,'Key', .GetKey(x1) )
|
||||||
|
AddProperty(elem,'Value',.Item(x1))
|
||||||
|
Else
|
||||||
|
elem = .Item(x1)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
este = este+Iif(x1>1,','+xtabs,'')
|
||||||
|
|
||||||
|
If Vartype(elem) # 'O'
|
||||||
|
|
||||||
|
este = este+concatval(m.elem)
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
If Vartype( m.elem.BaseClass ) = 'C' And m.elem.BaseClass = 'Collection'
|
||||||
|
iscollection = .T.
|
||||||
|
este = m.este+'{ '+m.xtabs+'"collection'+collTagName(m.elem)+'" :'
|
||||||
|
xtabs = nivel(2)
|
||||||
|
Else
|
||||||
|
iscollection = .F.
|
||||||
|
m.este = m.este+'{'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
este = este+procobject(m.elem, m.iscollection , m.nonullarrayitem, m.aMembersFlag )
|
||||||
|
|
||||||
|
este = este+'}'
|
||||||
|
|
||||||
|
If m.iscollection
|
||||||
|
xtabs = nivel(-2)
|
||||||
|
este = este+m.xtabs+'}'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
este = Rtrim(m.este,1,m.xtabs)
|
||||||
|
|
||||||
|
Endwith
|
||||||
775
nfjson/nfjsonread.prg
Normal file
775
nfjson/nfjsonread.prg
Normal file
@@ -0,0 +1,775 @@
|
|||||||
|
*-------------------------------------------------------------------
|
||||||
|
* Created by Marco Plaza vfp2nofox@gmail.com / @vfp2Nofox
|
||||||
|
* ver 2.000 - 26/03/2016
|
||||||
|
* ver 2.090 - 22/07/2016 :
|
||||||
|
* improved error management
|
||||||
|
* nfjsonread will return .null. for invalid json
|
||||||
|
*-------------------------------------------------------------------
|
||||||
|
Lparameters cjsonstr,isFileName,reviveCollection
|
||||||
|
|
||||||
|
#Define crlf Chr(13)+Chr(10)
|
||||||
|
|
||||||
|
Private All
|
||||||
|
|
||||||
|
stackLevels=Astackinfo(aerrs)
|
||||||
|
|
||||||
|
If m.stackLevels > 1
|
||||||
|
calledFrom = 'called From '+aerrs(m.stackLevels-1,4)+' line '+Transform(aerrs(m.stackLevels-1,5))
|
||||||
|
Else
|
||||||
|
calledFrom = ''
|
||||||
|
Endif
|
||||||
|
|
||||||
|
oJson = nfJsonCreate2(cjsonstr,isFileName,reviveCollection)
|
||||||
|
|
||||||
|
Return Iif(Vartype(m.oJson)='O',m.oJson,.Null.)
|
||||||
|
|
||||||
|
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
Function nfJsonCreate2(cjsonstr,isFileName,reviveCollection)
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
* validate parameters:
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case ;
|
||||||
|
Vartype(m.cjsonstr) # 'C' Or;
|
||||||
|
Vartype(m.reviveCollection) # 'L' Or ;
|
||||||
|
Vartype(m.isFileName) # 'L'
|
||||||
|
|
||||||
|
jERROR('invalid parameter type')
|
||||||
|
|
||||||
|
Case m.isFileName And !File(m.cjsonstr)
|
||||||
|
|
||||||
|
jERROR('File "'+Rtrim(Left(m.cjsonstr,255))+'" does not exist')
|
||||||
|
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
* process json:
|
||||||
|
|
||||||
|
If m.isFileName
|
||||||
|
cjsonstr = Filetostr(m.cjsonstr)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
cJson = Rtrim(Chrtran(m.cjsonstr,Chr(13)+Chr(9)+Chr(10),''))
|
||||||
|
pChar = Left(Ltrim(m.cJson),1)
|
||||||
|
|
||||||
|
|
||||||
|
nl = Alines(aj,m.cJson,20,'{','}','"',',',':','[',']')
|
||||||
|
|
||||||
|
For xx = 1 To Alen(aj)
|
||||||
|
If Left(Ltrim(aj(m.xx)),1) $ '{}",:[]' Or Left(Ltrim(m.aj(m.xx)),4) $ 'true/false/null'
|
||||||
|
aj(m.xx) = Ltrim(aj(m.xx))
|
||||||
|
Endif
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
|
||||||
|
Try
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
cError = ''
|
||||||
|
oStack = Createobject('stack')
|
||||||
|
|
||||||
|
oJson = Createobject('empty')
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case aj(1)='{'
|
||||||
|
x = 1
|
||||||
|
oStack.pushObject()
|
||||||
|
procstring(m.oJson)
|
||||||
|
|
||||||
|
Case aj(1) = '['
|
||||||
|
x = 0
|
||||||
|
procstring(m.oJson,.T.)
|
||||||
|
|
||||||
|
Otherwise
|
||||||
|
Error 'Invalid Json: expecting [{ received '+m.pChar
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
If m.reviveCollection
|
||||||
|
oJson = reviveCollection(m.oJson)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
Catch To oerr
|
||||||
|
|
||||||
|
strp = ''
|
||||||
|
|
||||||
|
For Y = 1 To m.x
|
||||||
|
strp = m.strp+aj(m.y)
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case oerr.ErrorNo = 1098
|
||||||
|
|
||||||
|
cError = ' Invalid Json: '+ m.oerr.Message+crlf+' Parsing: '+Right(m.strp,80)
|
||||||
|
|
||||||
|
*+' program line: '+Transform(oerr.Lineno)+' array item '+Transform(m.x)
|
||||||
|
|
||||||
|
Case oerr.ErrorNo = 2034
|
||||||
|
|
||||||
|
cError = ' INVALID DATE: '+crlf+' Parsing: '+Right(m.strp,80)
|
||||||
|
|
||||||
|
|
||||||
|
Otherwise
|
||||||
|
|
||||||
|
cError = 'program error # '+Transform(m.oerr.ErrorNo)+crlf+m.oerr.Message+' at: '+Transform(oerr.Lineno)+crlf+' Parsing ('+Transform(m.x)+') '
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
Endtry
|
||||||
|
|
||||||
|
If !Empty(m.cError)
|
||||||
|
jERROR(m.cError)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Return m.oJson
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*------------------------------------------------
|
||||||
|
Procedure jERROR( cMessage )
|
||||||
|
*------------------------------------------------
|
||||||
|
Error 'nfJson ('+m.calledFrom+'):'+crlf+m.cMessage
|
||||||
|
Return To nfJsonRead
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*--------------------------------------------------------------------------------
|
||||||
|
Procedure procstring(obj,eValue)
|
||||||
|
*--------------------------------------------------------------------------------
|
||||||
|
#Define cvalid 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_'
|
||||||
|
#Define creem '_______________________________________________________________'
|
||||||
|
|
||||||
|
Private rowpos,colpos,bidim,ncols,arrayName,expecting,arrayLevel,vari
|
||||||
|
Private expectingPropertyName,expectingValue,objectOpen
|
||||||
|
|
||||||
|
expectingPropertyName = !m.eValue
|
||||||
|
expectingValue = m.eValue
|
||||||
|
expecting = Iif(expectingPropertyName,'"}','')
|
||||||
|
objectOpen = .T.
|
||||||
|
bidim = .F.
|
||||||
|
colpos = 0
|
||||||
|
rowpos = 0
|
||||||
|
arrayLevel = 0
|
||||||
|
arrayName = ''
|
||||||
|
vari = ''
|
||||||
|
ncols = 0
|
||||||
|
|
||||||
|
Do While m.objectOpen
|
||||||
|
|
||||||
|
x = m.x+1
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
|
||||||
|
Case m.x > m.nl
|
||||||
|
|
||||||
|
m.x = m.nl
|
||||||
|
|
||||||
|
If oStack.Count > 0
|
||||||
|
Error 'expecting '+m.expecting
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Return
|
||||||
|
|
||||||
|
Case aj(m.x) = '}' And '}' $ m.expecting
|
||||||
|
closeObject()
|
||||||
|
|
||||||
|
Case aj(x) = ']' And ']' $ m.expecting
|
||||||
|
closeArray()
|
||||||
|
|
||||||
|
Case m.expecting = ':'
|
||||||
|
If aj(m.x) = ':'
|
||||||
|
expecting = ''
|
||||||
|
Loop
|
||||||
|
Else
|
||||||
|
Error 'expecting : received '+aj(m.x)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Case ',' $ m.expecting
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case aj(x) = ','
|
||||||
|
expecting = Iif( '[' $ m.expecting , '[' , '' )
|
||||||
|
Case Not aj(m.x) $ m.expecting
|
||||||
|
Error 'expecting '+m.expecting+' received '+aj(m.x)
|
||||||
|
Otherwise
|
||||||
|
expecting = Strtran(m.expecting,',','')
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
Case m.expectingPropertyName
|
||||||
|
|
||||||
|
If aj(m.x) = '"'
|
||||||
|
propertyName(m.obj)
|
||||||
|
Else
|
||||||
|
Error 'expecting "'+m.expecting+' received '+aj(m.x)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
Case m.expectingValue
|
||||||
|
|
||||||
|
If m.expecting == '[' And m.aj(m.x) # '['
|
||||||
|
Error 'expecting [ received '+aj(m.x)
|
||||||
|
Else
|
||||||
|
procValue(m.obj)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
Enddo
|
||||||
|
|
||||||
|
|
||||||
|
*----------------------------------------------------------
|
||||||
|
Function anuevoel(obj,arrayName,valasig,bidim,colpos,rowpos)
|
||||||
|
*----------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
If m.bidim
|
||||||
|
|
||||||
|
colpos = m.colpos+1
|
||||||
|
|
||||||
|
If colpos > m.ncols
|
||||||
|
ncols = m.colpos
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Dimension obj.&arrayName(m.rowpos,m.ncols)
|
||||||
|
|
||||||
|
obj.&arrayName(m.rowpos,m.colpos) = m.valasig
|
||||||
|
|
||||||
|
If Vartype(m.valasig) = 'O'
|
||||||
|
procstring(obj.&arrayName(m.rowpos,m.colpos))
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
rowpos = m.rowpos+1
|
||||||
|
Dimension obj.&arrayName(m.rowpos)
|
||||||
|
|
||||||
|
obj.&arrayName(m.rowpos) = m.valasig
|
||||||
|
|
||||||
|
If Vartype(m.valasig) = 'O'
|
||||||
|
procstring(obj.&arrayName(m.rowpos))
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
*-----------------------------------------
|
||||||
|
Function unescunicode( Value )
|
||||||
|
*-----------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
noc=1
|
||||||
|
|
||||||
|
Do While .T.
|
||||||
|
|
||||||
|
posunicode = At('\u',m.value,m.noc)
|
||||||
|
|
||||||
|
If m.posunicode = 0
|
||||||
|
Return
|
||||||
|
Endif
|
||||||
|
|
||||||
|
If Substr(m.value,m.posunicode-1,1) = '\' And Substr(m.value,m.posunicode-2,1) # '\'
|
||||||
|
noc=m.noc+1
|
||||||
|
Loop
|
||||||
|
Endif
|
||||||
|
|
||||||
|
nunic = Evaluate('0x'+ Substr(m.value,m.posunicode+2,4) )
|
||||||
|
|
||||||
|
If Between(m.nunic,0,255)
|
||||||
|
unicodec = Chr(m.nunic)
|
||||||
|
Else
|
||||||
|
unicodec = '&#'+Transform(m.nunic)+';'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Value = Stuff(m.value,m.posunicode,6,m.unicodec)
|
||||||
|
|
||||||
|
|
||||||
|
Enddo
|
||||||
|
|
||||||
|
*-----------------------------------
|
||||||
|
Function unescapecontrolc( Value )
|
||||||
|
*-----------------------------------
|
||||||
|
|
||||||
|
If At('\', m.value) = 0
|
||||||
|
Return
|
||||||
|
Endif
|
||||||
|
|
||||||
|
* unescape special characters:
|
||||||
|
|
||||||
|
Private aa,elem,unesc
|
||||||
|
|
||||||
|
|
||||||
|
Declare aa(1)
|
||||||
|
=Alines(m.aa,m.value,18,'\\','\b','\f','\n','\r','\t','\"','\/')
|
||||||
|
|
||||||
|
unesc =''
|
||||||
|
|
||||||
|
#Define sustb 'bnrt/"'
|
||||||
|
#Define sustr Chr(127)+Chr(10)+Chr(13)+Chr(9)+Chr(47)+Chr(34)
|
||||||
|
|
||||||
|
For Each elem In m.aa
|
||||||
|
|
||||||
|
If ! m.elem == '\\' And Right(m.elem,2) = '\'
|
||||||
|
elem = Left(m.elem,Len(m.elem)-2)+Chrtran(Right(m.elem,1),sustb,sustr)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
unesc = m.unesc+m.elem
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
Value = m.unesc
|
||||||
|
|
||||||
|
*--------------------------------------------
|
||||||
|
Procedure propertyName(obj)
|
||||||
|
*--------------------------------------------
|
||||||
|
|
||||||
|
vari=''
|
||||||
|
|
||||||
|
Do While ( Right(m.vari,1) # '"' Or ( Right(m.vari,2) = '\"' And Right(m.vari,3) # '\\"' ) ) And Alen(aj) > m.x
|
||||||
|
x=m.x+1
|
||||||
|
vari = m.vari+aj(m.x)
|
||||||
|
Enddo
|
||||||
|
|
||||||
|
If Right(m.vari,1) # '"'
|
||||||
|
Error ' expecting " received '+ Right(Rtrim(m.vari),1)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
vari = Left(m.vari,Len(m.vari)-1)
|
||||||
|
vari = Iif(Isalpha(m.vari),'','_')+m.vari
|
||||||
|
vari = Chrtran( vari, Chrtran( vari, cvalid,'' ) , creem )
|
||||||
|
|
||||||
|
If vari = 'tabindex'
|
||||||
|
vari = '_tabindex'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
expecting = ':'
|
||||||
|
expectingValue = .T.
|
||||||
|
expectingPropertyName = .F.
|
||||||
|
|
||||||
|
|
||||||
|
*-------------------------------------------------------------
|
||||||
|
Procedure procValue(obj)
|
||||||
|
*-------------------------------------------------------------
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case aj(m.x) = '{'
|
||||||
|
|
||||||
|
oStack.pushObject()
|
||||||
|
|
||||||
|
If m.arrayLevel = 0
|
||||||
|
|
||||||
|
AddProperty(obj,m.vari,Createobject('empty'))
|
||||||
|
|
||||||
|
procstring(obj.&vari)
|
||||||
|
expectingPropertyName = .T.
|
||||||
|
expecting = ',}'
|
||||||
|
expectingValue = .F.
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
anuevoel(m.obj,m.arrayName,Createobject('empty'),m.bidim,@colpos,@rowpos)
|
||||||
|
expectingPropertyName = .F.
|
||||||
|
expecting = ',]'
|
||||||
|
expectingValue = .T.
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
Case aj(x) = '['
|
||||||
|
|
||||||
|
oStack.pushArray()
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
|
||||||
|
Case m.arrayLevel = 0
|
||||||
|
|
||||||
|
arrayName = Evl(m.vari,'array')
|
||||||
|
rowpos = 0
|
||||||
|
colpos = 0
|
||||||
|
bidim = .F.
|
||||||
|
|
||||||
|
#DEFINE EMPTYARRAYFLAG '_EMPTY_ARRAY_FLAG_'
|
||||||
|
|
||||||
|
Try
|
||||||
|
AddProperty(obj,(m.arrayName+'(1)'),EMPTYARRAYFLAG)
|
||||||
|
Catch
|
||||||
|
m.arrayName = m.arrayName+'_vfpSafe_'
|
||||||
|
AddProperty(obj,(m.arrayName+'(1)'),EMPTYARRAYFLAG)
|
||||||
|
Endtry
|
||||||
|
|
||||||
|
|
||||||
|
Case m.arrayLevel = 1 And !m.bidim
|
||||||
|
|
||||||
|
rowpos = 1
|
||||||
|
colpos = 0
|
||||||
|
ncols = 1
|
||||||
|
|
||||||
|
Dime obj.&arrayName(1,2)
|
||||||
|
bidim = .T.
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
arrayLevel = m.arrayLevel+1
|
||||||
|
|
||||||
|
vari=''
|
||||||
|
|
||||||
|
expecting = Iif(!m.bidim,'[]{',']')
|
||||||
|
expectingValue = .T.
|
||||||
|
expectingPropertyName = .F.
|
||||||
|
|
||||||
|
Otherwise
|
||||||
|
|
||||||
|
isstring = aj(m.x)='"'
|
||||||
|
x = m.x + Iif(m.isstring,1,0)
|
||||||
|
|
||||||
|
Value = ''
|
||||||
|
|
||||||
|
Do While .T.
|
||||||
|
|
||||||
|
Value = m.value+m.aj(m.x)
|
||||||
|
|
||||||
|
If m.isstring
|
||||||
|
If Right(m.value,1) = '"' And ( Right(m.value,2) # '\"' Or Right(m.value,3) = '\\' )
|
||||||
|
Exit
|
||||||
|
Endif
|
||||||
|
Else
|
||||||
|
If Right(m.value,1) $ '}],' And ( Left(Right(m.value,2),1) # '\' Or Left(Right(Value,3),2) = '\\')
|
||||||
|
Exit
|
||||||
|
Endif
|
||||||
|
Endif
|
||||||
|
|
||||||
|
If m.x < Alen(aj)
|
||||||
|
x = m.x+1
|
||||||
|
Else
|
||||||
|
Exit
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Enddo
|
||||||
|
|
||||||
|
closeChar = Right(m.value,1)
|
||||||
|
|
||||||
|
Value = Rtrim(m.value,1,m.closeChar)
|
||||||
|
|
||||||
|
If Empty(Value) And Not ( m.isstring And m.closeChar = '"' )
|
||||||
|
Error 'Expecting value received '+m.closeChar
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
|
||||||
|
Case m.isstring
|
||||||
|
If m.closeChar # '"'
|
||||||
|
Error 'expecting " received '+m.closeChar
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Case oStack.isObject() And Not m.closeChar $ ',}'
|
||||||
|
Error 'expecting ,} received '+m.closeChar
|
||||||
|
|
||||||
|
Case oStack.isArray() And Not m.closeChar $ ',]'
|
||||||
|
Error 'expecting ,] received '+m.closeChar
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
If m.isstring
|
||||||
|
|
||||||
|
* don't change this lines sequence!:
|
||||||
|
unescunicode(@Value) && 1
|
||||||
|
unescapecontrolc(@Value) && 2
|
||||||
|
Value = Strtran(m.value,'\\','\') && 3
|
||||||
|
|
||||||
|
** check for Json Date:
|
||||||
|
If isJsonDt( m.value )
|
||||||
|
Value = jsonDateToDT( m.value )
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
Value = Alltrim(m.value)
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case m.value == 'null'
|
||||||
|
Value = .Null.
|
||||||
|
Case m.value == 'true' Or m.value == 'false'
|
||||||
|
Value = Value='true'
|
||||||
|
Case Empty(Chrtran(m.value,'-1234567890.E','')) And Occurs('.',m.value) <= 1 And Occurs('-',m.value) <= 1 And Occurs('E',m.value)<=1
|
||||||
|
If Not 'E' $ m.value
|
||||||
|
Value = Cast( m.value As N( Len(m.value) , Iif(At('.',m.value)>0,Len(m.value)-At( '.',m.value) ,0) ))
|
||||||
|
Endif
|
||||||
|
Otherwise
|
||||||
|
Error 'expecting "|number|null|true|false| received '+aj(m.x)
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
If m.arrayLevel = 0
|
||||||
|
|
||||||
|
|
||||||
|
AddProperty(obj,m.vari,m.value)
|
||||||
|
|
||||||
|
expecting = '}'
|
||||||
|
expectingValue = .F.
|
||||||
|
expectingPropertyName = .T.
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
anuevoel(obj,m.arrayName,m.value,m.bidim,@colpos,@rowpos)
|
||||||
|
expecting = ']'
|
||||||
|
expectingValue = .T.
|
||||||
|
expectingPropertyName = .F.
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
expecting = Iif(m.isstring,',','')+m.expecting
|
||||||
|
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case m.closeChar = ']'
|
||||||
|
closeArray()
|
||||||
|
Case m.closeChar = '}'
|
||||||
|
closeObject()
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
*------------------------------
|
||||||
|
Function closeArray()
|
||||||
|
*------------------------------
|
||||||
|
|
||||||
|
If oStack.Pop() # 'A'
|
||||||
|
Error 'unexpected ] '
|
||||||
|
Endif
|
||||||
|
|
||||||
|
If m.arrayLevel = 0
|
||||||
|
Error 'unexpected ] '
|
||||||
|
Endif
|
||||||
|
|
||||||
|
arrayLevel = m.arrayLevel-1
|
||||||
|
|
||||||
|
If m.arrayLevel = 0
|
||||||
|
|
||||||
|
arrayName = ''
|
||||||
|
rowpos = 0
|
||||||
|
colpos = 0
|
||||||
|
|
||||||
|
expecting = Iif(oStack.isObject(),',}','')
|
||||||
|
expectingPropertyName = .T.
|
||||||
|
expectingValue = .F.
|
||||||
|
|
||||||
|
Else
|
||||||
|
|
||||||
|
If m.bidim
|
||||||
|
rowpos = m.rowpos+1
|
||||||
|
colpos = 0
|
||||||
|
expecting = ',]['
|
||||||
|
Else
|
||||||
|
expecting = ',]'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
expectingValue = .T.
|
||||||
|
expectingPropertyName = .F.
|
||||||
|
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*-------------------------------------
|
||||||
|
Procedure closeObject
|
||||||
|
*-------------------------------------
|
||||||
|
|
||||||
|
If oStack.Pop() # 'O'
|
||||||
|
Error 'unexpected }'
|
||||||
|
Endif
|
||||||
|
|
||||||
|
If m.arrayLevel = 0
|
||||||
|
expecting = ',}'
|
||||||
|
expectingValue = .F.
|
||||||
|
expectingPropertyName = .T.
|
||||||
|
objectOpen = .F.
|
||||||
|
Else
|
||||||
|
expecting = ',]'
|
||||||
|
expectingValue = .T.
|
||||||
|
expectingPropertyName = .F.
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
*----------------------------------------------
|
||||||
|
Function reviveCollection( o )
|
||||||
|
*----------------------------------------------
|
||||||
|
|
||||||
|
Private All
|
||||||
|
|
||||||
|
oConv = Createobject('empty')
|
||||||
|
|
||||||
|
nProp = Amembers(elem,m.o,0,'U')
|
||||||
|
|
||||||
|
For x = 1 To m.nProp
|
||||||
|
|
||||||
|
estaVar = m.elem(x)
|
||||||
|
|
||||||
|
esArray = .F.
|
||||||
|
esColeccion = Type('m.o.'+m.estaVar) = 'O' And Right( m.estaVar , 14 ) $ '_KV_COLLECTION,_KL_COLLECTION' And Type( 'm.o.'+m.estaVar+'.collectionitems',1) = 'A'
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case m.esColeccion
|
||||||
|
|
||||||
|
estaProp = Createobject('collection')
|
||||||
|
|
||||||
|
tv = m.o.&estaVar
|
||||||
|
|
||||||
|
m.keyValColl = Right( m.estaVar , 14 ) = '_KV_COLLECTION'
|
||||||
|
|
||||||
|
For T = 1 To Alen(m.tv.collectionItems)
|
||||||
|
|
||||||
|
If m.keyValColl
|
||||||
|
esteval = m.tv.collectionItems(m.T).Value
|
||||||
|
Else
|
||||||
|
esteval = m.tv.collectionItems(m.T)
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
IF VARTYPE(m.esteval) = 'C' AND m.esteval = emptyarrayflag
|
||||||
|
loop
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
If Vartype(m.esteval) = 'O' Or Type('esteVal',1) = 'A'
|
||||||
|
esteval = reviveCollection(m.esteval)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
If m.keyValColl
|
||||||
|
estaProp.Add(esteval,m.tv.collectionItems(m.T).Key)
|
||||||
|
Else
|
||||||
|
estaProp.Add(m.esteval)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
Case Type('m.o.'+m.estaVar,1) = 'A'
|
||||||
|
|
||||||
|
esArray = .T.
|
||||||
|
|
||||||
|
For T = 1 To Alen(m.o.&estaVar)
|
||||||
|
|
||||||
|
Dimension &estaVar(m.T)
|
||||||
|
|
||||||
|
If Type('m.o.&estaVar(m.T)') = 'O'
|
||||||
|
&estaVar(m.T) = reviveCollection(m.o.&estaVar(m.T))
|
||||||
|
Else
|
||||||
|
&estaVar(m.T) = m.o.&estaVar(m.T)
|
||||||
|
Endif
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
Case Type('m.o.'+estaVar) = 'O'
|
||||||
|
estaProp = reviveCollection(m.o.&estaVar)
|
||||||
|
|
||||||
|
Otherwise
|
||||||
|
estaProp = m.o.&estaVar
|
||||||
|
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
|
||||||
|
estaVar = Strtran( m.estaVar,'_KV_COLLECTION', '' )
|
||||||
|
estaVar = Strtran( m.estaVar, '_KL_COLLECTION', '' )
|
||||||
|
|
||||||
|
Do Case
|
||||||
|
Case m.esColeccion
|
||||||
|
AddProperty(m.oConv,m.estaVar,m.estaProp)
|
||||||
|
Case m.esArray
|
||||||
|
AddProperty(m.oConv,m.estaVar+'(1)')
|
||||||
|
Acopy(&estaVar,m.oConv.&estaVar)
|
||||||
|
Otherwise
|
||||||
|
AddProperty(m.oConv,m.estaVar,m.estaProp)
|
||||||
|
Endcase
|
||||||
|
|
||||||
|
Endfor
|
||||||
|
|
||||||
|
Try
|
||||||
|
retCollection = m.oConv.Collection.BaseClass = 'Collection'
|
||||||
|
Catch
|
||||||
|
retCollection = .F.
|
||||||
|
Endtry
|
||||||
|
|
||||||
|
If m.retCollection
|
||||||
|
Return m.oConv.Collection
|
||||||
|
Else
|
||||||
|
Return m.oConv
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
*----------------------------------
|
||||||
|
Function isJsonDt( cstr )
|
||||||
|
*----------------------------------
|
||||||
|
Return Iif( Len(m.cstr) = 19 ;
|
||||||
|
AND Len(Chrtran(m.cstr,'01234567890:T-','')) = 0 ;
|
||||||
|
and Substr(m.cstr,5,1) = '-' ;
|
||||||
|
and Substr(m.cstr,8,1) = '-' ;
|
||||||
|
and Substr(m.cstr,11,1) = 'T' ;
|
||||||
|
and Substr(m.cstr,14,1) = ':' ;
|
||||||
|
and Substr(m.cstr,17,1) = ':' ;
|
||||||
|
and Occurs('T',m.cstr) = 1 ;
|
||||||
|
and Occurs('-',m.cstr) = 2 ;
|
||||||
|
and Occurs(':',m.cstr) = 2 ,.T.,.F. )
|
||||||
|
|
||||||
|
|
||||||
|
*-----------------------------------
|
||||||
|
Procedure jsonDateToDT( cJsonDate )
|
||||||
|
*-----------------------------------
|
||||||
|
Return Eval("{^"+m.cJsonDate+"}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
******************************************
|
||||||
|
Define Class Stack As Collection
|
||||||
|
******************************************
|
||||||
|
|
||||||
|
*---------------------------
|
||||||
|
Function pushObject()
|
||||||
|
*---------------------------
|
||||||
|
This.Add('O')
|
||||||
|
|
||||||
|
*---------------------------
|
||||||
|
Function pushArray()
|
||||||
|
*---------------------------
|
||||||
|
This.Add('A')
|
||||||
|
|
||||||
|
*--------------------------------------
|
||||||
|
Function isObject()
|
||||||
|
*--------------------------------------
|
||||||
|
If This.Count > 0
|
||||||
|
Return This.Item( This.Count ) = 'O'
|
||||||
|
Else
|
||||||
|
Return .F.
|
||||||
|
Endif
|
||||||
|
|
||||||
|
|
||||||
|
*--------------------------------------
|
||||||
|
Function isArray()
|
||||||
|
*--------------------------------------
|
||||||
|
If This.Count > 0
|
||||||
|
Return This.Item( This.Count ) = 'A'
|
||||||
|
Else
|
||||||
|
Return .F.
|
||||||
|
Endif
|
||||||
|
|
||||||
|
*----------------------------
|
||||||
|
Function Pop()
|
||||||
|
*----------------------------
|
||||||
|
cret = This.Item( This.Count )
|
||||||
|
This.Remove( This.Count )
|
||||||
|
Return m.cret
|
||||||
|
|
||||||
|
******************************************
|
||||||
|
Enddefine
|
||||||
|
******************************************
|
||||||
|
|
||||||
|
|
||||||
208
orders-example.json
Normal file
208
orders-example.json
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
{
|
||||||
|
"total": "399",
|
||||||
|
"page": "1",
|
||||||
|
"pages": 4,
|
||||||
|
"orders": {
|
||||||
|
"60644": {
|
||||||
|
"id": "60644",
|
||||||
|
"number": "436232189",
|
||||||
|
"date": "2025-08-27 16:32:43",
|
||||||
|
"invoice": {
|
||||||
|
"series": "",
|
||||||
|
"number": "0",
|
||||||
|
"date": "0000-00-00 00:00:00"
|
||||||
|
},
|
||||||
|
"total": "1026.24",
|
||||||
|
"status": "Comanda noua",
|
||||||
|
"statusId": "1",
|
||||||
|
"source": "internal",
|
||||||
|
"sales_channel": "Website",
|
||||||
|
"sales_channel_marketplace": "",
|
||||||
|
"sales_agent": "",
|
||||||
|
"currency": "RON",
|
||||||
|
"observation": "",
|
||||||
|
"payment": {
|
||||||
|
"name": "Numerar/Ramburs sau Card la easybox",
|
||||||
|
"online": "0",
|
||||||
|
"completed": "0"
|
||||||
|
},
|
||||||
|
"delivery": {
|
||||||
|
"name": "Transport gratuit",
|
||||||
|
"total": 0,
|
||||||
|
"date": "0000-00-00 00:00:00",
|
||||||
|
"lockerId": 0
|
||||||
|
},
|
||||||
|
"shipping": {
|
||||||
|
"company": "",
|
||||||
|
"firstname": "Liviu",
|
||||||
|
"lastname": "Stefan",
|
||||||
|
"phone": "0751013764",
|
||||||
|
"email": "liviustefan2001@gmail.com",
|
||||||
|
"address": "aleea Soarelui nr 2, casa nr 4, cartier Brates Lake",
|
||||||
|
"city": "Galați",
|
||||||
|
"region": "Galati",
|
||||||
|
"country": "Romania",
|
||||||
|
"zipcode": null
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "582",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "8000070028685",
|
||||||
|
"ean": "8000070028685",
|
||||||
|
"name": "Lavazza Gusto Forte Vending Cafea Boabe 1kg",
|
||||||
|
"price": "69.79",
|
||||||
|
"baseprice": "78",
|
||||||
|
"vat": "11",
|
||||||
|
"quantity": "10.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "589",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "5941623010333",
|
||||||
|
"ean": "5941623010333",
|
||||||
|
"name": "Doncafe Espresso Intense Cafea Boabe 1 kg",
|
||||||
|
"price": "56.49",
|
||||||
|
"baseprice": "62",
|
||||||
|
"vat": "11",
|
||||||
|
"quantity": "2.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "512",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "82",
|
||||||
|
"ean": "",
|
||||||
|
"name": "Zahar Plic Lavazza 200buc",
|
||||||
|
"price": "10.99",
|
||||||
|
"baseprice": "14",
|
||||||
|
"vat": "21",
|
||||||
|
"quantity": "5.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "671",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "312349",
|
||||||
|
"ean": "",
|
||||||
|
"name": "Palete manuale din lemn 110mm 1000buc",
|
||||||
|
"price": "7.99",
|
||||||
|
"baseprice": "10.5",
|
||||||
|
"vat": "21",
|
||||||
|
"quantity": "2.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "554",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "8004990127091",
|
||||||
|
"ean": "8004990127091",
|
||||||
|
"name": "Ristora Ciocolată Instant 1kg",
|
||||||
|
"price": "25.99",
|
||||||
|
"baseprice": "28",
|
||||||
|
"vat": "21",
|
||||||
|
"quantity": "2.00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "567",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "8004990125530",
|
||||||
|
"ean": "8004990125530",
|
||||||
|
"name": "Prolait Topping Blue Lapte Granulat 500g",
|
||||||
|
"price": "18.49",
|
||||||
|
"baseprice": "23",
|
||||||
|
"vat": "21",
|
||||||
|
"quantity": "5.00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"billing": {
|
||||||
|
"firstname": "Liviu",
|
||||||
|
"lastname": "Stefan",
|
||||||
|
"phone": "0751013764",
|
||||||
|
"email": "liviustefan2001@gmail.com",
|
||||||
|
"address": "aleea Soarelui nr 2, casa nr 4, cartier Brates Lake",
|
||||||
|
"city": "Galați",
|
||||||
|
"region": "Galati",
|
||||||
|
"country": "Romania",
|
||||||
|
"customerId": "5997"
|
||||||
|
},
|
||||||
|
"discounts": [
|
||||||
|
],
|
||||||
|
"updated": "2025-08-27 16:32:43"
|
||||||
|
},
|
||||||
|
"60643": {
|
||||||
|
"id": "60643",
|
||||||
|
"number": "436232175",
|
||||||
|
"date": "2025-08-27 16:19:29",
|
||||||
|
"invoice": {
|
||||||
|
"series": "",
|
||||||
|
"number": "0",
|
||||||
|
"date": "0000-00-00 00:00:00"
|
||||||
|
},
|
||||||
|
"total": "359.4",
|
||||||
|
"status": "Comanda noua",
|
||||||
|
"statusId": "1",
|
||||||
|
"source": "internal",
|
||||||
|
"sales_channel": "Website",
|
||||||
|
"sales_channel_marketplace": "",
|
||||||
|
"sales_agent": "",
|
||||||
|
"currency": "RON",
|
||||||
|
"observation": "",
|
||||||
|
"payment": {
|
||||||
|
"name": "Numerar/Ramburs sau Card la easybox",
|
||||||
|
"online": "0",
|
||||||
|
"completed": "0"
|
||||||
|
},
|
||||||
|
"delivery": {
|
||||||
|
"name": "Transport National",
|
||||||
|
"total": 30,
|
||||||
|
"date": "0000-00-00 00:00:00",
|
||||||
|
"lockerId": 0
|
||||||
|
},
|
||||||
|
"shipping": {
|
||||||
|
"company": "",
|
||||||
|
"firstname": "Alexandra",
|
||||||
|
"lastname": "TONE",
|
||||||
|
"phone": "0763571486",
|
||||||
|
"email": "aristocratgaminggr@gmail.com",
|
||||||
|
"address": "Str. Garii, Nr. 102",
|
||||||
|
"city": "Giurgiu",
|
||||||
|
"region": "Giurgiu",
|
||||||
|
"country": "Romania",
|
||||||
|
"zipcode": null
|
||||||
|
},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": "279",
|
||||||
|
"type": "product",
|
||||||
|
"sku": "30006ozLavazza",
|
||||||
|
"ean": "",
|
||||||
|
"name": "Pahar carton 6oz Lavazza RLP bax 3000buc",
|
||||||
|
"price": "329.4",
|
||||||
|
"baseprice": "360",
|
||||||
|
"vat": "21",
|
||||||
|
"quantity": "1.00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"billing": {
|
||||||
|
"company": {
|
||||||
|
"name": "ARISTOCRAT GAMING SRL",
|
||||||
|
"code": "32128076",
|
||||||
|
"registrationNo": "J27/487/2013",
|
||||||
|
"bank": "",
|
||||||
|
"iban": ""
|
||||||
|
},
|
||||||
|
"firstname": "Alexandra",
|
||||||
|
"lastname": "TONE",
|
||||||
|
"phone": "0763571486",
|
||||||
|
"email": "aristocratgaminggr@gmail.com",
|
||||||
|
"address": "Aleea Revolutiei, Spatiul Comercial Nr.32",
|
||||||
|
"city": "Roman",
|
||||||
|
"region": "Neamt",
|
||||||
|
"country": "Romania",
|
||||||
|
"customerId": "12283"
|
||||||
|
},
|
||||||
|
"discounts": [
|
||||||
|
],
|
||||||
|
"updated": "2025-08-27 16:19:29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shippingAsProduct": false
|
||||||
|
}
|
||||||
137
products-example.json
Normal file
137
products-example.json
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
"total": "Numar total de rezultate",
|
||||||
|
"page": "Pagina curenta",
|
||||||
|
"pages": "Numar total de pagini",
|
||||||
|
"products": {
|
||||||
|
"id": "ID intern al produsului",
|
||||||
|
"sku": "SKU",
|
||||||
|
"name": "Denumire",
|
||||||
|
"description": "Descriere",
|
||||||
|
"short_description": "Descriere scurta",
|
||||||
|
"url": "URL",
|
||||||
|
"canonical_url": "URL canonic",
|
||||||
|
"brand": "Marca produs",
|
||||||
|
"categories": {
|
||||||
|
"Branch 1": [
|
||||||
|
{
|
||||||
|
"id": "ID-ul categoriei",
|
||||||
|
"name": "Denumirea categoriei"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ID-ul categoriei",
|
||||||
|
"name": "Denumirea categoriei"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Branch 2": [
|
||||||
|
{
|
||||||
|
"id": "ID-ul categoriei",
|
||||||
|
"name": "Denumirea categoriei"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ID-ul categoriei",
|
||||||
|
"name": "Denumirea categoriei"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"...": [
|
||||||
|
"..."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"weight": "Greutatea produsului (kg)",
|
||||||
|
"enabled": "Activ (0/1)",
|
||||||
|
"parent": "ID produs parinte",
|
||||||
|
"parent_sku": "SKU produs parinte",
|
||||||
|
"group_key": "Codul de grupare al variantelor de produs",
|
||||||
|
"stockManagement": "Gestioneaza automat stocul produsului",
|
||||||
|
"stock": "Stoc cantitativ",
|
||||||
|
"stockStatus": "Status stoc",
|
||||||
|
"base_price": "Pret de baza al produsului",
|
||||||
|
"price": "Pret de vanzare al produsului (dupa aplicarea adaosurilor si reducerilor)",
|
||||||
|
"vat_included": "Pret include TVA (0/1)",
|
||||||
|
"vat": "Valoare TVA",
|
||||||
|
"currency": "Moneda",
|
||||||
|
"ecotax": "Valoare taxa verde",
|
||||||
|
"um": "Unitate de masura",
|
||||||
|
"html_title": "Titlu html",
|
||||||
|
"html_description": "Descrierere html",
|
||||||
|
"customSearchKeys": "Cuvinte cheie folosite la cautarea SEO",
|
||||||
|
"feedDescription": "Descriere pentru feed-uri",
|
||||||
|
"allowOrdersWhenOutOfStock": "Produsul se aduce la comanda",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"id": "ID atribut",
|
||||||
|
"type": "Tip atribut: dropdown, textinput, textarea",
|
||||||
|
"name": "Denumire atribut",
|
||||||
|
"value": "Optiune"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ID atribut",
|
||||||
|
"type": "Tip atribut multipleselect (accepta valori multiple)",
|
||||||
|
"name": "Denumire atribut",
|
||||||
|
"value": [
|
||||||
|
"Optiune1",
|
||||||
|
"Optiune2",
|
||||||
|
"..."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"images": [
|
||||||
|
"Imagine 1 (principala)",
|
||||||
|
"Imagine 2",
|
||||||
|
"..."
|
||||||
|
],
|
||||||
|
"variations": [
|
||||||
|
{
|
||||||
|
"id": "ID intern al produsului",
|
||||||
|
"sku": "SKU",
|
||||||
|
"base_price": "Pret de baza al produsului",
|
||||||
|
"price": "Pret de vanzare al produsului (dupa aplicarea adaosurilor si reducerilor)",
|
||||||
|
"stock": "Stoc cantitativ",
|
||||||
|
"stockStatus": "Status stoc",
|
||||||
|
"stockManagement": "Gestioneaza automat stocul produsului",
|
||||||
|
"versionAttributes": {
|
||||||
|
"id Attribut": {
|
||||||
|
"name": "Denumire atribut",
|
||||||
|
"value": "Valoare atribut"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ID intern al produsului",
|
||||||
|
"sku": "SKU",
|
||||||
|
"base_price": "Pret de baza al produsului",
|
||||||
|
"price": "Pret de vanzare al produsului (dupa aplicarea adaosurilor si reducerilor)",
|
||||||
|
"stock": "Stoc cantitativ",
|
||||||
|
"stockStatus": "Status stoc",
|
||||||
|
"stockManagement": "Gestioneaza automat stocul produsului",
|
||||||
|
"versionAttributes": {
|
||||||
|
"id Attribut": {
|
||||||
|
"id": "ID atribut",
|
||||||
|
"name": "Denumire atribut",
|
||||||
|
"value": "Valoare atribut"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ean": "EAN",
|
||||||
|
"videos": [
|
||||||
|
"URL video"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"URL fisiere"
|
||||||
|
],
|
||||||
|
"updated": "Ultima modificare",
|
||||||
|
"created": "Data crearii",
|
||||||
|
"delivery_time": "Timp de livrare",
|
||||||
|
"delivery_time_type": "Tip timp de livrare",
|
||||||
|
"bundleItems": [
|
||||||
|
{
|
||||||
|
"sku": "SKU componenta",
|
||||||
|
"quantity": "Cantitate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sku": "SKU componenta",
|
||||||
|
"quantity": "Cantitate"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
268
regex.prg
Normal file
268
regex.prg
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
*!* CLEAR
|
||||||
|
*!* ?strtranx([ana are 1234567890.1234 lei], [\s\d+\.\d\s], [=TRANSFORM($1, "999 999 999 999.99")])
|
||||||
|
*?strtranx([ana are <<1234567890.1234>> lei], [<<], [=TRANSFORM($1, "AA")])
|
||||||
|
*!* RETURN
|
||||||
|
CLEAR
|
||||||
|
|
||||||
|
|
||||||
|
*-- http://www.cornerstonenw.com/article_id_parsing3.htm
|
||||||
|
SET STEP ON
|
||||||
|
|
||||||
|
lcSourceString = [ana are mere 123,345 678 ad]
|
||||||
|
LOCAL laItems[10]
|
||||||
|
|
||||||
|
lnResults = GetRegExpAll(lcSourceString, '\d+', @laItems)
|
||||||
|
|
||||||
|
SET STEP ON
|
||||||
|
RETURN
|
||||||
|
strTest = [ab cd2""$$<24>]
|
||||||
|
?strTest
|
||||||
|
?StripNonAscii(strTest)
|
||||||
|
|
||||||
|
*-- replace non a-z09 with "" case-insensitive
|
||||||
|
? strtranx([Ab ra /ca\d&abr'a],"[^a-z0-9]",[],1,,1)
|
||||||
|
RETURN
|
||||||
|
|
||||||
|
*-- count words
|
||||||
|
? OccursRegExp("\b(\w+)\b", [the then quick quick brown fox fox])
|
||||||
|
&& prints 7
|
||||||
|
|
||||||
|
*-- count repeatedwords
|
||||||
|
? OccursRegExp("\b(\w+)\s\1\b", [the then quick quick brown fox fox])
|
||||||
|
&& prints 2
|
||||||
|
|
||||||
|
|
||||||
|
*-- replace first and second lower-case "a"
|
||||||
|
? strtranx([Abracadabra],[a],[*],1,2)
|
||||||
|
&& prints Abr*c*dabra
|
||||||
|
|
||||||
|
*-- replace first and second "a" case-insensitive
|
||||||
|
? strtranx([Abracadabra],[a],[*],1,2,1)
|
||||||
|
&& prints *br*cadabra
|
||||||
|
|
||||||
|
*-- locate the replacement targets
|
||||||
|
? strtranx([Abracadabra],[^a|a$],[*],1,2,0)
|
||||||
|
&& Abracadabr*
|
||||||
|
? strtranx([Abracadabra],[^a|a$],[*],1,2,1)
|
||||||
|
&& *bracadabr*
|
||||||
|
|
||||||
|
|
||||||
|
lcText = "The cost, is $123,345.75. "
|
||||||
|
*-- convert the commas
|
||||||
|
lcText = strtranx( m.lcText, "(\d{1,3})\,(\d{1,}) ","$1 $2" )
|
||||||
|
|
||||||
|
*-- convert the decimals
|
||||||
|
? strtranx( m.lcText, "(\d{1,3})\.(\d{1,})", "$1,$2" )
|
||||||
|
|
||||||
|
** prints "The cost, is $123 345,75."
|
||||||
|
|
||||||
|
*-- add 1 to all digits
|
||||||
|
? strtranx( [ABC123], "(\d)", [=TRANSFORM(VAL($1)+1)] )
|
||||||
|
** prints "ABC234"
|
||||||
|
|
||||||
|
*-- convert all dates to long format
|
||||||
|
? strtranx( [the date is: 7/18/2004 ] , [(\d{1,2}/\d{1,2}/\d{4})], [=TRANSFORM(CTOD($1),"@YL")])
|
||||||
|
** prints "the date is: Sunday, July 18, 2004"
|
||||||
|
|
||||||
|
|
||||||
|
*----------------------------------------------------------
|
||||||
|
FUNCTION StrtranRegExp( tcSourceString, tcPattern, tcReplace )
|
||||||
|
LOCAL loRE
|
||||||
|
loRE = CREATEOBJECT("vbscript.regexp")
|
||||||
|
WITH loRE
|
||||||
|
.PATTERN = tcPattern
|
||||||
|
.GLOBAL = .T.
|
||||||
|
.multiline = .T.
|
||||||
|
RETURN .REPLACE( tcSourceString , tcReplace )
|
||||||
|
ENDWITH
|
||||||
|
ENDFUNC
|
||||||
|
|
||||||
|
*----------------------------------------------------------
|
||||||
|
FUNCTION OccursRegExp(tcPattern, tcText)
|
||||||
|
LOCAL loRE, loMatches, lnResult
|
||||||
|
loRE = CREATEOBJECT("vbscript.regexp")
|
||||||
|
WITH loRE
|
||||||
|
.PATTERN = m.tcPattern
|
||||||
|
.GLOBAL = .T.
|
||||||
|
.multiline = .T.
|
||||||
|
loMatches = loRE.Execute( m.tcText )
|
||||||
|
lnResult = loMatches.COUNT
|
||||||
|
loMatches = NULL
|
||||||
|
ENDWITH
|
||||||
|
RETURN m.lnResult
|
||||||
|
ENDFUNC
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*----------------------------------------------------------
|
||||||
|
FUNCTION strtranx(tcSearched, ;
|
||||||
|
tcSearchFor, ;
|
||||||
|
tcReplacement, ;
|
||||||
|
tnStart, tnNumber, ;
|
||||||
|
tnFlag )
|
||||||
|
|
||||||
|
*-- the final version of the UDF
|
||||||
|
LOCAL loRE, lcText, lnShift, lcCommand,;
|
||||||
|
loMatch, loMatches, lnI, lnK, lcSubMatch,;
|
||||||
|
llevaluate, lcMatchDelim, lcReplaceText, lcReplacement,;
|
||||||
|
lnStart, lnNumber, loCol, lcKey
|
||||||
|
|
||||||
|
IF EMPTY(NVL(tcSearched, ''))
|
||||||
|
RETURN NVL(tcSearched, '')
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
loRE = CREATEOBJECT("vbscript.regexp")
|
||||||
|
|
||||||
|
WITH loRE
|
||||||
|
.PATTERN = m.tcSearchFor
|
||||||
|
.GLOBAL = .T.
|
||||||
|
.multiline = .T.
|
||||||
|
.ignorecase = IIF(VARTYPE(m.tnFlag)=[N],m.tnFlag = 1,.F.)
|
||||||
|
ENDWITH
|
||||||
|
|
||||||
|
lcReplacement = m.tcReplacement
|
||||||
|
|
||||||
|
*--- are we evaluating?
|
||||||
|
IF m.lcReplacement = [=]
|
||||||
|
llevaluate = .T.
|
||||||
|
lcReplacement = SUBSTR( m.lcReplacement, 2 )
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
IF VARTYPE( m.tnStart )=[N]
|
||||||
|
lnStart = m.tnStart
|
||||||
|
ELSE
|
||||||
|
lnStart = 1
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
IF VARTYPE( m.tnNumber) =[N]
|
||||||
|
lnNumber = m.tnNumber
|
||||||
|
ELSE
|
||||||
|
lnNumber = -1
|
||||||
|
ENDIF
|
||||||
|
|
||||||
|
IF m.lnStart>1 OR m.lnNumber#-1 OR m.llevaluate
|
||||||
|
|
||||||
|
lcText = m.tcSearched
|
||||||
|
lnShift = 1
|
||||||
|
loMatches = loRE.execute( m.lcText )
|
||||||
|
loCol = CREATEOBJECT([collection])
|
||||||
|
lnNumber = IIF( lnNumber=-1,loMatches.COUNT,MIN(lnNumber,loMatches.COUNT))
|
||||||
|
|
||||||
|
FOR lnK = m.lnStart TO m.lnNumber
|
||||||
|
loMatch = loMatches.ITEM(m.lnK-1) && zero based
|
||||||
|
lcCommand = m.lcReplacement
|
||||||
|
FOR lnI= 1 TO loMatch.submatches.COUNT
|
||||||
|
lcSubMatch = loMatch.submatches(m.lnI-1) && zero based
|
||||||
|
IF m.llevaluate
|
||||||
|
* "escape" the string we are about to use in an evaluation.
|
||||||
|
* it is important to escape due to possible delim chars (like ", ' etc)
|
||||||
|
* malicious content, or VFP line-length violations.
|
||||||
|
lcKey = ALLTRIM(TRANSFORM(m.lnK)+[_]+TRANSFORM(m.lnI))
|
||||||
|
loCol.ADD( m.lcSubMatch, m.lcKey )
|
||||||
|
lcSubMatch = [loCol.item(']+m.lcKey+[')]
|
||||||
|
ENDIF
|
||||||
|
lcCommand = STRTRAN( m.lcCommand, "$" + ALLTRIM( STR( m.lnI ) ) , m.lcSubMatch)
|
||||||
|
ENDFOR
|
||||||
|
|
||||||
|
IF m.llevaluate
|
||||||
|
TRY
|
||||||
|
lcReplaceText = EVALUATE( m.lcCommand )
|
||||||
|
CATCH TO loErr
|
||||||
|
lcReplaceText="[[ERROR #"+TRANSFORM(loErr.ERRORNO)+[ ]+loErr.MESSAGE+"]]"
|
||||||
|
ENDTRY
|
||||||
|
ELSE
|
||||||
|
lcReplaceText = m.lcCommand
|
||||||
|
ENDIF
|
||||||
|
lcText = STUFF( m.lcText, loMatch.FirstIndex + m.lnShift, m.loMatch.LENGTH, m.lcReplaceText )
|
||||||
|
lnShift = m.lnShift + LEN( m.lcReplaceText ) - m.loMatch.LENGTH
|
||||||
|
ENDFOR
|
||||||
|
ELSE
|
||||||
|
lcText = loRE.REPLACE( m.tcSearched, m.tcReplacement )
|
||||||
|
ENDIF
|
||||||
|
RETURN m.lcText
|
||||||
|
ENDFUNC
|
||||||
|
|
||||||
|
*=====================
|
||||||
|
FUNCTION StripNonAscii
|
||||||
|
LPARAMETERS tcSourceString, tcReplaceString
|
||||||
|
|
||||||
|
TEXT TO lcPattern NOSHOW
|
||||||
|
[^A-Za-z 0-9 \.,\?'""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]
|
||||||
|
ENDTEXT
|
||||||
|
lcReplace = IIF(TYPE('tcReplaceString') <> 'C', "", tcReplaceString)
|
||||||
|
lcReturn = strtranx( m.tcSourceString, m.lcPattern, m.lcReplace,1,,1)
|
||||||
|
|
||||||
|
RETURN m.lcReturn
|
||||||
|
ENDFUNC && StripNonAscii
|
||||||
|
|
||||||
|
*=====================
|
||||||
|
* Intoarce un text care se potriveste cu pattern-ul
|
||||||
|
* Ex. Localitatea din textul: STRADA NR LOCALITATE
|
||||||
|
*=====================
|
||||||
|
FUNCTION GetRegExp
|
||||||
|
LPARAMETERS tcSourceString, tcPattern, tnOccurence
|
||||||
|
|
||||||
|
* tcSourceString: Bld. Stefan cel Mare 14 Tirgu Neamt
|
||||||
|
* tcPattern: [A-Za-z\s]+$ = (caracter sau spatiu) de cel putin o data la sfarsitul liniei = Tirgu Neamt
|
||||||
|
* tcPattern: \d+[A-Za-z\s]+$ = oricate cifre (caracter sau spatiu) de cel putin o data la sfarsitul liniei = 14 Tirgu Neamt
|
||||||
|
|
||||||
|
LOCAL loRE, loMatches, lcResult, lnOccurence
|
||||||
|
lcResult = ''
|
||||||
|
lnOccurence = IIF(!EMPTY(m.tnOccurence) and TYPE('tnOccurence') = 'N', m.tnOccurence, 1)
|
||||||
|
|
||||||
|
loRE = CREATEOBJECT("vbscript.regexp")
|
||||||
|
WITH loRE
|
||||||
|
.PATTERN = m.tcPattern
|
||||||
|
.GLOBAL = .T.
|
||||||
|
.multiline = .T.
|
||||||
|
loMatches = loRE.Execute( m.tcSourceString)
|
||||||
|
IF loMatches.COUNT >= m.lnOccurence
|
||||||
|
lcResult = loMatches.Item(m.lnOccurence - 1).Value
|
||||||
|
ENDIF
|
||||||
|
loMatches = NULL
|
||||||
|
ENDWITH
|
||||||
|
|
||||||
|
RETURN m.lcResult
|
||||||
|
ENDFUNC && GetRegExp
|
||||||
|
|
||||||
|
*=====================
|
||||||
|
* Intoarce numarul potrivirilor si un parametru OUT array sau lista de numere facturi separate prin ","
|
||||||
|
* Ex. Toate numerele dintr-un text lnMatches = GetRegExpAll(lcSourceString, '\d+', @loMatches)
|
||||||
|
*=====================
|
||||||
|
FUNCTION GetRegExpAll
|
||||||
|
LPARAMETERS tcSourceString, tcPattern, taItems
|
||||||
|
|
||||||
|
* tcSourceString: Bld. Stefan cel Mare 14 Tirgu Neamt
|
||||||
|
* tcPattern: [A-Za-z\s]+$ = (caracter sau spatiu) de cel putin o data la sfarsitul liniei = Tirgu Neamt
|
||||||
|
* tcPattern: \d+[A-Za-z\s]+$ = oricate cifre (caracter sau spatiu) de cel putin o data la sfarsitul liniei = 14 Tirgu Neamt
|
||||||
|
* taItems "A">taItems : array cu rezultatele (OUT) taItems[1..Result] sau taItems "C" lista facturi separate prin virgula
|
||||||
|
LOCAL loRE, loMatches, lnResults, lnItem
|
||||||
|
IF TYPE('taItems') = "A"
|
||||||
|
EXTERNAL ARRAY taItems
|
||||||
|
ELSE
|
||||||
|
taItems = ""
|
||||||
|
ENDIF
|
||||||
|
lnResult = 0
|
||||||
|
|
||||||
|
loRE = CREATEOBJECT("vbscript.regexp")
|
||||||
|
WITH loRE
|
||||||
|
.PATTERN = m.tcPattern
|
||||||
|
.GLOBAL = .T.
|
||||||
|
.multiline = .T.
|
||||||
|
loMatches = loRE.Execute( m.tcSourceString)
|
||||||
|
lnResults = loMatches.COUNT
|
||||||
|
IF TYPE('taItems') = "A"
|
||||||
|
DIMENSION taItems[m.lnResult]
|
||||||
|
FOR lnItem = 1 TO m.lnResult
|
||||||
|
taItems[m.lnItem] = loMatches.Item(m.lnItem-1).Value
|
||||||
|
ENDFOR
|
||||||
|
ELSE
|
||||||
|
FOR lnItem = 1 TO m.lnResults
|
||||||
|
taItems = taItems + IIF(m.lnItem > 1, ",", "") + loMatches.Item(m.lnItem-1).Value
|
||||||
|
ENDFOR
|
||||||
|
ENDIF
|
||||||
|
loMatches = NULL
|
||||||
|
ENDWITH
|
||||||
|
|
||||||
|
RETURN m.lnResults
|
||||||
|
ENDFUNC && GetRegExp
|
||||||
4
run-gomag.bat
Normal file
4
run-gomag.bat
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0"
|
||||||
|
"C:\Program Files (x86)\Microsoft Visual FoxPro 9\vfp9.exe" -T "%~dp0gomag-vending.prg"
|
||||||
|
pause
|
||||||
17
settings.ini
Normal file
17
settings.ini
Normal file
@@ -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=1
|
||||||
|
GetOrders=1
|
||||||
|
|
||||||
|
[FILTERS]
|
||||||
|
OrderDaysBack=7
|
||||||
264
utils.prg
Normal file
264
utils.prg
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
*-- 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
|
||||||
|
|
||||||
|
*-- 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
|
||||||
Reference in New Issue
Block a user