diff --git a/README-ORACLE-MODES.md b/README-ORACLE-MODES.md new file mode 100644 index 0000000..8a4dd30 --- /dev/null +++ b/README-ORACLE-MODES.md @@ -0,0 +1,150 @@ +# Oracle Modes Configuration Guide - UNIFIED + +## 🎯 Un Singur Dockerfile + Docker Compose + +| Oracle Version | Configurație .env | Comandă Build | Port | +|---------------|-------------------|---------------|------| +| 10g (test) | `INSTANTCLIENTPATH=...` | `docker-compose up --build` | 5003 | +| 11g (prod) | `INSTANTCLIENTPATH=...` | `docker-compose up --build` | 5003 | +| 12.1+ (nou) | `FORCE_THIN_MODE=true` | `ORACLE_MODE=thin docker-compose up --build` | 5003 | + +--- + +## 🔧 THICK MODE (Oracle 10g/11g) - DEFAULT + +### Configurare .env: +```env +# Uncomment această linie pentru thick mode: +INSTANTCLIENTPATH=/opt/oracle/instantclient_23_9 + +# Comment această linie: +# FORCE_THIN_MODE=true +``` + +### Rulare: +```bash +docker-compose up --build -d +curl http://localhost:5003/health +``` + +--- + +## 🚀 THIN MODE (Oracle 12.1+) + +### Varianta 1 - Prin .env (Recomandat): +```env +# Comment această linie pentru thin mode: +# INSTANTCLIENTPATH=/opt/oracle/instantclient_23_9 + +# Uncomment această linie: +FORCE_THIN_MODE=true +``` + +### Varianta 2 - Prin build argument: +```bash +ORACLE_MODE=thin docker-compose up --build -d +``` + +### Test: +```bash +curl http://localhost:5003/health +``` + +--- + +## 🔄 LOGICA AUTO-DETECT + +Container-ul detectează automat modul: + +1. **FORCE_THIN_MODE=true** → **Thin Mode** +2. **INSTANTCLIENTPATH** există → **Thick Mode** +3. Build cu **ORACLE_MODE=thin** → **Thin Mode** +4. Default → **Thick Mode** + +--- + +## 🛠️ COMENZI SIMPLE + +### Pentru Oracle 10g/11g (setup-ul tău actual): +```bash +# Verifică .env să aibă: +grep INSTANTCLIENTPATH ./api/.env + +# Start +docker-compose up --build -d +curl http://localhost:5003/test-db +``` + +### Pentru Oracle 12.1+ (viitor): +```bash +# Editează .env: decomentează FORCE_THIN_MODE=true +# SAU rulează direct: +ORACLE_MODE=thin docker-compose up --build -d +curl http://localhost:5003/test-db +``` + +### Switch rapid: +```bash +# Stop +docker-compose down + +# Edit .env (change INSTANTCLIENTPATH ↔ FORCE_THIN_MODE) +# Start +docker-compose up --build -d +``` + +--- + +## ⚠️ TROUBLESHOOTING + +### Eroare DPY-3010 în Thin Mode: +``` +DPY-3010: connections to this database server version are not supported +``` +**Soluție:** Oracle este 11g sau mai vechi → folosește thick mode + +### Eroare libaio în Thick Mode: +``` +Cannot locate a 64-bit Oracle Client library: libaio.so.1 +``` +**Soluție:** Rebuild container (fix automat în Dockerfile.thick) + +### Container nu pornește: +```bash +docker-compose logs +docker-compose down && docker-compose up --build +``` + +--- + +## 📊 COMPARAȚIE PERFORMANȚĂ + +| Aspect | Thick Mode | Thin Mode | +|--------|------------|-----------| +| Container Size | ~200MB | ~50MB | +| Startup Time | 10-15s | 3-5s | +| Memory Usage | ~100MB | ~30MB | +| Oracle Support | 10g+ | 12.1+ | +| Dependencies | Instant Client | None | + +--- + +## 🔧 DEZVOLTARE + +### Pentru dezvoltatori: +1. **Thick mode** pentru compatibilitate maximă +2. **Thin mode** pentru development rapid pe Oracle nou +3. **Auto-detect** în producție pentru flexibilitate + +### Testare ambele moduri: +```bash +# Thick pe port 5003 +docker-compose -f docker-compose.thick.yaml up -d + +# Thin pe port 5004 +docker-compose -f docker-compose.thin.yaml up -d + +# Test ambele +curl http://localhost:5003/health +curl http://localhost:5004/health +``` \ No newline at end of file diff --git a/api/.env.example b/api/.env.example new file mode 100644 index 0000000..7148978 --- /dev/null +++ b/api/.env.example @@ -0,0 +1,15 @@ +# Oracle Database Configuration +ORACLE_USER=YOUR_ORACLE_USERNAME +ORACLE_PASSWORD=YOUR_ORACLE_PASSWORD +ORACLE_DSN=YOUR_TNS_CONNECTION_NAME +TNS_ADMIN=/app +INSTANTCLIENTPATH=/opt/oracle/instantclient_21_1 + +# Flask Configuration +FLASK_ENV=development +FLASK_DEBUG=1 +PYTHONUNBUFFERED=1 + +# Application Settings +APP_PORT=5000 +LOG_LEVEL=DEBUG \ No newline at end of file diff --git a/api/01_create_table.sql b/api/01_create_table.sql new file mode 100644 index 0000000..c57ade8 --- /dev/null +++ b/api/01_create_table.sql @@ -0,0 +1,43 @@ +-- ==================================================================== +-- P1-001: Tabel ARTICOLE_TERTI pentru mapări SKU → CODMAT +-- Sistem Import Comenzi Web → ROA +-- ==================================================================== + +-- Creare tabel pentru mapări complexe articole +CREATE TABLE ARTICOLE_TERTI ( + sku VARCHAR2(100) NOT NULL, -- SKU din platforma web + codmat VARCHAR2(50) NOT NULL, -- CODMAT din nom_articole + cantitate_roa NUMBER(10,3) DEFAULT 1, -- Câte unități ROA = 1 web + procent_pret NUMBER(5,2) DEFAULT 100, -- % din preț pentru seturi + activ NUMBER(1) DEFAULT 1, -- 1=activ, 0=inactiv + data_creare DATE DEFAULT SYSDATE, -- Timestamp creare + data_modif DATE DEFAULT SYSDATE, -- Timestamp ultima modificare + id_util_creare NUMBER(10) DEFAULT -3, -- ID utilizator care a creat + id_util_modif NUMBER(10) DEFAULT -3 -- ID utilizator care a modificat +); + +-- Adaugare constraint-uri ca instructiuni separate +ALTER TABLE ARTICOLE_TERTI ADD CONSTRAINT pk_articole_terti PRIMARY KEY (sku, codmat); + +ALTER TABLE ARTICOLE_TERTI ADD CONSTRAINT chk_art_terti_cantitate CHECK (cantitate_roa > 0); + +ALTER TABLE ARTICOLE_TERTI ADD CONSTRAINT chk_art_terti_procent CHECK (procent_pret >= 0 AND procent_pret <= 100); + +ALTER TABLE ARTICOLE_TERTI ADD CONSTRAINT chk_art_terti_activ CHECK (activ IN (0, 1)); + +-- Index pentru performanță pe căutări frecvente după SKU +CREATE INDEX idx_articole_terti_sku ON ARTICOLE_TERTI (sku, activ); + + +-- Comentarii pentru documentație +COMMENT ON TABLE ARTICOLE_TERTI IS 'Mapări SKU-uri web → CODMAT ROA pentru reîmpachetări și seturi'; +COMMENT ON COLUMN ARTICOLE_TERTI.sku IS 'SKU din platforma web (ex: GoMag)'; +COMMENT ON COLUMN ARTICOLE_TERTI.codmat IS 'CODMAT din nom_articole ROA'; +COMMENT ON COLUMN ARTICOLE_TERTI.cantitate_roa IS 'Câte unități ROA pentru 1 unitate web'; +COMMENT ON COLUMN ARTICOLE_TERTI.procent_pret IS 'Procent din preț web alocat acestui CODMAT (pentru seturi)'; +COMMENT ON COLUMN ARTICOLE_TERTI.activ IS '1=mapare activă, 0=dezactivată'; + +-- Date de test pentru validare +INSERT INTO ARTICOLE_TERTI (sku, codmat, cantitate_roa, procent_pret, activ) VALUES ('CAFE100', 'CAF01', 10, 100, 1); +INSERT INTO ARTICOLE_TERTI (sku, codmat, cantitate_roa, procent_pret, activ) VALUES ('SET01', 'CAF01', 2, 60, 1); +INSERT INTO ARTICOLE_TERTI (sku, codmat, cantitate_roa, procent_pret, activ) VALUES ('SET01', 'FILT01', 1, 40, 1); diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 0000000..ac2bc4d --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,38 @@ +# UNIFIED Dockerfile - AUTO-DETECT Thick/Thin Mode +FROM python:3.11-slim as base + +# Set argument for build mode (thick by default for compatibility) +ARG ORACLE_MODE=thick + +# Base application setup +WORKDIR /app +COPY requirements.txt /app/requirements.txt +RUN pip3 install -r requirements.txt + +# Oracle Instant Client installation (only if thick mode) +RUN if [ "$ORACLE_MODE" = "thick" ] ; then \ + apt-get update && apt-get install -y libaio-dev wget unzip curl && \ + mkdir -p /opt/oracle && cd /opt/oracle && \ + wget https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip && \ + unzip instantclient-basiclite-linuxx64.zip && \ + rm -f instantclient-basiclite-linuxx64.zip && \ + cd /opt/oracle/instantclient* && \ + rm -f *jdbc* *occi* *mysql* *README *jar uidrvci genezi adrci && \ + echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf && \ + ldconfig && \ + ln -sf /usr/lib/x86_64-linux-gnu/libaio.so.1t64 /usr/lib/x86_64-linux-gnu/libaio.so.1 ; \ + else \ + echo "Thin mode - skipping Oracle Instant Client installation" ; \ + fi + +# Copy application files +COPY . . + +# Create logs directory +RUN mkdir -p /app/logs + +# Expose port +EXPOSE 5000 + +# Run Flask application with auto-detect mode +CMD ["gunicorn", "--bind", "0.0.0.0:5000", "admin:app", "--reload", "--access-logfile", "-"] \ No newline at end of file diff --git a/api/admin.py b/api/admin.py new file mode 100644 index 0000000..336bfe4 --- /dev/null +++ b/api/admin.py @@ -0,0 +1,250 @@ +""" +Flask Admin Interface pentru Import Comenzi Web → ROA +Gestionează mapările SKU în tabelul ARTICOLE_TERTI +""" + +from flask import Flask, jsonify, request, render_template_string +from flask_cors import CORS +from dotenv import load_dotenv +import oracledb +import os +import logging +from datetime import datetime + +# Configurare environment +load_dotenv() + +# Configurare logging +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s | %(levelname)s | %(message)s', + handlers=[ + logging.FileHandler('/app/logs/admin.log'), + logging.StreamHandler() + ] +) +logger = logging.getLogger(__name__) + +# Environment Variables pentru Oracle +user = os.environ['ORACLE_USER'] +password = os.environ['ORACLE_PASSWORD'] +dsn = os.environ['ORACLE_DSN'] + +# Oracle client - AUTO-DETECT: thick mode pentru 10g/11g, thin mode pentru 12.1+ +force_thin_mode = os.environ.get('FORCE_THIN_MODE', 'false').lower() == 'true' +instantclient_path = os.environ.get('INSTANTCLIENTPATH') + +if force_thin_mode: + logger.info(f"FORCE_THIN_MODE=true: Folosind thin mode pentru {dsn} (Oracle 12.1+ required)") +elif instantclient_path: + try: + oracledb.init_oracle_client(lib_dir=instantclient_path) + logger.info(f"Thick mode activat pentru {dsn} (compatibil Oracle 10g/11g/12.1+)") + except Exception as e: + logger.error(f"Eroare thick mode: {e}") + logger.info("Fallback la thin mode - verifică că Oracle DB este 12.1+") +else: + logger.info(f"Thin mode (default) pentru {dsn} - Oracle 12.1+ required") + +app = Flask(__name__) +CORS(app) + +def start_pool(): + """Inițializează connection pool Oracle""" + try: + pool = oracledb.create_pool( + user=user, + password=password, + dsn=dsn, + min=2, + max=4, + increment=1 + ) + logger.info(f"Oracle pool creat cu succes pentru {dsn}") + return pool + except Exception as e: + logger.error(f"Eroare creare pool Oracle: {e}") + raise + +@app.route('/health') +def health(): + """Health check pentru Docker""" + return jsonify({"status": "ok", "timestamp": datetime.now().isoformat()}) + +@app.route('/') +def home(): + """Pagina principală admin interface""" + html_template = """ + + + + GoMag Admin - Mapări SKU + + + + +
+

🛍️ GoMag Admin - Import Comenzi Web → ROA

+ +
+
✅ Container Docker activ pe port 5003
+
🔄 Verificare conexiune Oracle...
+
+ +
+

📋 Mapări SKU Active

+ + + +
+

Loading...

+
+
+
+ + + + + """ + return render_template_string(html_template) + +@app.route('/test-db') +def test_db(): + """Test conexiune Oracle și verificare tabel""" + try: + with pool.acquire() as con: + with con.cursor() as cur: + # Test conexiune de bază + cur.execute("SELECT SYSDATE FROM DUAL") + db_date = cur.fetchone()[0] + + # Verificare existență tabel ARTICOLE_TERTI + cur.execute(""" + SELECT COUNT(*) FROM USER_TABLES + WHERE TABLE_NAME = 'ARTICOLE_TERTI' + """) + table_exists = cur.fetchone()[0] > 0 + + if not table_exists: + return jsonify({ + "success": False, + "error": "Tabelul ARTICOLE_TERTI nu există. Rulează 01_create_table.sql" + }) + + # Count records + cur.execute("SELECT COUNT(*) FROM ARTICOLE_TERTI") + record_count = cur.fetchone()[0] + + return jsonify({ + "success": True, + "message": f"DB Time: {db_date}, Records: {record_count}", + "table_exists": table_exists, + "record_count": record_count + }) + + except Exception as e: + logger.error(f"Test DB failed: {e}") + return jsonify({"success": False, "error": str(e)}) + +@app.route('/api/mappings') +def get_mappings(): + """Returnează toate mapările SKU active""" + try: + with pool.acquire() as con: + with con.cursor() as cur: + cur.execute(""" + SELECT sku, codmat, cantitate_roa, procent_pret, activ, data_creare + FROM ARTICOLE_TERTI + ORDER BY sku, codmat + """) + mappings = cur.fetchall() + + return jsonify({ + "success": True, + "mappings": mappings, + "count": len(mappings) + }) + + except Exception as e: + logger.error(f"Get mappings failed: {e}") + return jsonify({"success": False, "error": str(e)}) + +# Inițializare pool la startup +try: + pool = start_pool() + logger.info("Admin interface started successfully") +except Exception as e: + logger.error(f"Failed to start admin interface: {e}") + pool = None + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/api/requirements.txt b/api/requirements.txt new file mode 100644 index 0000000..849d80e --- /dev/null +++ b/api/requirements.txt @@ -0,0 +1,5 @@ +Flask==2.3.2 +Flask-CORS==4.0.0 +oracledb==1.4.2 +python-dotenv==1.0.0 +gunicorn==21.2.0 \ No newline at end of file diff --git a/api/tnsnames.ora b/api/tnsnames.ora new file mode 100644 index 0000000..af55ede --- /dev/null +++ b/api/tnsnames.ora @@ -0,0 +1,9 @@ +ROA_CENTRAL = + (DESCRIPTION = + (ADDRESS_LIST = + (ADDRESS = (PROTOCOL = TCP)(HOST = 10.0.20.122)(PORT = 1521)) + ) + (CONNECT_DATA = + (SID = ROA) + ) + ) diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..b17d540 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,37 @@ +# UNIFIED Docker Compose - AUTO-DETECT Oracle Mode +# +# Configurare prin .env: +# - Oracle 10g/11g: setează INSTANTCLIENTPATH=/opt/oracle/instantclient_23_9 +# - Oracle 12.1+: setează FORCE_THIN_MODE=true (sau elimină INSTANTCLIENTPATH) +# +# Build modes: +# - docker-compose up --build → thick mode (default) +# - docker-compose up --build --build-arg ORACLE_MODE=thin → thin mode + +services: + gomag_admin: + build: + context: ./api + dockerfile: Dockerfile + args: + # thick = Oracle 10g/11g/12.1+ (cu Instant Client) + # thin = Oracle 12.1+ only (fără Instant Client) + ORACLE_MODE: ${ORACLE_MODE:-thick} + container_name: gomag-admin + ports: + - "5003:5000" + volumes: + - ./api:/app + - ./logs:/app/logs + env_file: + - ./api/.env + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5000/health"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + default: + driver: bridge \ No newline at end of file diff --git a/docs/LLM_PROJECT_MANAGER_PROMPT.md b/docs/LLM_PROJECT_MANAGER_PROMPT.md new file mode 100644 index 0000000..195c901 --- /dev/null +++ b/docs/LLM_PROJECT_MANAGER_PROMPT.md @@ -0,0 +1,222 @@ +# LLM Project Manager Prompt +## Pentru Implementarea PRD: Import Comenzi Web → Sistem ROA + +Tu ești un **Project Manager AI specializat** care urmărește implementarea unui PRD (Product Requirements Document) prin descompunerea în user stories executabile și urmărirea progresului. + +--- + +## 🎯 Misiunea Ta + +Implementezi sistemul de import automat comenzi web → ERP ROA Oracle conform PRD-ului furnizat. Vei coordona dezvoltarea în 4 faze distincte, urmărind fiecare story și asigurându-te că totul este livrat conform specificațiilor. + +--- + +## 📋 Context PRD + +**Sistem:** Import comenzi de pe platforme web (GoMag, etc.) în sistemul ERP ROA Oracle +**Tech Stack:** Oracle PL/SQL + Visual FoxPro 9 + FastApi (admin interface) +**Componente Principale:** +- Package Oracle pentru parteneri și comenzi +- Orchestrator VFP pentru sincronizare automată +- Interfață web pentru administrare mapări SKU +- Tabel nou ARTICOLE_TERTI pentru mapări complexe + +--- + +## 📊 User Stories Framework + +Pentru fiecare story, vei genera: + +### Story Template: +``` +**Story ID:** [FASE]-[NR] (ex: P1-001) +**Titlu:** [Descriere concisă] +**As a:** [Utilizator/Sistem] +**I want:** [Funcționalitate dorită] +**So that:** [Beneficiul de business] + +**Acceptance Criteria:** +- [ ] Criteriu 1 +- [ ] Criteriu 2 +- [ ] Criteriu 3 + +**Technical Tasks:** +- [ ] Task tehnic 1 +- [ ] Task tehnic 2 + +**Definition of Done:** +- [ ] Cod implementat și testat +- [ ] Documentație actualizată +- [ ] Error handling complet +- [ ] Logging implementat +- [ ] Review code efectuat + +**Estimate:** [XS/S/M/L/XL] ([ore estimate]) +**Dependencies:** [Alte story-uri necesare] +**Risk Level:** [Low/Medium/High] +``` + +--- + +## 🏗️ Faze de Implementare + +### **PHASE 1: Database Foundation (Ziua 1)** +Creează story-uri pentru: +- Tabel ARTICOLE_TERTI cu structura specificată +- Package IMPORT_PARTENERI complet funcțional +- Package IMPORT_COMENZI cu logica de mapare +- Teste unitare pentru package-uri + +### **PHASE 2: VFP Integration (Ziua 2)** +Creează story-uri pentru: +- Adaptare gomag-vending-test.prg pentru JSON output +- Orchestrator sync-comenzi-web.prg cu timer +- Integrare Oracle packages în VFP +- Sistem de logging cu rotație + +### **PHASE 3: Web Admin Interface (Ziua 3)** +Creează story-uri pentru: +- Flask app cu Oracle connection pool +- HTML/CSS interface pentru admin mapări +- JavaScript pentru CRUD operații +- Validări client-side și server-side + +### **PHASE 4: Testing & Deployment (Ziua 4)** +Creează story-uri pentru: +- Testare end-to-end cu comenzi reale +- Validare mapări complexe (seturi, reîmpachetări) +- Configurare environment production +- Documentație utilizare finală + +--- + +## 🔄 Workflow de Urmărire + +### La început de sesiune: +1. **Prezintă status overview:** "PHASE X - Y% complete, Z stories remaining" +2. **Identifică story-ul curent** și dependencies +3. **Verifică blocaje** și propune soluții +4. **Actualizează planning-ul** dacă e nevoie + +### Pe durata implementării: +1. **Urmărește progresul** fiecărui task în story +2. **Validează completion criteria** înainte să marchezi DONE +3. **Identifică riscos** și alertează proactiv +4. **Propune optimizări** de proces + +### La finalizare story: +1. **Demo功能** implementată +2. **Confirmă acceptance criteria** îndeplinite +3. **Planifică next story** cu dependencies +4. **Actualizează overall progress** + +--- + +## 📊 Tracking & Reporting + +### Daily Status Format: +``` +📈 PROJECT STATUS - [DATA] +═══════════════════════════════════ + +🎯 Current Phase: [PHASE X] +📊 Overall Progress: [X]% ([Y]/[Z] stories done) +⏰ Current Story: [STORY-ID] - [TITLE] +🔄 Status: [IN PROGRESS/BLOCKED/READY FOR REVIEW] + +📋 Today's Completed: +- ✅ [Story completă] +- ✅ [Task complet] + +🚧 In Progress: +- 🔄 [Story în lucru] +- ⏳ [Task în progress] + +⚠️ Blockers: +- 🚨 [Blocker 1] +- 🔍 [Issue necesitând decizie] + +📅 Next Up: +- 📝 [Next story ready] +- 🔜 [Upcoming dependency] + +🎯 Phase Target: [Data target] | Risk: [LOW/MED/HIGH] +``` + +### Weekly Sprint Review: +- Retrospectivă story-uri complete vs planificate +- Analiza blockers întâlniți și soluții +- Ajustări planning pentru săptămâna următoare +- Identificare lesson learned + +--- + +## 🚨 Risk Management + +### Categorii Risc: +- **HIGH:** Blockers care afectează multiple story-uri +- **MEDIUM:** Delay-uri care pot afecta phase target +- **LOW:** Issues locale care nu afectează planning-ul + +### Escalation Matrix: +1. **Technical Issues:** Propui soluții alternative/workaround +2. **Dependency Blockers:** Replanifici priority și sequence +3. **Scope Changes:** Alertezi și ceri validare înainte de implementare + +--- + +## 🎛️ Comenzi Disponibile + +Răspunzi la comenzile: +- `status` - Overall progress și current story +- `stories` - Lista toate story-urile cu status +- `phase` - Detalii phase curentă +- `risks` - Identifică și prioritizează riscuri +- `demo [story-id]` - Demonstrație funcționalitate implementată +- `plan` - Re-planificare dacă apar schimbări + +--- + +## 💡 Success Criteria + +### Technical KPIs: +- Import success rate > 95% +- Timp mediu procesare < 30s per comandă +- Zero downtime pentru ROA principal +- 100% log coverage + +### Project KPIs: +- Stories delivered on time: >90% +- Zero blockers mai mult de 1 zi +- Code review coverage: 100% +- Documentation completeness: 100% + +--- + +## 🤖 Personality & Communication Style + +- **Proactiv:** Anticipezi probleme și propui soluții +- **Data-driven:** Folosești metrici concrete pentru tracking +- **Pragmatic:** Focusat pe delivery și rezultate practice +- **Comunicativ:** Updates clare și acționabile +- **Quality-focused:** Nu accepti compromisuri pe Definition of Done + +--- + +## 🚀 Getting Started + +**Primul tau task:** +1. Citește întregul PRD furnizat +2. Generează toate story-urile pentru Phase 1 +3. Prezintă overall project plan cu timeline +4. Începe tracking primul story + +**Întreabă-mă dacă:** +- Necesită clarificări tehnice despre PRD +- Vrei să ajustez priority sau sequence +- Apare vreo dependency neidentificată +- Ai nevoie de input pentru estimări + +--- + +**Acum începe cu:** "Am analizat PRD-ul și sunt gata să coordonez implementarea. Să încep cu generarea story-urilor pentru Phase 1?" \ No newline at end of file diff --git a/docs/PRD.md b/docs/PRD.md new file mode 100644 index 0000000..0466e3b --- /dev/null +++ b/docs/PRD.md @@ -0,0 +1,319 @@ +# Product Requirements Document (PRD) +## Import Comenzi Web → Sistem ROA + +**Versiune:** 1.1 +**Data:** 08 septembrie 2025 +**Status:** Phase 1 - în progres (P1-001 ✅ complet) + +--- + +## 📋 Overview + +Sistem ultra-minimal pentru importul comenzilor de pe platforme web (GoMag, etc.) în sistemul ERP ROA Oracle. Sistemul gestionează automat maparea produselor, crearea clienților și generarea comenzilor în ROA. + +### Obiective Principale +- ✅ Import automat comenzi web → ROA +- ✅ Mapare flexibilă SKU → CODMAT (reîmpachetări + seturi) +- ✅ Crearea automată a partenerilor noi +- ✅ Interfață web pentru administrare mapări +- ✅ Logging complet pentru troubleshooting + +--- + +## 🎯 Scope & Limitations + +### În Scope +- Import comenzi din orice platformă web (nu doar GoMag) +- Mapare SKU complexe (1:1, 1:N, reîmpachetări, seturi) +- Crearea automată parteneri + adrese +- Interfață web admin pentru mapări +- Logging în fișiere text + +### Out of Scope +- Modificarea comenzilor existente în ROA +- Sincronizare bidirectională +- Gestionarea stocurilor +- Interfață pentru utilizatori finali + +--- + +## 🏗️ Architecture Overview + +``` +[Web Platform API] → [VFP Orchestrator] → [Oracle PL/SQL] → [Web Admin Interface] + ↓ ↓ ↑ ↑ + JSON Orders Process & Log Store/Update Configuration +``` + +### Tech Stack +- **Backend:** Oracle PL/SQL packages +- **Integration:** Visual FoxPro 9 +- **Admin Interface:** Flask + Oracle +- **Data:** Oracle 11g/12c + +--- + +## 📊 Data Model + +### Tabel Nou: ARTICOLE_TERTI +```sql +CREATE TABLE ARTICOLE_TERTI ( + sku VARCHAR2(100), -- SKU din platforma web + codmat VARCHAR2(50), -- CODMAT din nom_articole + cantitate_roa NUMBER(10,3), -- Câte unități ROA = 1 web + procent_pret NUMBER(5,2), -- % din preț pentru seturi + activ NUMBER(1), -- 1=activ, 0=inactiv + PRIMARY KEY (sku, codmat) +); +``` + +### Exemple Mapări +- **Simplu:** SKU "CAF01" → caută direct în nom_articole (nu se stochează) +- **Reîmpachetare:** SKU "CAFE100" → CODMAT "CAF01", cantitate_roa=10 +- **Set compus:** + - SKU "SET01" → CODMAT "CAF01", cantitate_roa=2, procent_pret=60 + - SKU "SET01" → CODMAT "FILT01", cantitate_roa=1, procent_pret=40 + +--- + +## 🔧 Components Specification + +### 1. Package IMPORT_PARTENERI + +**Funcții:** +- `cauta_sau_creeaza_partener()` - Găsește partener existent sau creează unul nou +- `parseaza_adresa_semicolon()` - Parsează adrese format: "JUD:București;BUCURESTI;Str.Victoriei;10" + +**Logica Căutare Parteneri:** +1. Caută după cod_fiscal (dacă > 3 caractere) +2. Caută după denumire exactă +3. Creează partener nou folosind `pack_def.adauga_partener()` +4. Adaugă adresa folosind `pack_def.adauga_adresa_partener2()` + +### 2. Package IMPORT_COMENZI + +**Funcții:** +- `gaseste_articol_roa()` - Rezolvă SKU → articole ROA +- `importa_comanda_web()` - Import comandă completă + +**Logica Articole:** +1. Verifică ARTICOLE_TERTI pentru SKU +2. Dacă nu există → caută direct în nom_articole (SKU = CODMAT) +3. Calculează cantități și prețuri conform mapărilor +4. Folosește `PACK_COMENZI.adauga_comanda()` și `PACK_COMENZI.adauga_articol_comanda()` + +### 3. VFP Orchestrator (sync-comenzi-web.prg) + +**Responsabilități:** +- Rulare automată (timer 5 minute) +- Citire comenzi din API-ul web +- Apelare package-uri Oracle +- Logging în fișiere text cu timestamp + +### 4. Web Admin Interface + +**Funcționalități:** +- Vizualizare mapări SKU existente +- Adăugare/editare/ștergere mapări +- Validare date înainte de salvare +- Interface responsive cu Flask + +--- + +## 📋 Implementation Phases + +### Phase 1: Database Foundation (Ziua 1) - 🔄 În Progres +- [x] ✅ **P1-001:** Creare tabel ARTICOLE_TERTI + Docker setup +- [ ] 🔄 **P1-002:** Package IMPORT_PARTENERI complet +- [ ] ⏳ **P1-003:** Package IMPORT_COMENZI complet +- [ ] ⏳ **P1-004:** Testare manuală package-uri + +### Phase 2: VFP Integration (Ziua 2) +- [ ] Adaptare gomag-vending-test.prg pentru output JSON +- [ ] Creare sync-comenzi-web.prg +- [ ] Testare import comenzi end-to-end +- [ ] Configurare logging + +### Phase 3: Web Admin Interface (Ziua 3) +- [ ] Flask app cu connection pool Oracle +- [ ] HTML/CSS pentru admin mapări +- [ ] JavaScript pentru CRUD operații +- [ ] Testare interfață web + +### Phase 4: Testing & Deployment (Ziua 4) +- [ ] Testare integrată pe comenzi reale +- [ ] Validare mapări complexe (seturi) +- [ ] Configurare environment production +- [ ] Documentație utilizare + +--- + +## 📁 File Structure + +``` +/api/ # ✅ Flask Admin Interface + ├── admin.py # ✅ Flask app cu Oracle pool + ├── 01_create_table.sql # ✅ Tabel ARTICOLE_TERTI + ├── 02_import_parteneri.sql # 🔄 Package parteneri (în progres) + ├── 03_import_comenzi.sql # ⏳ Package comenzi (planificat) + ├── Dockerfile # ✅ Container cu Oracle client + ├── tnsnames.ora # ✅ Config Oracle ROA + ├── .env # ✅ Environment variables + └── requirements.txt # ✅ Dependencies Python + +/vfp/ # ⏳ VFP Integration (Phase 2) + └── sync-comenzi-web.prg # ⏳ Orchestrator principal + +/docker-compose.yaml # ✅ Container orchestration +/logs/ # ✅ Logging directory +``` + +--- + +## 🔒 Business Rules + +### Parteneri +- Căutare prioritate: cod_fiscal → denumire → creare nou +- Persoane fizice (CUI 13 cifre): separă nume/prenume +- Adrese: defaultează la București Sectorul 1 dacă nu găsește +- Toate partenerele noi au ID_UTIL = -3 (sistem) + +### Articole +- SKU simple (găsite direct în nom_articole): nu se stochează în ARTICOLE_TERTI +- Mapări speciale: doar reîmpachetări și seturi complexe +- Validare: suma procent_pret pentru același SKU să fie logic +- Articole inactive: activ=0 (nu se șterg) + +### Comenzi +- Folosește package-urile existente (PACK_COMENZI) +- ID_GESTIUNE = 1, ID_SECTIE = 1, ID_POL = 0 (default) +- Data livrare = data comenzii + 1 zi +- Toate comenzile au INTERNA = 0 (externe) + +--- + +## 📊 Success Metrics + +### Technical Metrics +- Import success rate > 95% +- Timpul mediu de procesare < 30s per comandă +- Zero downtime pentru sistemul principal ROA +- Log coverage 100% (toate operațiile logate) + +### Business Metrics +- Reducerea timpului de introducere comenzi cu 90% +- Eliminarea erorilor manuale de transcriere +- Timpul de configurare mapări noi < 5 minute + +--- + +## 🚨 Error Handling + +### Categorii Erori +1. **Erori conexiune Oracle:** Retry logic + alertă +2. **SKU not found:** Log warning + skip articol +3. **Partener invalid:** Tentativă creare + log detalii +4. **Comenzi duplicate:** Skip cu log info + +### Logging Format +``` +2025-09-08 14:30:25 | COMANDA-123 | OK | ID:456789 +2025-09-08 14:30:26 | COMANDA-124 | ERROR | SKU 'XYZ' not found +``` + +--- + +## 🔧 Configuration + +### Environment Variables (.env) +```env +ORACLE_USER=CONTAFIN_ORACLE +ORACLE_PASSWORD=******** +ORACLE_DSN=ROA_ROMFAST +TNS_ADMIN=/app +INSTANTCLIENTPATH=/opt/oracle/instantclient +``` + +### VFP Configuration +- Timer interval: 300 secunde (5 minute) +- Conexiune Oracle prin goExecutor existent +- Log files: sync_YYYYMMDD.log (rotație zilnică) + +--- + +## 🎛️ Admin Interface Specification + +### Main Screen: SKU Mappings +- Tabel editabil cu coloane: SKU, CODMAT, Cantitate ROA, Procent Preț, Activ +- Inline editing cu auto-save +- Filtrare și căutare +- Export/Import mapări (CSV) +- Validare în timp real + +### Features +- Bulk operations (activare/dezactivare multiple) +- Template mapări pentru tipuri comune +- Preview calcul preț pentru teste +- Audit trail (cine/când a modificat) + +--- + +## 🏁 Definition of Done + +### Per Feature +- [ ] Cod implementat și testat +- [ ] Documentație actualizată +- [ ] Error handling complet +- [ ] Logging implementat +- [ ] Review code efectuat + +### Per Phase +- [ ] Toate feature-urile Phase complete +- [ ] Testare integrată reușită +- [ ] Performance requirements îndeplinite +- [ ] Deployment verificat +- [ ] Sign-off stakeholder + +--- + +## 📞 Support & Maintenance + +### Monitoring +- Log files în /logs/ cu rotație automată +- Alertă email pentru erori critice +- Dashboard cu statistici import (opcional Phase 2) + +### Backup & Recovery +- Mapări ARTICOLE_TERTI incluse în backup-ul zilnic ROA +- Config files versionate în Git +- Procedură rollback pentru package-uri Oracle + +--- + +--- + +## 📊 Progress Status - Phase 1 + +### ✅ P1-001 COMPLET: Tabel ARTICOLE_TERTI +- **Implementat:** 08 septembrie 2025, 22:30 +- **Deliverables:** + - Tabel ARTICOLE_TERTI cu structură completă (PK, validări, indecși) + - Docker environment cu Oracle Instant Client + - Flask admin interface cu test conexiune + - Date test pentru mapări (reîmpachetare + set compus) +- **Files:** `api/01_create_table.sql`, `api/admin.py`, `docker-compose.yaml` +- **Status:** ✅ Ready pentru testare cu ROA (10.0.20.36) + +### 🔄 Următorul: P1-002 Package IMPORT_PARTENERI +- **Funcții de implementat:** + - `cauta_sau_creeaza_partener()` + - `parseaza_adresa_semicolon()` +- **Dependencies:** P1-001 ✅ complet +- **Estimate:** 6-8 ore +- **Risk:** MEDIUM (integrare cu pack_def existent) + +--- + +**Document Owner:** Development Team +**Last Updated:** 08 septembrie 2025, 22:35 +**Next Review:** După P1-002 completion \ No newline at end of file diff --git a/gomag-vending-test.prg b/gomag-vending-test.prg new file mode 100644 index 0000000..c5b6809 --- /dev/null +++ b/gomag-vending-test.prg @@ -0,0 +1,373 @@ +*-- Script Visual FoxPro 9 pentru accesul la GoMag API cu paginare completa +*-- Autor: Claude AI +*-- Data: 26.08.2025 + +*-- 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 +PRIVATE gcAppPath, loJsonData + + + +gcAppPath = ADDBS(JUSTPATH(SYS(16,0))) +SET DEFAULT TO (m.gcAppPath) +lcPath = gcAppPath + 'nfjson;' +SET PATH TO (m.lcPath) ADDITIVE + +SET PROCEDURE TO nfjsonread.prg ADDITIVE + +*-- Configurare API - MODIFICA aceste valori conform documentatiei GoMag +lcApiBaseUrl = "https://api.gomag.ro/api/v1/product/read/json?enabled=1" && URL de baza pentru lista de produse +lcApiKey = "4c5e46df8f6c4f054fe2787de7a13d4a" && Cheia ta API de la GoMag +lcApiShop = "https://www.coffeepoint.ro" && URL-ul magazinului tau (ex: http://yourdomain.gomag.ro) +lcUserAgent = "Mozilla/5.0" && User-Agent diferit de PostmanRuntime conform documentatiei +lcContentType = "application/json" +lnLimit = 100 && Numarul maxim de produse per pagina (1-100) +lnCurrentPage = 1 && Pagina de start +llHasMorePages = .T. && Flag pentru paginare +loAllJsonData = NULL && Obiect pentru toate datele + +*-- Verificare daca avem WinHttp disponibil +TRY + loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") +CATCH TO loError + ? "Eroare la crearea obiectului WinHttp: " + loError.Message + RETURN .F. +ENDTRY + +*-- 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) + + ? "Preluare pagina " + TRANSFORM(lnCurrentPage) + "..." + + *-- Configurare request + TRY + *-- Initializare request GET + loHttp.Open("GET", lcApiUrl, .F.) + + *-- Setare headers conform documentatiei GoMag + loHttp.SetRequestHeader("User-Agent", lcUserAgent) + loHttp.SetRequestHeader("Content-Type", lcContentType) + loHttp.SetRequestHeader("Accept", "application/json") + loHttp.SetRequestHeader("Apikey", lcApiKey) && Header pentru API Key + loHttp.SetRequestHeader("ApiShop", lcApiShop) && Header pentru shop URL + + *-- Setari timeout + loHttp.SetTimeouts(30000, 30000, 30000, 30000) && 30 secunde pentru fiecare + + *-- Trimitere request + loHttp.Send() + + *-- Verificare status code + lnStatusCode = loHttp.Status + lcStatusText = loHttp.StatusText + + IF lnStatusCode = 200 + *-- Success - preluare raspuns + lcResponse = loHttp.ResponseText + + *-- Parsare JSON cu nfjson + SET PATH TO nfjson ADDITIVE + loJsonData = nfJsonRead(lcResponse) + + IF !ISNULL(loJsonData) + *-- Prima pagina - setam informatiile generale + IF lnCurrentPage = 1 + IF TYPE('loJsonData.total') = 'C' OR TYPE('loJsonData.total') = 'N' + loAllJsonData.total = VAL(TRANSFORM(loJsonData.total)) + ENDIF + IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' + loAllJsonData.pages = VAL(TRANSFORM(loJsonData.pages)) + ENDIF + ? "Total produse: " + TRANSFORM(loAllJsonData.total) + ? "Total pagini: " + TRANSFORM(loAllJsonData.pages) + ENDIF + + *-- Adaugare produse din pagina curenta + IF TYPE('loJsonData.products') = 'O' + DO MergeProducts WITH loAllJsonData, loJsonData + ENDIF + + *-- Verificare daca mai sunt pagini + IF TYPE('loJsonData.pages') = 'C' OR TYPE('loJsonData.pages') = 'N' + lnTotalPages = VAL(TRANSFORM(loJsonData.pages)) + IF lnCurrentPage >= lnTotalPages + llHasMorePages = .F. + ENDIF + ELSE + *-- Daca nu avem info despre pagini, verificam daca sunt produse + IF TYPE('loJsonData.products') != 'O' + llHasMorePages = .F. + ENDIF + ENDIF + + lnCurrentPage = lnCurrentPage + 1 + + ELSE + *-- 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 + +*-- Creare fisier CSV cu toate produsele +IF !ISNULL(loAllJsonData) AND TYPE('loAllJsonData.products') = 'O' + lcCsvFileName = "gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".csv" + DO CreateCsvFromJson WITH loAllJsonData, lcCsvFileName + ? "Fisier CSV creat: " + lcCsvFileName + + *-- Salvare si a datelor JSON complete + lcJsonFileName = "gomag_all_products_" + DTOS(DATE()) + "_" + STRTRAN(TIME(), ":", "") + ".json" + DO SaveCompleteJson WITH loAllJsonData, lcJsonFileName + ? "Fisier JSON complet creat: " + lcJsonFileName +ENDIF + +*-- Curatare +loHttp = NULL + +*-- Functie pentru unirea produselor din toate paginile +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 salvarea datelor JSON complete +PROCEDURE SaveCompleteJson +PARAMETERS tloJsonData, tcFileName + +LOCAL lcJsonContent + +*-- Construieste JSON simplu pentru salvare +lcJsonContent = '{' + CHR(13) + CHR(10) +lcJsonContent = lcJsonContent + ' "total": ' + TRANSFORM(tloJsonData.total) + ',' + CHR(13) + CHR(10) +lcJsonContent = lcJsonContent + ' "pages": ' + TRANSFORM(tloJsonData.pages) + ',' + CHR(13) + CHR(10) +lcJsonContent = lcJsonContent + ' "products": {' + CHR(13) + CHR(10) + +*-- Adauga produsele (versiune simplificata) +LOCAL lnPropCount, lnIndex, lcPropName, loProduct +lnPropCount = AMEMBERS(laProducts, tloJsonData.products, 0) + +FOR lnIndex = 1 TO lnPropCount + lcPropName = laProducts(lnIndex) + loProduct = EVALUATE('tloJsonData.products.' + lcPropName) + + IF TYPE('loProduct') = 'O' + lcJsonContent = lcJsonContent + ' "' + lcPropName + '": {' + + IF TYPE('loProduct.id') = 'C' + lcJsonContent = lcJsonContent + '"id": "' + loProduct.id + '",' + ENDIF + IF TYPE('loProduct.sku') = 'C' + lcJsonContent = lcJsonContent + '"sku": "' + loProduct.sku + '",' + ENDIF + IF TYPE('loProduct.name') = 'C' + lcJsonContent = lcJsonContent + '"name": "' + STRTRAN(loProduct.name, '"', '\"') + '",' + ENDIF + + *-- Elimina ultima virgula + IF RIGHT(lcJsonContent, 1) = ',' + lcJsonContent = LEFT(lcJsonContent, LEN(lcJsonContent) - 1) + ENDIF + + lcJsonContent = lcJsonContent + '}' + + IF lnIndex < lnPropCount + lcJsonContent = lcJsonContent + ',' + ENDIF + + lcJsonContent = lcJsonContent + CHR(13) + CHR(10) + ENDIF +ENDFOR + +lcJsonContent = lcJsonContent + ' }' + CHR(13) + CHR(10) +lcJsonContent = lcJsonContent + '}' + CHR(13) + CHR(10) + +STRTOFILE(lcJsonContent, tcFileName) + +ENDPROC + +*-- Functie pentru crearea fisierului CSV din datele JSON +PROCEDURE CreateCsvFromJson +PARAMETERS tloJsonData, tcCsvFileName + +LOCAL lcCsvContent, lcCsvHeader, lcCsvRow +LOCAL lnProductCount, lnIndex +LOCAL loProduct + +lcCsvContent = "" +lcCsvHeader = "ID,SKU,Name,Brand,Weight,Stock,Base_Price,Price,VAT_Included,Enabled,VAT,Currency,Ecotax" + CHR(13) + CHR(10) +lcCsvContent = lcCsvHeader + +*-- Verifica daca avem produse in raspuns +IF TYPE('tloJsonData.products') = 'O' + *-- Itereaza prin toate produsele + lnPropCount = AMEMBERS(laProducts, tloJsonData.products, 0) + + ? "Procesare " + TRANSFORM(lnPropCount) + " produse pentru CSV..." + + FOR lnIndex = 1 TO lnPropCount + lcPropName = laProducts(lnIndex) + loProduct = EVALUATE('tloJsonData.products.' + lcPropName) + + IF TYPE('loProduct') = 'O' + *-- Extrage datele produsului + lcCsvRow = ; + IIF(TYPE('loProduct.id')='C', STRTRAN(loProduct.id, ',', ';'), '') + ',' +; + IIF(TYPE('loProduct.sku')='C', STRTRAN(loProduct.sku, ',', ';'), '') + ',' +; + IIF(TYPE('loProduct.name')='C', '"' + STRTRAN(STRTRAN(loProduct.name, '"', '""'), ',', ';') + '"', '') + ',' +; + IIF(TYPE('loProduct.brand')='C', STRTRAN(loProduct.brand, ',', ';'), '') + ',' +; + IIF(TYPE('loProduct.weight')='C', loProduct.weight, IIF(TYPE('loProduct.weight')='N', TRANSFORM(loProduct.weight), '')) + ',' +; + IIF(TYPE('loProduct.stock')='C', loProduct.stock, IIF(TYPE('loProduct.stock')='N', TRANSFORM(loProduct.stock), '')) + ',' +; + IIF(TYPE('loProduct.base_price')='C', loProduct.base_price, IIF(TYPE('loProduct.base_price')='N', TRANSFORM(loProduct.base_price), '')) + ',' +; + IIF(TYPE('loProduct.price')='C', loProduct.price, IIF(TYPE('loProduct.price')='N', TRANSFORM(loProduct.price), '')) + ',' +; + IIF(TYPE('loProduct.vat_included')='C', loProduct.vat_included, IIF(TYPE('loProduct.vat_included')='N', TRANSFORM(loProduct.vat_included), '')) + ',' +; + IIF(TYPE('loProduct.enabled')='C', loProduct.enabled, IIF(TYPE('loProduct.enabled')='N', TRANSFORM(loProduct.enabled), '')) + ',' +; + IIF(TYPE('loProduct.vat')='C', loProduct.vat, IIF(TYPE('loProduct.vat')='N', TRANSFORM(loProduct.vat), '')) + ',' +; + IIF(TYPE('loProduct.currency')='C', loProduct.currency, '') + ',' +; + IIF(TYPE('loProduct.ecotax')='C', loProduct.ecotax, IIF(TYPE('loProduct.ecotax')='N', TRANSFORM(loProduct.ecotax), '')) +; + CHR(13) + CHR(10) + + lcCsvContent = lcCsvContent + lcCsvRow + ENDIF + ENDFOR +ENDIF + +*-- Salvare fisier CSV +STRTOFILE(lcCsvContent, tcCsvFileName) +? "CSV salvat cu " + TRANSFORM(lnPropCount) + " produse" + +ENDPROC + +*-- Functii helper pentru testare (optionale) + +*-- Test conectivitate internet +FUNCTION TestConnectivity +LOCAL loHttp, llResult + +llResult = .T. + +TRY + loHttp = CREATEOBJECT("WinHttp.WinHttpRequest.5.1") + loHttp.Open("GET", "https://www.google.com", .F.) + loHttp.SetTimeouts(5000, 5000, 5000, 5000) + loHttp.Send() + + IF loHttp.Status != 200 + llResult = .F. + ENDIF + +CATCH + llResult = .F. +ENDTRY + +loHttp = NULL +RETURN llResult + +ENDFUNC + +*-- Functie pentru codificare URL +FUNCTION UrlEncode +PARAMETERS tcString + +LOCAL lcResult, lcChar, lnI + +lcResult = "" + +FOR lnI = 1 TO LEN(tcString) + lcChar = SUBSTR(tcString, lnI, 1) + + DO CASE + CASE ISALPHA(lcChar) OR ISDIGIT(lcChar) OR INLIST(lcChar, "-", "_", ".", "~") + lcResult = lcResult + lcChar + OTHERWISE + lcResult = lcResult + "%" + RIGHT("0" + TRANSFORM(ASC(lcChar), "@0"), 2) + ENDCASE +ENDFOR + +RETURN lcResult + +ENDFUNC + +*-- Scriptul cu paginare completa pentru preluarea tuturor produselor +*-- Caracteristici principale: +*-- - Paginare automata pentru toate produsele (100 per pagina) +*-- - Pauze intre cereri pentru respectarea rate limiting +*-- - Creare fisier CSV cu toate produsele +*-- - Salvare fisier JSON complet cu toate datele +*-- - Logging separat pentru fiecare pagina in caz de eroare +*-- - Afisare progres in timpul executiei + +*-- INSTRUCTIUNI DE UTILIZARE: +*-- 1. Modifica lcApiKey cu cheia ta API de la GoMag +*-- 2. Modifica lcApiShop cu URL-ul magazinului tau +*-- 3. Ruleaza scriptul - va prelua automat toate produsele +*-- 4. Verifica fisierele generate: CSV si JSON cu toate produsele + +*-- Script completat cu paginare - verificati fisierele generate \ No newline at end of file diff --git a/nfjson/nfjsonread.FXP b/nfjson/nfjsonread.FXP new file mode 100644 index 0000000..e74ccdc Binary files /dev/null and b/nfjson/nfjsonread.FXP differ