"""Shared import lifecycle; callers enforce their own admin permission boundary.""" import json from datetime import timedelta from django.contrib.admin.models import LogEntry, ADDITION from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db import transaction from django.shortcuts import get_object_or_404 from django.utils import timezone from apps.shop.import_models import ProductImportBatch from apps.shop.models import Product from apps.shop.product_import import parse_workbook, import_products, catalog_fingerprint, lock_catalog, HEADERS from utils.admin_excel import excel_response @transaction.atomic def preview_batch(owner, workspace, upload, options, sheet='', header_row=1, mapping=None): rows = parse_workbook(upload, sheet, header_row, mapping) lock_catalog(workspace) fingerprint = catalog_fingerprint(workspace) result = import_products(workspace, rows, options, dry_run=True) return ProductImportBatch.objects.create(sys_ws=workspace, created_by=owner, expires_at=timezone.now()+timedelta(minutes=30), filename=upload.name[:255], fingerprint=fingerprint, payload={'rows': rows, 'options': options}, result=result) @transaction.atomic def confirm_batch(batch_id, owner, workspace=None): queryset = ProductImportBatch.objects.select_for_update().filter(created_by=owner) if workspace is not None: queryset = queryset.filter(sys_ws=workspace) batch = get_object_or_404(queryset, pk=batch_id) if batch.confirmed_at: return batch if batch.expires_at <= timezone.now(): raise ValidationError('پیش‌نمایش منقضی شده؛ فایل را دوباره اعتبارسنجی کنید.') lock_catalog(batch.sys_ws) if catalog_fingerprint(batch.sys_ws) != batch.fingerprint: raise ValidationError('محصولات یا دسته‌ها پس از پیش‌نمایش تغییر کرده‌اند؛ پیش‌نمایش تازه بسازید.') result = import_products(batch.sys_ws, batch.payload['rows'], batch.payload['options']) batch.result = result if result['committed']: batch.confirmed_at = timezone.now() LogEntry.objects.log_action(user_id=owner.pk, content_type_id=ContentType.objects.get_for_model(Product).pk, object_id=None, object_repr=f'Excel import #{batch.pk}', action_flag=ADDITION, change_message=json.dumps({'import': batch.pk, 'created': result['created'], 'updated': result['updated'], 'errors': result['errors']})) batch.save(update_fields=('result', 'confirmed_at')) return batch def batch_report(batch): rows = ((row['row'], *(row['values'].get(key) for key in HEADERS), row['status'], row['errors']) for row in batch.result.get('rows', [])) return excel_response(('ردیف', *HEADERS, 'وضعیت', 'خطاها'), rows, f'import-report-{batch.pk}.xlsx') def import_template(): return excel_response(HEADERS.keys(), [('محصول نمونه', 'دسته اصلی/زیردسته', 'CODE-001', 1000, 1, 1, 1, 1, False, '{}', 'عدد')], 'product-import-template.xlsx')