Remove validation that blocked creating mappings when SKU matches an existing CODMAT. Users need this for unit quantity conversion (e.g., website sells 50 units per SKU but ROA tracks 100, requiring cantitate_roa=0.5). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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, stergeINSTANTCLIENTPATH
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_vanzaresauid_pol_productiepe 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:
- Descarca comenzile din GoMag API (ultimele N zile, configurat in Setari)
- 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"
- Verifica daca comanda exista deja in Oracle → da: ALREADY_IMPORTED, nu: se importa
- Comenzi cu status ERROR din run-uri anterioare sunt reverificate in Oracle (crash recovery)
- Import in Oracle: cauta/creeaza partener → adrese → comanda
- 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
- Dashboard (
GET /api/dashboard/orders) — comenzile fara cache sunt verificate live si cached automat la fiecare request - Detaliu comanda (
GET /api/sync/order/{order_number}) — verifica Oracle live daca nu e cached - 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 (candperiod_days=0)status:all/IMPORTED/SKIPPED/ERROR/UNINVOICED/INVOICEDsearch,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 continetnsnames.ora(NU fisierul in sine)ORACLE_DSN= alias-ul exact dintnsnames.oraINSTANTCLIENTPATH= calea catre Oracle bin (thick mode, Oracle 10g/11g)FORCE_THIN_MODE=true= elimina necesitatea Instant Client (Oracle 12.1+)- Setarile din
.envpot 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 --reloadnu functioneaza pe/mnt/e/(WSL2 limitation) — restarta manual- Serverul trebuie pornit din project root, nu din
api/ JSON_OUTPUT_DIRsiSQLITE_DB_PATHsunt relative la project root