feat: show Jobs tab for all vendors when no filter selected

- Add /admin/letzshop/jobs API endpoint for all jobs across vendors
- Update list_letzshop_jobs service to support optional vendor_id
- Remove x-if condition from Jobs tab button and panel
- Update JS to use global or vendor-specific endpoint based on selection
- Update jobs table subtitle to show context

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-25 00:30:33 +01:00
parent 50fd1d01c2
commit bedc979b12
5 changed files with 91 additions and 46 deletions

View File

@@ -607,29 +607,37 @@ class LetzshopOrderService:
def list_letzshop_jobs(
self,
vendor_id: int,
vendor_id: int | None = None,
job_type: str | None = None,
status: str | None = None,
skip: int = 0,
limit: int = 20,
) -> tuple[list[dict[str, Any]], int]:
"""
List unified Letzshop-related jobs for a vendor.
List unified Letzshop-related jobs for a vendor or all vendors.
Combines product imports, historical order imports, and order syncs.
If vendor_id is None, returns jobs across all vendors.
"""
jobs = []
# Fetch vendor info once for all jobs
vendor = self.get_vendor(vendor_id)
vendor_name = vendor.name if vendor else None
vendor_code = vendor.vendor_code if vendor else None
# Fetch vendor info - for single vendor or build lookup for all vendors
if vendor_id:
vendor = self.get_vendor(vendor_id)
vendor_lookup = {vendor_id: (vendor.name if vendor else None, vendor.vendor_code if vendor else None)}
else:
# Build lookup for all vendors when showing all jobs
from models.database.vendor import Vendor
vendors = self.db.query(Vendor.id, Vendor.name, Vendor.vendor_code).all()
vendor_lookup = {v.id: (v.name, v.vendor_code) for v in vendors}
# Historical order imports from letzshop_historical_import_jobs
if job_type in (None, "historical_import"):
hist_query = self.db.query(LetzshopHistoricalImportJob).filter(
LetzshopHistoricalImportJob.vendor_id == vendor_id,
)
hist_query = self.db.query(LetzshopHistoricalImportJob)
if vendor_id:
hist_query = hist_query.filter(
LetzshopHistoricalImportJob.vendor_id == vendor_id,
)
if status:
hist_query = hist_query.filter(
LetzshopHistoricalImportJob.status == status
@@ -640,6 +648,7 @@ class LetzshopOrderService:
).all()
for job in hist_jobs:
v_name, v_code = vendor_lookup.get(job.vendor_id, (None, None))
jobs.append(
{
"id": job.id,
@@ -652,9 +661,9 @@ class LetzshopOrderService:
"records_succeeded": (job.orders_imported or 0)
+ (job.orders_updated or 0),
"records_failed": job.orders_skipped or 0,
"vendor_id": vendor_id,
"vendor_name": vendor_name,
"vendor_code": vendor_code,
"vendor_id": job.vendor_id,
"vendor_name": v_name,
"vendor_code": v_code,
"current_phase": job.current_phase,
"error_message": job.error_message,
}
@@ -663,9 +672,12 @@ class LetzshopOrderService:
# Product imports from marketplace_import_jobs
if job_type in (None, "import"):
import_query = self.db.query(MarketplaceImportJob).filter(
MarketplaceImportJob.vendor_id == vendor_id,
MarketplaceImportJob.marketplace == "Letzshop",
)
if vendor_id:
import_query = import_query.filter(
MarketplaceImportJob.vendor_id == vendor_id,
)
if status:
import_query = import_query.filter(
MarketplaceImportJob.status == status
@@ -676,6 +688,7 @@ class LetzshopOrderService:
).all()
for job in import_jobs:
v_name, v_code = vendor_lookup.get(job.vendor_id, (None, None))
jobs.append(
{
"id": job.id,
@@ -688,24 +701,26 @@ class LetzshopOrderService:
"records_succeeded": (job.imported_count or 0)
+ (job.updated_count or 0),
"records_failed": job.error_count or 0,
"vendor_id": vendor_id,
"vendor_name": vendor_name,
"vendor_code": vendor_code,
"vendor_id": job.vendor_id,
"vendor_name": v_name,
"vendor_code": v_code,
}
)
# Order syncs from letzshop_sync_logs
if job_type in (None, "order_sync"):
sync_query = self.db.query(LetzshopSyncLog).filter(
LetzshopSyncLog.vendor_id == vendor_id,
LetzshopSyncLog.operation_type == "order_import",
)
if vendor_id:
sync_query = sync_query.filter(LetzshopSyncLog.vendor_id == vendor_id)
if status:
sync_query = sync_query.filter(LetzshopSyncLog.status == status)
sync_logs = sync_query.order_by(LetzshopSyncLog.created_at.desc()).all()
for log in sync_logs:
v_name, v_code = vendor_lookup.get(log.vendor_id, (None, None))
jobs.append(
{
"id": log.id,
@@ -717,9 +732,9 @@ class LetzshopOrderService:
"records_processed": log.records_processed or 0,
"records_succeeded": log.records_succeeded or 0,
"records_failed": log.records_failed or 0,
"vendor_id": vendor_id,
"vendor_name": vendor_name,
"vendor_code": vendor_code,
"vendor_id": log.vendor_id,
"vendor_name": v_name,
"vendor_code": v_code,
"error_details": log.error_details,
}
)
@@ -727,9 +742,10 @@ class LetzshopOrderService:
# Product exports from letzshop_sync_logs
if job_type in (None, "export"):
export_query = self.db.query(LetzshopSyncLog).filter(
LetzshopSyncLog.vendor_id == vendor_id,
LetzshopSyncLog.operation_type == "product_export",
)
if vendor_id:
export_query = export_query.filter(LetzshopSyncLog.vendor_id == vendor_id)
if status:
export_query = export_query.filter(LetzshopSyncLog.status == status)
@@ -738,6 +754,7 @@ class LetzshopOrderService:
).all()
for log in export_logs:
v_name, v_code = vendor_lookup.get(log.vendor_id, (None, None))
jobs.append(
{
"id": log.id,
@@ -749,10 +766,10 @@ class LetzshopOrderService:
"records_processed": log.records_processed or 0,
"records_succeeded": log.records_succeeded or 0,
"records_failed": log.records_failed or 0,
"vendor_id": vendor_id,
"vendor_name": vendor_name,
"vendor_code": vendor_code,
"error_details": log.error_details, # Include export file details
"vendor_id": log.vendor_id,
"vendor_name": v_name,
"vendor_code": v_code,
"error_details": log.error_details,
}
)