Claude Agent 91ddb4fbdd fix(mappings): allow SKU=CODMAT mappings for quantity conversion
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>
2026-03-25 22:01:30 +00:00
2026-03-24 12:07:28 +00:00
2026-03-25 19:06:34 +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%