fix: read parent_id field from Evotor API and move VK products between albums on group change
- catalog.py: include parent_id as fallback for group_evotor_id (Evotor API returns parent_id instead of group/parentUuid) - vk_sync.py: on product update, detect album change and call market.removeFromAlbum + market.addToAlbum to move product to the correct album Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,11 +8,11 @@ Beat schedule entry (set in celery_app.py):
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import httpx
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
|
||||||
from web.config import settings
|
from web.config import settings
|
||||||
from web.database import SessionLocal
|
from web.database import SessionLocal
|
||||||
|
import web.lib.api_logger as api_logger
|
||||||
from web.models.connections import CachedGroup, CachedProduct, CachedStore, EvotorConnection, SyncConfig, SyncFilter
|
from web.models.connections import CachedGroup, CachedProduct, CachedStore, EvotorConnection, SyncConfig, SyncFilter
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -34,18 +34,18 @@ def _now() -> datetime:
|
|||||||
|
|
||||||
# ── per-user helpers ──────────────────────────────────────────────────────────
|
# ── per-user helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _fetch_stores(token: str) -> list[dict]:
|
def _fetch_stores(token: str, user_id: int | None = None) -> list[dict]:
|
||||||
r = httpx.get(f"{EVO_API}/stores", headers=_headers(token), timeout=15)
|
r = api_logger.get(f"{EVO_API}/stores", user_id=user_id, headers=_headers(token), timeout=15)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
data = r.json()
|
data = r.json()
|
||||||
return data.get("items", data) if isinstance(data, dict) else data
|
return data.get("items", data) if isinstance(data, dict) else data
|
||||||
|
|
||||||
|
|
||||||
def _fetch_groups(token: str, store_id: str) -> list[dict] | None:
|
def _fetch_groups(token: str, store_id: str, user_id: int | None = None) -> list[dict] | None:
|
||||||
"""Returns None if the store is not accessible (402/403), list otherwise."""
|
"""Returns None if the store is not accessible (402/403), list otherwise."""
|
||||||
r = httpx.get(
|
r = api_logger.get(
|
||||||
f"{EVO_API}/stores/{store_id}/product-groups",
|
f"{EVO_API}/stores/{store_id}/product-groups",
|
||||||
headers=_headers(token), timeout=15,
|
user_id=user_id, headers=_headers(token), timeout=15,
|
||||||
)
|
)
|
||||||
if r.status_code in (402, 403):
|
if r.status_code in (402, 403):
|
||||||
return None
|
return None
|
||||||
@@ -54,11 +54,11 @@ def _fetch_groups(token: str, store_id: str) -> list[dict] | None:
|
|||||||
return data.get("items", data) if isinstance(data, dict) else data
|
return data.get("items", data) if isinstance(data, dict) else data
|
||||||
|
|
||||||
|
|
||||||
def _fetch_products(token: str, store_id: str) -> list[dict] | None:
|
def _fetch_products(token: str, store_id: str, user_id: int | None = None) -> list[dict] | None:
|
||||||
"""Returns None if the store is not accessible (402/403), list otherwise."""
|
"""Returns None if the store is not accessible (402/403), list otherwise."""
|
||||||
r = httpx.get(
|
r = api_logger.get(
|
||||||
f"{EVO_API}/stores/{store_id}/products",
|
f"{EVO_API}/stores/{store_id}/products",
|
||||||
headers=_headers(token), timeout=30,
|
user_id=user_id, headers=_headers(token), timeout=30,
|
||||||
)
|
)
|
||||||
if r.status_code in (402, 403):
|
if r.status_code in (402, 403):
|
||||||
return None
|
return None
|
||||||
@@ -158,7 +158,7 @@ def _sync_user(db, user_id: int, token: str) -> None:
|
|||||||
row = db.query(CachedProduct).filter_by(user_id=user_id, evotor_id=evo_id).first()
|
row = db.query(CachedProduct).filter_by(user_id=user_id, evotor_id=evo_id).first()
|
||||||
if row:
|
if row:
|
||||||
row.store_evotor_id = store_evo_id
|
row.store_evotor_id = store_evo_id
|
||||||
row.group_evotor_id = p.get("group") or p.get("parentUuid")
|
row.group_evotor_id = p.get("group") or p.get("parentUuid") or p.get("parent_id")
|
||||||
row.name = p.get("name", "")
|
row.name = p.get("name", "")
|
||||||
row.price = float(price) if price is not None else None
|
row.price = float(price) if price is not None else None
|
||||||
row.quantity = float(quantity) if quantity is not None else None
|
row.quantity = float(quantity) if quantity is not None else None
|
||||||
@@ -171,7 +171,7 @@ def _sync_user(db, user_id: int, token: str) -> None:
|
|||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
evotor_id=evo_id,
|
evotor_id=evo_id,
|
||||||
store_evotor_id=store_evo_id,
|
store_evotor_id=store_evo_id,
|
||||||
group_evotor_id=p.get("group") or p.get("parentUuid"),
|
group_evotor_id=p.get("group") or p.get("parentUuid") or p.get("parent_id"),
|
||||||
name=p.get("name", ""),
|
name=p.get("name", ""),
|
||||||
price=float(price) if price is not None else None,
|
price=float(price) if price is not None else None,
|
||||||
quantity=float(quantity) if quantity is not None else None,
|
quantity=float(quantity) if quantity is not None else None,
|
||||||
|
|||||||
@@ -183,17 +183,20 @@ def _sync_product(
|
|||||||
vk_p = db.query(VkCachedProduct).filter_by(
|
vk_p = db.query(VkCachedProduct).filter_by(
|
||||||
user_id=user_id, vk_group_id=vk_group_id, vk_product_id=product.vk_product_id,
|
user_id=user_id, vk_group_id=vk_group_id, vk_product_id=product.vk_product_id,
|
||||||
).first()
|
).first()
|
||||||
|
album_changed = False
|
||||||
if vk_p:
|
if vk_p:
|
||||||
vk_price_kopecks = int(vk_p.price * 100) if vk_p.price is not None else 0
|
vk_price_kopecks = int(vk_p.price * 100) if vk_p.price is not None else 0
|
||||||
vk_stock = settings.VK_STOCK_AMOUNT if vk_p.availability == 0 else 0
|
vk_stock = settings.VK_STOCK_AMOUNT if vk_p.availability == 0 else 0
|
||||||
vk_name = vk_p.name or ""
|
vk_name = vk_p.name or ""
|
||||||
vk_desc = (vk_p.description or "").strip()
|
vk_desc = (vk_p.description or "").strip()
|
||||||
curr_desc = desc.strip()
|
curr_desc = desc.strip()
|
||||||
|
album_changed = str(vk_p.album_id) != str(album_id) if album_id else False
|
||||||
changed = (
|
changed = (
|
||||||
name != vk_name
|
name != vk_name
|
||||||
or price_kopecks != vk_price_kopecks
|
or price_kopecks != vk_price_kopecks
|
||||||
or curr_desc != vk_desc
|
or curr_desc != vk_desc
|
||||||
or stock != vk_stock
|
or stock != vk_stock
|
||||||
|
or album_changed
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
changed = True # cached VK product gone, push update
|
changed = True # cached VK product gone, push update
|
||||||
@@ -213,6 +216,24 @@ def _sync_product(
|
|||||||
if "error" in resp:
|
if "error" in resp:
|
||||||
logger.warning("market.edit error product=%s: %s", product.evotor_id, resp["error"])
|
logger.warning("market.edit error product=%s: %s", product.evotor_id, resp["error"])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if album_changed and album_id and vk_p:
|
||||||
|
old_album_id = str(vk_p.album_id)
|
||||||
|
_vk_post("market.removeFromAlbum", {
|
||||||
|
"owner_id": owner_id,
|
||||||
|
"item_ids": product.vk_product_id,
|
||||||
|
"album_ids": old_album_id,
|
||||||
|
}, token)
|
||||||
|
resp_album = _vk_post("market.addToAlbum", {
|
||||||
|
"owner_id": owner_id,
|
||||||
|
"item_ids": product.vk_product_id,
|
||||||
|
"album_ids": album_id,
|
||||||
|
}, token)
|
||||||
|
if "error" in resp_album:
|
||||||
|
logger.warning("market.addToAlbum error product=%s: %s", product.evotor_id, resp_album["error"])
|
||||||
|
else:
|
||||||
|
logger.info("user=%s moved VK product '%s' album %s→%s", user_id, name, old_album_id, album_id)
|
||||||
|
|
||||||
product.synced_at = now
|
product.synced_at = now
|
||||||
logger.info("user=%s updated VK product '%s' id=%s", user_id, name, product.vk_product_id)
|
logger.info("user=%s updated VK product '%s' id=%s", user_id, name, product.vk_product_id)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user