Claude Agent 419464a62c feat: add CI/CD testing infrastructure with test.sh orchestrator
Complete testing system: pyproject.toml (pytest markers), test.sh
orchestrator with auto app start/stop and colorful summary,
pre-push hook, Gitea Actions workflow.

New QA tests: API health (7 endpoints), responsive (3 viewports),
log monitoring (ERROR/ORA-/Traceback detection), real GoMag sync,
PL/SQL package validation, smoke prod (read-only).

Converted test_app_basic.py and test_integration.py to pytest.
Added pytestmark to all existing tests (unit/e2e/oracle).
E2E conftest upgraded: console error collector, screenshot on
failure, auto-detect live app on :5003.

Usage: ./test.sh ci (30s) | ./test.sh full (2-3min)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:40:25 +00:00
2025-08-26 22:59:58 +03:00
2026-03-05 11:54:29 +02:00
2026-03-16 17:56:09 +00:00

GoMag Vending - Import Comenzi Web → ROA Oracle

System automat de import comenzi din platforma GoMag in sistemul ERP ROA Oracle.

Arhitectura

[GoMag API] → [Python Sync Service] → [Oracle PL/SQL] → [FastAPI Admin]
     ↓                ↓                      ↑                  ↑
 JSON Orders    Download/Parse/Import    Store/Update    Dashboard + Config

Stack Tehnologic

  • API + Admin: FastAPI + Jinja2 + Bootstrap 5.3
  • GoMag Integration: Python (gomag_client.py — download comenzi cu paginare)
  • Sync Orchestrator: Python (sync_service.py — download → parse → validate → import)
  • Database: Oracle PL/SQL packages (IMPORT_PARTENERI, IMPORT_COMENZI) + SQLite (tracking)

Quick Start

Prerequisite

  • Python 3.10+
  • Oracle Instant Client 21.x (optional — suporta si thin mode pentru Oracle 12.1+)

Instalare

pip install -r api/requirements.txt
cp api/.env.example api/.env
# Editeaza api/.env cu datele de conectare Oracle

Pornire server

Important: serverul trebuie pornit din project root, nu din api/:

python -m uvicorn api.app.main:app --host 0.0.0.0 --port 5003

Sau folosind scriptul inclus:

./start.sh

Deschide http://localhost:5003 in browser.

Testare

Test A - Basic (fara Oracle):

python api/test_app_basic.py

Test C - Integrare Oracle:

python api/test_integration.py

Configurare (.env)

Copiaza .env.example si completeaza:

cp api/.env.example api/.env
Variabila Descriere Exemplu
ORACLE_USER User Oracle MARIUSM_AUTO
ORACLE_PASSWORD Parola Oracle secret
ORACLE_DSN TNS alias ROA_CENTRAL
TNS_ADMIN Cale absoluta la tnsnames.ora /mnt/e/.../gomag/api
INSTANTCLIENTPATH Cale Instant Client (thick mode) /opt/oracle/instantclient_21_15
FORCE_THIN_MODE Thin mode fara Instant Client true
SQLITE_DB_PATH Path SQLite (relativ la project root) api/data/import.db
JSON_OUTPUT_DIR Folder JSON-uri descarcate api/data/orders
APP_PORT Port HTTP 5003
ID_POL ID Politica ROA 39
ID_GESTIUNE ID Gestiune ROA 0
ID_SECTIE ID Sectie ROA 6

Nota Oracle mode:

  • Thick mode (Oracle 10g/11g): seteaza INSTANTCLIENTPATH
  • Thin mode (Oracle 12.1+): seteaza FORCE_THIN_MODE=true, sterge INSTANTCLIENTPATH

Structura Proiect

gomag-vending/
├── api/                          # FastAPI Admin + Dashboard
│   ├── app/
│   │   ├── main.py              # Entry point, lifespan, logging
│   │   ├── config.py            # Settings (pydantic-settings + .env)
│   │   ├── database.py          # Oracle pool + SQLite schema + migrari
│   │   ├── routers/             # Endpoint-uri HTTP
│   │   │   ├── health.py        # GET /health
│   │   │   ├── dashboard.py     # GET / (HTML) + /settings (HTML)
│   │   │   ├── mappings.py      # /mappings, /api/mappings
│   │   │   ├── articles.py      # /api/articles/search
│   │   │   ├── validation.py    # /api/validate/*
│   │   │   └── sync.py          # /api/sync/* + /api/dashboard/* + /api/settings
│   │   ├── services/
│   │   │   ├── gomag_client.py      # Download comenzi GoMag API
│   │   │   ├── sync_service.py      # Orchestrare: download→validate→import
│   │   │   ├── import_service.py    # Import comanda in Oracle ROA
│   │   │   ├── mapping_service.py   # CRUD ARTICOLE_TERTI + pct_total
│   │   │   ├── sqlite_service.py    # Tracking runs/orders/missing SKUs
│   │   │   ├── order_reader.py      # Citire gomag_orders_page*.json
│   │   │   ├── validation_service.py
│   │   │   ├── article_service.py
│   │   │   ├── invoice_service.py   # Verificare facturi ROA
│   │   │   └── scheduler_service.py # APScheduler timer
│   │   ├── templates/           # Jinja2 (dashboard, mappings, missing_skus, logs, settings)
│   │   └── static/              # CSS (style.css) + JS (dashboard, logs, mappings, settings, shared)
│   ├── database-scripts/        # Oracle SQL (ARTICOLE_TERTI, packages)
│   ├── data/                    # SQLite DB (import.db) + JSON orders
│   ├── .env                     # Configurare locala (nu in git)
│   ├── .env.example             # Template configurare
│   ├── test_app_basic.py        # Test A - fara Oracle
│   ├── test_integration.py      # Test C - cu Oracle
│   └── requirements.txt
├── logs/                        # Log-uri aplicatie (sync_comenzi_*.log)
├── docs/                        # Documentatie (PRD, stories)
├── screenshots/                 # Before/preview/after pentru UI changes
├── start.sh                     # Script pornire (Linux/WSL)
└── CLAUDE.md                    # Instructiuni pentru AI assistants

Dashboard Features

Sync Panel

  • Start sync manual sau scheduler automat (5/10/30 min)
  • Progress live: "Import 45/80: #CMD-1234 Ion Popescu"
  • Smart polling: 30s idle → 3s cand ruleaza → auto-refresh tabela
  • Last sync clickabil → jurnal detaliat

Comenzi

  • Filtru perioada: 3z / 7z / 30z / 3 luni / toate / custom
  • Status pills cu conturi totale pe perioada (nu per-pagina)
  • Cautare integrata in bara de filtre
  • Coloana Client cu tooltip cand persoana livrare ≠ facturare
  • Paginare sus + jos, selector rezultate per pagina (25/50/100/250)

Mapari SKU

  • Badge ✓ 100% / ⚠ 80% per grup SKU
  • Filtru Complete / Incomplete
  • Verificare duplicat SKU-CODMAT (409 cu optiune de restaurare)

SKU-uri Lipsa

  • Cautare dupa SKU sau nume produs
  • Filtru Nerezolvate / Rezolvate / Toate cu conturi
  • Re-scan cu progress inline si banner rezultat

Fluxul de Import

1. gomag_client.py descarca comenzi GoMag API → JSON files (paginat)
2. order_reader.py parseaza JSON-urile, sorteaza cronologic (cele mai vechi primele)
3. Comenzi anulate (GoMag statusId=7) → separate, sterse din Oracle daca nu au factura
4. validation_service.py valideaza SKU-uri: ARTICOLE_TERTI (mapped) → NOM_ARTICOLE (direct) → missing
5. Verificare existenta in Oracle (COMENZI by date range) → deja importate se sar
6. Stale error recovery: comenzi ERROR reverificate in Oracle (crash recovery)
7. Validare preturi + dual policy: articole rutate la id_pol_vanzare sau id_pol_productie
8. import_service.py: cauta/creeaza partener → adrese → importa comanda in Oracle
9. Invoice cache: verifica facturi + comenzi sterse din ROA
10. Rezultate salvate in SQLite (orders, sync_run_orders, order_items)

Statuses Comenzi

Status Descriere
IMPORTED Importata nou in ROA in acest run
ALREADY_IMPORTED Existenta deja in Oracle, contorizata
SKIPPED SKU-uri lipsa → neimportata
ERROR Eroare la import (reverificate automat la urmatorul sync)
CANCELLED Comanda anulata in GoMag (statusId=7)
DELETED_IN_ROA A fost importata dar comanda a fost stearsa din ROA

Regula upsert: daca statusul existent este IMPORTED, nu se suprascrie cu ALREADY_IMPORTED.

Reguli Business

Parteneri & Adrese:

  • Prioritate partener: daca exista companie in GoMag (billing.company_name) → firma (PJ, cod_fiscal + registru). Altfel → persoana fizica, cu shipping name ca nume partener
  • Adresa livrare: intotdeauna din GoMag shipping
  • Adresa facturare: daca shipping name ≠ billing name → adresa shipping pt ambele; daca aceeasi persoana → adresa billing din GoMag
  • Cautare partener in Oracle: cod_fiscal → denumire → create new (ID_UTIL = -3)

Articole & Mapari:

  • SKU lookup: ARTICOLE_TERTI (mapped, activ=1) are prioritate fata de NOM_ARTICOLE (direct)
  • SKU simplu: gasit direct in NOM_ARTICOLE → nu se stocheaza in ARTICOLE_TERTI
  • SKU cu repackaging: un SKU → CODMAT cu cantitate diferita (cantitate_roa)
  • SKU set complex: un SKU → multiple CODMAT-uri cu procent_pret (trebuie sum = 100%)

Preturi & Discounturi:

  • Dual policy: articolele sunt rutate la id_pol_vanzare sau id_pol_productie pe baza contului contabil (341/345 = productie)
  • Daca pretul lipseste in politica, se insereaza automat pret=0
  • Discount VAT splitting: daca split_discount_vat=1, discountul se repartizeaza proportional pe cotele TVA din comanda

Facturi & Cache

Sincronizari

Sistemul are 3 procese de sincronizare si o setare de refresh UI:

1. Sync Comenzi (Dashboard → scheduler sau buton Sync)

Procesul principal. Importa comenzi din GoMag in Oracle si verifica statusul celor existente.

Pasi:

  1. Descarca comenzile din GoMag API (ultimele N zile, configurat in Setari)
  2. Valideaza SKU-urile fiecarei comenzi:
    • Cauta in ARTICOLE_TERTI (mapari manuale) → apoi in NOM_ARTICOLE (potrivire directa)
    • Daca un SKU nu e gasit nicaieri → comanda e marcata SKIPPED si SKU-ul apare in "SKU-uri lipsa"
  3. Verifica daca comanda exista deja in Oracle → da: ALREADY_IMPORTED, nu: se importa
  4. Comenzi cu status ERROR din run-uri anterioare sunt reverificate in Oracle (crash recovery)
  5. Import in Oracle: cauta/creeaza partener → adrese → comanda
  6. Verificare facturi (la fiecare sync):
    • Comenzi nefacturate → au primit factura in ROA? → salveaza serie/numar/total
    • Comenzi facturate → a fost stearsa factura? → sterge cache
    • Comenzi importate → au fost sterse din ROA? → marcheaza DELETED_IN_ROA

Cand ruleaza:

  • Automat: scheduler configurat din Dashboard (interval: 5 / 10 / 30 min)
  • Manual: buton "Sync" din Dashboard sau POST /api/sync/start
  • Doar facturi: POST /api/dashboard/refresh-invoices (sare pasii 1-5)

Facturarea in ROA nu declanseaza sync — statusul se actualizeaza la urmatorul sync sau refresh manual.

2. Sync Preturi din Comenzi (Setari → on/off)

La fiecare sync comenzi, daca este activat (price_sync_enabled=1), compara preturile din comanda GoMag cu cele din politica de pret Oracle si le actualizeaza daca difera.

Configurat din: Setari → Sincronizare preturi din comenzi

3. Sync Catalog Preturi (Setari → manual sau zilnic)

Sync independent de comenzi. Descarca toate produsele din catalogul GoMag, le potriveste cu articolele Oracle (prin CODMAT/SKU) si actualizeaza preturile in politica de pret.

Configurat din: Setari → Sincronizare Preturi (activare + program)

  • Doar manual: buton "Sincronizeaza acum" din Setari sau POST /api/price-sync/start
  • Zilnic la 03:00 / 06:00: optiune in UI (neimplementat — setarea se salveaza dar scheduler-ul zilnic nu exista inca)

Interval polling dashboard (Setari → Dashboard)

Cat de des verifica interfata web (browser-ul) statusul sync-ului. Valoare in secunde (implicit 5s). Nu afecteaza frecventa sync-ului — e doar refresh-ul UI-ului.

Facturile sunt verificate din Oracle si cached in SQLite (factura_* pe tabelul orders).

Sursa Oracle

SELECT id_comanda, numar_act, serie_act,
       total_fara_tva, total_tva, total_cu_tva,
       TO_CHAR(data_act, 'YYYY-MM-DD')
FROM vanzari
WHERE id_comanda IN (...) AND sters = 0

Populare Cache

  1. Dashboard (GET /api/dashboard/orders) — comenzile fara cache sunt verificate live si cached automat la fiecare request
  2. Detaliu comanda (GET /api/sync/order/{order_number}) — verifica Oracle live daca nu e cached
  3. Refresh manual (POST /api/dashboard/refresh-invoices) — refresh complet pentru toate comenzile

Refresh Complet — /api/dashboard/refresh-invoices

Face trei verificari in Oracle si actualizeaza SQLite:

Verificare Actiune
Comenzi nefacturate → au primit factura? Cached datele facturii
Comenzi facturate → factura a fost stearsa? Sterge cache factura
Toate comenzile importate → comanda stearsa din ROA? Seteaza status DELETED_IN_ROA

Returneaza: { checked, invoices_added, invoices_cleared, orders_deleted }


API Reference — Sync & Comenzi

Sync

Method Path Descriere
POST /api/sync/start Porneste sync in background
POST /api/sync/stop Trimite semnal de stop
GET /api/sync/status Status curent + progres + last_run
GET /api/sync/history Istoric run-uri (paginat)
GET /api/sync/run/{id} Detalii run specific
GET /api/sync/run/{id}/log Log per comanda (JSON)
GET /api/sync/run/{id}/text-log Log text (live din memorie sau reconstruit din SQLite)
GET /api/sync/run/{id}/orders Comenzi run filtrate/paginate
GET /api/sync/order/{number} Detaliu comanda + items + ARTICOLE_TERTI + factura

Dashboard Comenzi

Method Path Descriere
GET /api/dashboard/orders Comenzi cu enrichment factura
POST /api/dashboard/refresh-invoices Force-refresh stare facturi + deleted orders

Parametri /api/dashboard/orders:

  • period_days: 3/7/30/90 sau 0 (toate sau interval custom)
  • period_start, period_end: interval custom (cand period_days=0)
  • status: all / IMPORTED / SKIPPED / ERROR / UNINVOICED / INVOICED
  • search, sort_by, sort_dir, page, per_page

Filtrele UNINVOICED si INVOICED fac fetch din toate comenzile IMPORTED si filtreaza server-side dupa prezenta/absenta cache-ului de factura.

Scheduler

Method Path Descriere
PUT /api/sync/schedule Configureaza (enabled, interval_minutes: 5/10/30)
GET /api/sync/schedule Status curent

Configuratia este persistata in SQLite (scheduler_config).

Settings

Method Path Descriere
GET /api/settings Citeste setari aplicatie
PUT /api/settings Salveaza setari
GET /api/settings/sectii Lista sectii Oracle
GET /api/settings/politici Lista politici preturi Oracle

Setari disponibile: transport_codmat, transport_vat, discount_codmat, discount_vat, transport_id_pol, discount_id_pol, id_pol, id_pol_productie, id_sectie, split_discount_vat, gomag_api_key, gomag_api_shop, gomag_order_days_back, gomag_limit


Deploy Windows

Instalare initiala

# Ruleaza ca Administrator
.\deploy.ps1

Scriptul deploy.ps1 face automat: git clone, venv, dependinte, detectare Oracle, start.bat, serviciu NSSM, configurare IIS reverse proxy.

Update cod (pull + restart)

# Ca Administrator
.\update.ps1

Sau manual:

cd C:\gomag-vending
git pull origin main
nssm restart GoMagVending

Configurare .env pe Windows

# api/.env — exemplu Windows
ORACLE_USER=VENDING
ORACLE_PASSWORD=****
ORACLE_DSN=ROA
TNS_ADMIN=C:\roa\instantclient_11_2_0_2
INSTANTCLIENTPATH=C:\app\Server\product\18.0.0\dbhomeXE\bin
SQLITE_DB_PATH=api/data/import.db
JSON_OUTPUT_DIR=api/data/orders
APP_PORT=5003
ID_POL=39
ID_GESTIUNE=0
ID_SECTIE=6
GOMAG_API_KEY=...
GOMAG_API_SHOP=...
GOMAG_ORDER_DAYS_BACK=7
GOMAG_LIMIT=100

Important:

  • TNS_ADMIN = folderul care contine tnsnames.ora (NU fisierul in sine)
  • ORACLE_DSN = alias-ul exact din tnsnames.ora
  • INSTANTCLIENTPATH = calea catre Oracle bin (thick mode, Oracle 10g/11g)
  • FORCE_THIN_MODE=true = elimina necesitatea Instant Client (Oracle 12.1+)
  • Setarile din .env pot fi suprascrise din UI → Setari → salvate in SQLite

Serviciu Windows (NSSM)

nssm restart GoMagVending    # restart serviciu
nssm status GoMagVending     # status serviciu
nssm stop GoMagVending       # stop serviciu
nssm start GoMagVending      # start serviciu

Loguri serviciu: logs/service_stdout.log, logs/service_stderr.log Loguri aplicatie: logs/sync_comenzi_*.log

Nota: Userul gomag nu are drepturi de admin — nssm restart necesita PowerShell Administrator direct pe server.

Depanare SSH

# Conectare SSH (PowerShell remote, cheie publica)
ssh -p 22122 gomag@79.119.86.134

# Verificare .env
cmd /c type C:\gomag-vending\api\.env

# Test conexiune Oracle
C:\gomag-vending\venv\Scripts\python.exe -c "import oracledb, os; os.environ['TNS_ADMIN']='C:/roa/instantclient_11_2_0_2'; conn=oracledb.connect(user='VENDING', password='ROMFASTSOFT', dsn='ROA'); print('Connected!'); conn.close()"

# Verificare tnsnames.ora
cmd /c type C:\roa\instantclient_11_2_0_2\tnsnames.ora

# Verificare procese Python
Get-Process *python* | Select-Object Id,ProcessName,Path

# Verificare loguri recente
Get-ChildItem C:\gomag-vending\logs\*.log | Sort-Object LastWriteTime -Descending | Select-Object -First 3

# Test sync manual (verifica ca Oracle pool porneste)
curl http://localhost:5003/health
curl -X POST http://localhost:5003/api/sync/start

# Refresh facturi manual
curl -X POST http://localhost:5003/api/dashboard/refresh-invoices

Probleme frecvente

Eroare Cauza Solutie
ORA-12154: TNS:could not resolve TNS_ADMIN gresit sau tnsnames.ora nu contine alias-ul DSN Verifica TNS_ADMIN in .env + alias in tnsnames.ora
ORA-04088: LOGON_AUDIT_TRIGGER + Nu aveti licenta pentru PYTHON Trigger ROA blocheaza executabile nelicențiate Adauga python.exe (calea completa) in ROASUPORT
503 Service Unavailable pe /api/articles/search Oracle pool nu s-a initializat Verifica logul sync_comenzi_*.log pentru eroarea exacta
Facturile nu apar in dashboard Cache SQLite gol — invoice_service nu a putut interoga Oracle Apasa butonul Refresh Facturi din dashboard sau POST /api/dashboard/refresh-invoices
Comanda apare ca DELETED_IN_ROA Comanda a fost stearsa manual din ROA Normal — marcat automat la refresh
Scheduler nu porneste dupa restart Config pierduta Verifica SQLite scheduler_config sau reconfigureaza din UI

WSL2 Note

  • uvicorn --reload nu functioneaza pe /mnt/e/ (WSL2 limitation) — restarta manual
  • Serverul trebuie pornit din project root, nu din api/
  • JSON_OUTPUT_DIR si SQLITE_DB_PATH sunt relative la project root
Description
No description provided
Readme 3.6 MiB
Languages
PLSQL 63.8%
Python 23.4%
JavaScript 6.4%
HTML 2.9%
PowerShell 1.4%
Other 2%