diff --git a/api/.env.example b/api/.env.example index eedbf8d..82da44e 100644 --- a/api/.env.example +++ b/api/.env.example @@ -59,6 +59,15 @@ ID_GESTIUNE=0 # Sectie implicita ID_SECTIE=6 +# ============================================================================= +# GoMag API +# ============================================================================= + +GOMAG_API_KEY=your_api_key_here +GOMAG_API_SHOP=https://yourstore.gomag.ro +GOMAG_ORDER_DAYS_BACK=7 +GOMAG_LIMIT=100 + # ============================================================================= # SMTP - Notificari email (optional) # ============================================================================= diff --git a/api/app/config.py b/api/app/config.py index 58edc8e..1822843 100644 --- a/api/app/config.py +++ b/api/app/config.py @@ -41,6 +41,13 @@ class Settings(BaseSettings): ID_GESTIUNE: int = 0 ID_SECTIE: int = 0 + # GoMag API + GOMAG_API_KEY: str = "" + GOMAG_API_SHOP: str = "" + GOMAG_ORDER_DAYS_BACK: int = 7 + GOMAG_LIMIT: int = 100 + GOMAG_API_URL: str = "https://api.gomag.ro/api/v1/order/read/json" + @model_validator(mode="after") def resolve_paths(self): """Resolve relative paths against known roots, independent of CWD.""" diff --git a/api/app/services/gomag_client.py b/api/app/services/gomag_client.py new file mode 100644 index 0000000..038435e --- /dev/null +++ b/api/app/services/gomag_client.py @@ -0,0 +1,88 @@ +"""GoMag API client - downloads orders and saves them as JSON files.""" +import asyncio +import json +import logging +from datetime import datetime, timedelta +from pathlib import Path +from typing import Callable + +import httpx + +from ..config import settings + +logger = logging.getLogger(__name__) + + +async def download_orders( + json_dir: str, + days_back: int = None, + log_fn: Callable[[str], None] = None, +) -> dict: + """Download orders from GoMag API and save as JSON files. + + Returns dict with keys: pages, total, files (list of saved file paths). + If API keys are not configured, returns immediately with empty result. + """ + def _log(msg: str): + logger.info(msg) + if log_fn: + log_fn(msg) + + if not settings.GOMAG_API_KEY or not settings.GOMAG_API_SHOP: + _log("GoMag API keys neconfigurați, skip download") + return {"pages": 0, "total": 0, "files": []} + + if days_back is None: + days_back = settings.GOMAG_ORDER_DAYS_BACK + + start_date = (datetime.now() - timedelta(days=days_back)).strftime("%Y-%m-%d") + out_dir = Path(json_dir) + out_dir.mkdir(parents=True, exist_ok=True) + + headers = { + "Apikey": settings.GOMAG_API_KEY, + "ApiShop": settings.GOMAG_API_SHOP, + "User-Agent": "Mozilla/5.0", + "Content-Type": "application/json", + } + + saved_files = [] + total_orders = 0 + total_pages = 1 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + + async with httpx.AsyncClient(timeout=30) as client: + page = 1 + while page <= total_pages: + params = { + "startDate": start_date, + "page": page, + "limit": settings.GOMAG_LIMIT, + } + try: + response = await client.get(settings.GOMAG_API_URL, headers=headers, params=params) + response.raise_for_status() + data = response.json() + except httpx.HTTPError as e: + _log(f"GoMag API eroare pagina {page}: {e}") + break + except Exception as e: + _log(f"GoMag eroare neașteptată pagina {page}: {e}") + break + + # Update totals from first page response + if page == 1: + total_orders = int(data.get("total", 0)) + total_pages = int(data.get("pages", 1)) + _log(f"GoMag: {total_orders} comenzi în {total_pages} pagini (startDate={start_date})") + + filename = out_dir / f"gomag_orders_page{page}_{timestamp}.json" + filename.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") + saved_files.append(str(filename)) + _log(f"GoMag: pagina {page}/{total_pages} salvată → {filename.name}") + + page += 1 + if page <= total_pages: + await asyncio.sleep(1) + + return {"pages": total_pages, "total": total_orders, "files": saved_files} diff --git a/api/app/services/sync_service.py b/api/app/services/sync_service.py index 5bc52d7..53fa961 100644 --- a/api/app/services/sync_service.py +++ b/api/app/services/sync_service.py @@ -4,7 +4,7 @@ import logging import uuid from datetime import datetime, timedelta -from . import order_reader, validation_service, import_service, sqlite_service, invoice_service +from . import order_reader, validation_service, import_service, sqlite_service, invoice_service, gomag_client from ..config import settings from .. import database @@ -122,9 +122,22 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None f"Inceput: {started_dt.strftime('%d.%m.%Y %H:%M:%S')}", "" ] - _log_line(run_id, "Citire fisiere JSON...") + + json_dir = settings.JSON_OUTPUT_DIR try: + # Phase 0: Download orders from GoMag API + _update_progress("downloading", "Descărcare comenzi din GoMag API...") + _log_line(run_id, "Descărcare comenzi din GoMag API...") + dl_result = await gomag_client.download_orders( + json_dir, log_fn=lambda msg: _log_line(run_id, msg) + ) + if dl_result["files"]: + _log_line(run_id, f"GoMag: {dl_result['total']} comenzi în {dl_result['pages']} pagini → {len(dl_result['files'])} fișiere") + + _update_progress("reading", "Citire fisiere JSON...") + _log_line(run_id, "Citire fisiere JSON...") + # Step 1: Read orders and sort chronologically (oldest first - R3) orders, json_count = order_reader.read_json_orders() orders.sort(key=lambda o: o.date or '') diff --git a/start.sh b/start.sh old mode 100644 new mode 100755 index 0751d1f..14b0b98 --- a/start.sh +++ b/start.sh @@ -28,7 +28,27 @@ fi # Oracle config export TNS_ADMIN="$(pwd)/api" -export LD_LIBRARY_PATH=/opt/oracle/instantclient_21_15:$LD_LIBRARY_PATH + +# Detect Oracle Instant Client path from .env or use default +INSTANTCLIENT_PATH="" +if [ -f "api/.env" ]; then + INSTANTCLIENT_PATH=$(grep -E "^INSTANTCLIENTPATH=" api/.env | cut -d'=' -f2- | tr -d ' ') +fi +# Fallback to default path if not set in .env +if [ -z "$INSTANTCLIENT_PATH" ]; then + INSTANTCLIENT_PATH="/opt/oracle/instantclient_21_15" +fi + +if [ -d "$INSTANTCLIENT_PATH" ]; then + echo "Oracle Instant Client found: $INSTANTCLIENT_PATH (thick mode)" + export LD_LIBRARY_PATH="$INSTANTCLIENT_PATH:$LD_LIBRARY_PATH" +else + echo "WARN: Oracle Instant Client NOT found la: $INSTANTCLIENT_PATH" + echo " Se va folosi thin mode (Oracle 12.1+ necesar)." + echo " Pentru thick mode: instaleaza instantclient sau seteaza INSTANTCLIENTPATH in api/.env" + # Force thin mode so app doesn't try to load missing libraries + export FORCE_THIN_MODE=true +fi cd api echo "Starting GoMag Import Manager on http://0.0.0.0:5003"