- Реализован импорт 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 для загрузки фото
- Introduced Recipient model to manage order recipients separately from customers.
- Updated Order model to link to Recipient, replacing recipient_name and recipient_phone fields.
- Enhanced OrderForm to include recipient selection modes: customer, history, and new.
- Added AJAX endpoint to fetch recipient history for customers.
- Updated admin interface to manage recipients and display recipient information in order details.
- Refactored address handling to accommodate new recipient logic.
- Improved demo order creation to include random recipients.
Resolves critical bug where photos of products with the same ID in different
tenants were overwriting each other. Implemented complete isolation of media
files between tenants using custom Django storage backend.
## Changes
### New Files
- products/utils/storage.py: TenantAwareFileSystemStorage backend
* Automatically adds tenant_id to file paths on disk
* Prevents cross-tenant file access with security checks
* Stores clean paths in DB for portability
- products/tests/test_multi_tenant_photos.py: Comprehensive tests
* 5 tests covering isolation, security, and configuration
* All tests passing ✅
- MULTITENANT_PHOTO_FIX.md: Complete documentation
### Modified Files
- settings.py: Configured DEFAULT_FILE_STORAGE to use TenantAwareFileSystemStorage
- products/models/photos.py:
* Converted upload_to from strings to callable functions
* Updated ProductPhoto, ProductKitPhoto, ProductCategoryPhoto
* Added tenant isolation documentation
- products/tasks.py: Added documentation about file structure
- products/utils/image_processor.py: Added documentation
- products/utils/image_service.py: Added documentation
## Architecture
**On disk:** media/tenants/{tenant_id}/products/{entity_id}/{photo_id}/{size}.ext
**In DB:** products/{entity_id}/{photo_id}/{size}.ext
Tenant ID is automatically added/removed during file operations.
## Security
- Storage rejects cross-tenant file access
- Proper tenant context validation
- Integration with django-tenants schema system
## Testing
- All 5 multi-tenant photo tests pass
- Verified photo paths are isolated per tenant
- Verified storage rejects cross-tenant access
- Verified configuration is correct
## Future-proof
- Ready for S3 migration (just change storage backend)
- No breaking changes to existing code
- Clean separation of concerns
Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Добавлен retry на 5 сек при DoesNotExist для ожидания коммита транзакции
- temp_path сохраняется в PhotoProcessingStatus.result_data при постановке задачи
- При окончательной неудаче not_found удаляется осиротевший temp файл
- Предотвращает накопление temp файлов при гонке создания фото
- Added temp file deletion in Celery task after successful processing
- Added temp file cleanup in sync fallback method
- Added temp file removal in delete() if processing never completed
- Prevents accumulation of orphaned files in media/<entity>/temp/ folders
При асинхронной обработке фото нужно сначала сохранить файл в БД,
потом запустить Celery task. Иначе task не найдет файл.
Изменения:
- BasePhoto.save() теперь сохраняет файл перед запуском task
- Исправлена проблема 'Photo has no image file' в Celery worker
🤖 Generated with Claude Code