Add ability to re-import individual ERROR/SKIPPED orders directly from
the order detail modal. Downloads narrow date range from GoMag API,
finds the specific order, and re-runs import_single_order().
Backend:
- New retry_service.py with retry_single_order() — downloads order_date
±1 day from GoMag, finds order by number, imports via import_service
- Guard: blocks retry during active sync (_sync_lock check)
- POST /api/orders/{order_number}/retry endpoint
Frontend:
- "Reimporta" button in modal footer (visible only for ERROR/SKIPPED)
- Spinner during retry, success/error feedback with auto-refresh
Cache-bust: shared.js?v=18
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
132 lines
5.1 KiB
Python
132 lines
5.1 KiB
Python
"""Retry service — re-import individual failed/skipped orders."""
|
|
import asyncio
|
|
import logging
|
|
import tempfile
|
|
from datetime import datetime, timedelta
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def retry_single_order(order_number: str, app_settings: dict) -> dict:
|
|
"""Re-download and re-import a single order from GoMag.
|
|
|
|
Steps:
|
|
1. Read order from SQLite to get order_date / customer_name
|
|
2. Check sync lock (no retry during active sync)
|
|
3. Download narrow date range from GoMag (order_date ± 1 day)
|
|
4. Find the specific order in downloaded data
|
|
5. Run import_single_order()
|
|
6. Update status in SQLite
|
|
|
|
Returns: {"success": bool, "message": str, "status": str|None}
|
|
"""
|
|
from . import sqlite_service, sync_service, gomag_client, import_service, order_reader
|
|
|
|
# Check sync lock
|
|
if sync_service._sync_lock.locked():
|
|
return {"success": False, "message": "Sync in curs — asteapta finalizarea"}
|
|
|
|
# Get order from SQLite
|
|
detail = await sqlite_service.get_order_detail(order_number)
|
|
if not detail:
|
|
return {"success": False, "message": "Comanda nu a fost gasita"}
|
|
|
|
order_data = detail["order"]
|
|
status = order_data.get("status", "")
|
|
if status not in ("ERROR", "SKIPPED"):
|
|
return {"success": False, "message": f"Retry permis doar pentru ERROR/SKIPPED (status actual: {status})"}
|
|
|
|
order_date_str = order_data.get("order_date", "")
|
|
customer_name = order_data.get("customer_name", "")
|
|
|
|
# Parse order date for narrow download window
|
|
try:
|
|
order_date = datetime.fromisoformat(order_date_str.replace("Z", "+00:00")).date()
|
|
except (ValueError, AttributeError):
|
|
order_date = datetime.now().date() - timedelta(days=1)
|
|
|
|
gomag_key = app_settings.get("gomag_api_key") or None
|
|
gomag_shop = app_settings.get("gomag_api_shop") or None
|
|
|
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
try:
|
|
today = datetime.now().date()
|
|
days_back = (today - order_date).days + 1
|
|
if days_back < 2:
|
|
days_back = 2
|
|
|
|
await gomag_client.download_orders(
|
|
tmp_dir, days_back=days_back,
|
|
api_key=gomag_key, api_shop=gomag_shop,
|
|
limit=200,
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Retry download failed for {order_number}: {e}")
|
|
return {"success": False, "message": f"Eroare download GoMag: {e}"}
|
|
|
|
# Find the specific order in downloaded data
|
|
target_order = None
|
|
orders, _ = order_reader.read_json_orders(json_dir=tmp_dir)
|
|
for o in orders:
|
|
if str(o.number) == str(order_number):
|
|
target_order = o
|
|
break
|
|
|
|
if not target_order:
|
|
return {"success": False, "message": f"Comanda {order_number} nu a fost gasita in GoMag API"}
|
|
|
|
# Import the order
|
|
id_pol = int(app_settings.get("id_pol") or 0)
|
|
id_sectie = int(app_settings.get("id_sectie") or 0)
|
|
id_gestiune = app_settings.get("id_gestiune", "")
|
|
id_gestiuni = [int(g.strip()) for g in id_gestiune.split(",") if g.strip()] if id_gestiune else None
|
|
|
|
try:
|
|
result = await asyncio.to_thread(
|
|
import_service.import_single_order,
|
|
target_order, id_pol=id_pol, id_sectie=id_sectie,
|
|
app_settings=app_settings, id_gestiuni=id_gestiuni
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Retry import failed for {order_number}: {e}")
|
|
await sqlite_service.upsert_order(
|
|
sync_run_id="retry",
|
|
order_number=order_number,
|
|
order_date=order_date_str,
|
|
customer_name=customer_name,
|
|
status="ERROR",
|
|
error_message=f"Retry failed: {e}",
|
|
)
|
|
return {"success": False, "message": f"Eroare import: {e}"}
|
|
|
|
if result.get("success"):
|
|
await sqlite_service.upsert_order(
|
|
sync_run_id="retry",
|
|
order_number=order_number,
|
|
order_date=order_date_str,
|
|
customer_name=customer_name,
|
|
status="IMPORTED",
|
|
id_comanda=result.get("id_comanda"),
|
|
id_partener=result.get("id_partener"),
|
|
error_message=None,
|
|
)
|
|
if result.get("id_adresa_facturare") or result.get("id_adresa_livrare"):
|
|
await sqlite_service.update_import_order_addresses(
|
|
order_number=order_number,
|
|
id_adresa_facturare=result.get("id_adresa_facturare"),
|
|
id_adresa_livrare=result.get("id_adresa_livrare"),
|
|
)
|
|
logger.info(f"Retry successful for order {order_number} → IMPORTED")
|
|
return {"success": True, "message": "Comanda reimportata cu succes", "status": "IMPORTED"}
|
|
else:
|
|
error = result.get("error", "Unknown error")
|
|
await sqlite_service.upsert_order(
|
|
sync_run_id="retry",
|
|
order_number=order_number,
|
|
order_date=order_date_str,
|
|
customer_name=customer_name,
|
|
status="ERROR",
|
|
error_message=f"Retry: {error}",
|
|
)
|
|
return {"success": False, "message": f"Import esuat: {error}", "status": "ERROR"}
|