- Oracle PL/SQL: kit pricing logic with Mode A (distributed discount) and
Mode B (separate discount line), dual policy support, PRETURI_CU_TVA flag
- Eliminate procent_pret from entire stack (Oracle, Python, JS, HTML)
- New settings: kit_pricing_mode, kit_discount_codmat, price_sync_enabled
- Settings UI: cards for Kit Pricing and Price Sync configuration
- Mappings UI: kit badges with lazy-loaded component prices from price list
- Price sync from orders: auto-update ROA prices when web prices differ
- Catalog price sync: new service to sync all GoMag product prices to ROA
- Kit component price validation: pre-check prices before import
- New endpoint GET /api/mappings/{sku}/prices for component price display
- New endpoints POST /api/price-sync/start, GET status, GET history
- DDL script 07_drop_procent_pret.sql (run after deploy confirmation)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
178 lines
6.0 KiB
Python
178 lines
6.0 KiB
Python
"""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,
|
|
api_key: str = None,
|
|
api_shop: str = None,
|
|
limit: 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.
|
|
Optional api_key, api_shop, limit override config.settings values.
|
|
"""
|
|
def _log(msg: str):
|
|
logger.info(msg)
|
|
if log_fn:
|
|
log_fn(msg)
|
|
|
|
effective_key = api_key or settings.GOMAG_API_KEY
|
|
effective_shop = api_shop or settings.GOMAG_API_SHOP
|
|
effective_limit = limit or settings.GOMAG_LIMIT
|
|
|
|
if not effective_key or not effective_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)
|
|
|
|
# Clean old JSON files before downloading new ones
|
|
old_files = list(out_dir.glob("gomag_orders*.json"))
|
|
if old_files:
|
|
for f in old_files:
|
|
f.unlink()
|
|
_log(f"Șterse {len(old_files)} fișiere JSON vechi")
|
|
|
|
headers = {
|
|
"Apikey": effective_key,
|
|
"ApiShop": effective_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": effective_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}
|
|
|
|
|
|
async def download_products(
|
|
api_key: str = None,
|
|
api_shop: str = None,
|
|
products_url: str = None,
|
|
log_fn: Callable[[str], None] = None,
|
|
) -> list[dict]:
|
|
"""Download all products from GoMag Products API.
|
|
Returns list of product dicts with: sku, price, vat, vat_included, bundleItems.
|
|
"""
|
|
def _log(msg: str):
|
|
logger.info(msg)
|
|
if log_fn:
|
|
log_fn(msg)
|
|
|
|
effective_key = api_key or settings.GOMAG_API_KEY
|
|
effective_shop = api_shop or settings.GOMAG_API_SHOP
|
|
default_url = "https://api.gomag.ro/api/v1/product/read/json"
|
|
effective_url = products_url or default_url
|
|
|
|
if not effective_key or not effective_shop:
|
|
_log("GoMag API keys neconfigurați, skip product download")
|
|
return []
|
|
|
|
headers = {
|
|
"Apikey": effective_key,
|
|
"ApiShop": effective_shop,
|
|
"User-Agent": "Mozilla/5.0",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
all_products = []
|
|
total_pages = 1
|
|
|
|
async with httpx.AsyncClient(timeout=30) as client:
|
|
page = 1
|
|
while page <= total_pages:
|
|
params = {"page": page, "limit": 100}
|
|
try:
|
|
response = await client.get(effective_url, headers=headers, params=params)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
except httpx.HTTPError as e:
|
|
_log(f"GoMag Products API eroare pagina {page}: {e}")
|
|
break
|
|
except Exception as e:
|
|
_log(f"GoMag Products eroare neașteptată pagina {page}: {e}")
|
|
break
|
|
|
|
if page == 1:
|
|
total_pages = int(data.get("pages", 1))
|
|
_log(f"GoMag Products: {data.get('total', '?')} produse în {total_pages} pagini")
|
|
|
|
products = data.get("products", [])
|
|
if isinstance(products, dict):
|
|
products = [products]
|
|
if isinstance(products, list):
|
|
for p in products:
|
|
if isinstance(p, dict) and p.get("sku"):
|
|
all_products.append({
|
|
"sku": p["sku"],
|
|
"price": p.get("price", "0"),
|
|
"vat": p.get("vat", "19"),
|
|
"vat_included": p.get("vat_included", "1"),
|
|
"bundleItems": p.get("bundleItems", []),
|
|
})
|
|
|
|
page += 1
|
|
if page <= total_pages:
|
|
await asyncio.sleep(1)
|
|
|
|
_log(f"GoMag Products: {len(all_products)} produse cu SKU descărcate")
|
|
return all_products
|