Добавлен асинхронный импорт товаров с параллельной загрузкой фото + исправлен баг со счётчиком SKU

- Реализован импорт Product из CSV/XLSX через Celery с прогресс-баром
- Параллельная загрузка фото товаров с внешних URL (масштабируемость до 500+ товаров)
- Добавлена модель ProductImportJob для отслеживания статуса импорта
- Создан таск download_product_photo_async для загрузки фото в фоне
- Интеграция с существующим ImageProcessor (синхронная обработка через use_async=False)
- Добавлены view и template для импорта с real-time обновлением через AJAX

FIX: Исправлен баг со счётчиком SKU - инкремент только после успешного сохранения
- Добавлен SKUCounter.peek_next_value() - возвращает следующий номер БЕЗ инкремента
- Добавлен SKUCounter.increment_counter() - инкрементирует счётчик
- generate_product_sku() использует peek_next_value() вместо get_next_value()
- Добавлен post_save сигнал increment_sku_counter_after_save() для инкремента после создания
- Предотвращает пропуски номеров при ошибках валидации (например cost_price NULL)

FIX: Исправлена ошибка с is_main в ProductPhoto
- ProductPhoto не имеет поля is_main, используется только order
- Первое фото (order=0) автоматически считается главным
- Удалён параметр is_main из download_product_photo_async и _collect_photo_tasks

Изменены файлы:
- products/models/base.py - методы для управления счётчиком SKU
- products/models/import_job.py - модель для отслеживания импорта
- products/services/import_export.py - сервис импорта с поддержкой Celery
- products/tasks.py - таски для асинхронного импорта и загрузки фото
- products/signals.py - сигнал для инкремента счётчика после сохранения
- products/utils/sku_generator.py - использование peek_next_value()
- products/views/product_import_views.py - view для импорта
- products/templates/products/product_import*.html - UI для импорта
- docker/entrypoint.sh - настройка Celery worker (concurrency=4)
- requirements.txt - добавлен requests для загрузки фото
This commit is contained in:
2026-01-06 07:10:12 +03:00
parent d44ae0b598
commit 0f19542ac9
16 changed files with 1678 additions and 6 deletions

View File

@@ -2,13 +2,15 @@
Signals для приложения products.
Логирует изменения себестоимости товара через CostPriceHistory.
FIX: SKU counter bug - инкрементирует счётчик после успешного создания Product.
"""
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import models
import re
from .models import Product, CostPriceHistory
from .models import Product, CostPriceHistory, SKUCounter
@receiver(post_save, sender=Product)
@@ -48,3 +50,24 @@ def log_cost_price_changes(sender, instance, created, **kwargs):
reason='recalculation',
notes='Себестоимость пересчитана на основе партий товара'
)
@receiver(post_save, sender=Product)
def increment_sku_counter_after_save(sender, instance, created, **kwargs):
"""
FIX: SKU counter bug - increment only after successful save
Инкрементирует счётчик SKU ПОСЛЕ успешного создания товара с автогенерированным артикулом.
Это предотвращает пропуски номеров при ошибках сохранения (например, валидация cost_price).
Счётчик инкрементируется только если:
- Товар только что создан (created=True)
- SKU соответствует автогенерированному формату (PROD-XXXXXX)
"""
if not created:
return
# Проверяем что SKU был автогенерирован (формат PROD-XXXXXX)
if instance.sku and re.match(r'^PROD-\d{6}$', instance.sku):
# Инкрементируем счётчик product
SKUCounter.increment_counter('product')