Script failed with "SQLite not initialized" because module-level connection state wasn't set up when invoked standalone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
117 lines
4.1 KiB
Python
117 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""One-shot recovery: re-populate SQLite `order_items` for orders where the
|
|
table was wiped (e.g. DELETED_IN_ROA → retry flow, before the retry items fix).
|
|
|
|
Reads settings from SQLite, downloads orders from GoMag API for a ~14-day window
|
|
around the order date, finds the target order, rebuilds the items rows.
|
|
|
|
Does NOT touch Oracle. Does NOT change order status / id_comanda.
|
|
|
|
Usage (inside the venv, on the prod server):
|
|
python scripts/backfill_order_items.py 485224762
|
|
python scripts/backfill_order_items.py 485224762 485224763 # multiple
|
|
"""
|
|
import asyncio
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
from datetime import datetime, timedelta
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
|
|
from api.app.services import sqlite_service, gomag_client, order_reader, validation_service
|
|
from api.app import database
|
|
|
|
|
|
async def _backfill_one(order_number: str, app_settings: dict, use_oracle: bool) -> dict:
|
|
detail = await sqlite_service.get_order_detail(order_number)
|
|
if not detail:
|
|
return {"ok": False, "msg": f"#{order_number}: nu e in SQLite"}
|
|
|
|
order_data = detail["order"]
|
|
existing_items = len(detail["items"])
|
|
order_date_str = order_data.get("order_date") or ""
|
|
|
|
try:
|
|
order_date = datetime.fromisoformat(order_date_str.replace("Z", "+00:00")).date()
|
|
except (ValueError, AttributeError):
|
|
order_date = datetime.now().date() - timedelta(days=1)
|
|
|
|
days_back = max((datetime.now().date() - order_date).days + 2, 2)
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
await gomag_client.download_orders(
|
|
tmp,
|
|
days_back=days_back,
|
|
api_key=app_settings.get("gomag_api_key"),
|
|
api_shop=app_settings.get("gomag_api_shop"),
|
|
limit=200,
|
|
)
|
|
orders, _ = order_reader.read_json_orders(json_dir=tmp)
|
|
target = next((o for o in orders if str(o.number) == str(order_number)), None)
|
|
|
|
if not target:
|
|
return {"ok": False, "msg": f"#{order_number}: nu e in GoMag (fereastra {days_back}z)"}
|
|
|
|
validation = {"mapped": set(), "direct": set()}
|
|
if use_oracle:
|
|
skus = {item.sku for item in target.items if item.sku}
|
|
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:
|
|
validation = await asyncio.to_thread(
|
|
validation_service.validate_skus, skus, None, id_gestiuni
|
|
)
|
|
except Exception as e:
|
|
print(f" [WARN] validate_skus a esuat, mapping_status default='direct': {e}")
|
|
|
|
items_data = [
|
|
{
|
|
"sku": item.sku, "product_name": item.name,
|
|
"quantity": item.quantity, "price": item.price,
|
|
"baseprice": item.baseprice, "vat": item.vat,
|
|
"mapping_status": "mapped" if item.sku in validation["mapped"] else "direct",
|
|
"codmat": None, "id_articol": None, "cantitate_roa": None,
|
|
}
|
|
for item in target.items
|
|
]
|
|
|
|
await sqlite_service.add_order_items(order_number, items_data)
|
|
return {
|
|
"ok": True,
|
|
"msg": f"#{order_number}: {len(items_data)} items scrise (era {existing_items})",
|
|
}
|
|
|
|
|
|
async def main(order_numbers: list[str]):
|
|
database.init_sqlite()
|
|
app_settings = await sqlite_service.get_app_settings()
|
|
|
|
use_oracle = False
|
|
try:
|
|
database.init_oracle()
|
|
use_oracle = True
|
|
print("Oracle conectat — mapping_status va fi calculat corect.")
|
|
except Exception as e:
|
|
print(f"Oracle indisponibil ({e}) — mapping_status default 'direct'.")
|
|
|
|
try:
|
|
for on in order_numbers:
|
|
result = await _backfill_one(on, app_settings, use_oracle)
|
|
tag = "OK " if result["ok"] else "FAIL"
|
|
print(f"[{tag}] {result['msg']}")
|
|
finally:
|
|
if use_oracle:
|
|
try:
|
|
database.close_oracle()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python scripts/backfill_order_items.py <order_number> [<order_number>...]")
|
|
sys.exit(1)
|
|
|
|
asyncio.run(main(sys.argv[1:]))
|