Commit Graph

674 Commits

Author SHA1 Message Date
483f150e7a feat(static): improve static files handling and permissions in Docker
- Add script to set correct permissions on static files after collectstatic
- Introduce collectstatic command in entrypoint with permission fixing
- Add WhiteNoise middleware for efficient static file serving without DB access
- Configure WhiteNoise static files storage backend in settings
- Set STATIC_ROOT path properly for Docker container environment
- Add fallback static files serving in Django urls for production without nginx
- Enhance inventory_detail.html scripts to log errors if JS files or components fail to load
- Add whitenoise package to requirements for static file serving support
2025-12-22 20:45:52 +03:00
6eea53754a Прочие изменения в модулях inventory и products 2025-12-22 13:44:08 +03:00
c476eafd4a Добавлено сохранение snapshot-значений для проведенных инвентаризаций
- Добавлены поля snapshot_* в модель InventoryLine для фиксации значений на момент завершения
- Обновлен InventoryProcessor для сохранения snapshot перед обработкой
- Обновлен InventoryDetailView для отображения snapshot-значений в проведенных инвентаризациях
- Добавлена миграция 0018 для новых полей
- Теперь в проведенных инвентаризациях отображаются оригинальные значения и правильная разница, а не текущие скорректированные остатки
2025-12-22 13:43:35 +03:00
9b430c7eb0 Исправлен порядок создания ролей при одобрении заявки
- Перемещено создание системных ролей перед назначением роли владельцу
- Теперь UserRole создается автоматически для владельца при одобрении заявки
- Исправлена ошибка: роль назначалась до создания ролей в БД
2025-12-22 10:44:42 +03:00
ccb0c4304f Исправлено отображение номера документа в WriteOffDocumentItem на странице дебага
- Убран ID документа из отображения (было: WO-0000025, стало: WO-000002)
- Теперь отображается только document_number родительского документа
- Устранена путаница с номерами: WriteOffDocumentItem не имеет собственного номера
2025-12-22 00:42:48 +03:00
a8ba5ce780 Улучшения инвентаризации: автоматическое проведение документов, оптимизация запросов и улучшения UI
- Автоматическое проведение документов списания и оприходования после завершения инвентаризации
- Оптимизация SQL-запросов: устранение N+1, bulk-операции для Stock, агрегация для StockBatch и Reservation
- Изменение формулы расчета разницы: (quantity_fact + quantity_reserved) - quantity_available
- Переименование поля 'По факту' в 'Подсчитано (факт, свободные)'
- Добавлены столбцы 'В резервах' и 'Всего на складе' в таблицу инвентаризации
- Перемещение столбца 'В системе (свободно)' после 'В резервах' с визуальным выделением
- Центральное выравнивание значений в столбцах таблицы
- Автоматическое выделение текста при фокусе на поле ввода количества
- Исправление форматирования разницы (убраны лишние нули)
- Изменение статуса 'Не обработана' на 'Не проведено'
- Добавление номера документа для инвентаризаций (INV-XXXXXX)
- Отображение всех типов списаний в debug-странице (WriteOff, WriteOffDocument, WriteOffDocumentItem)
- Улучшение отображения документов в детальном просмотре инвентаризации с возможностью перехода к ним
2025-12-21 23:59:02 +03:00
bb821f9ef4 Исправление отображения фото в POS и улучшение обработки изображений
- Исправлен POS для использования миниатюр вместо оригиналов для быстрой загрузки
- Убран fallback на оригиналы - показываем миниатюру или ничего (лучше видно ошибки)
- Исправлен ImageService - возвращает пустую строку если миниатюра обработанного файла не найдена
- Исправлена ошибка JavaScript при массовом удалении фото (insertAdjacentElement на null)
- Добавлен контейнер photos-messages-container для надежного отображения сообщений
- Улучшено логирование ImageService для отладки путей к файлам
- Добавлена проверка exists() с детальным логированием в TenantAwareFileSystemStorage
2025-12-21 19:52:55 +03:00
812ecb53e6 Fix media file storage path and permissions
- Fix MEDIA_ROOT path to match Docker volume mount (/app/myproject/media)
- Update docker-compose.yml volume mounts to match MEDIA_ROOT
- Add setup_directories() function in entrypoint.sh to create media directories with proper permissions
- Add logging to TenantAwareFileSystemStorage for debugging
- Fix is_returned flag logic improvements (from previous work)
2025-12-21 16:54:44 +03:00
a55be3095b Fix Docker setup: add gunicorn, fix permissions, update docker-compose and entrypoint, add deployment instructions 2025-12-21 15:05:58 +03:00
ec02360eac Оптимизация страницы детального просмотра заказа: перемещение блока товаров вверх, добавление статуса в заголовок, оптимизация SQL-запросов 2025-12-21 12:57:54 +03:00
375ec5366a Унификация генерации номеров документов и оптимизация кода
- Унифицирован формат номеров документов: IN-XXXXXX (6 цифр), как WO-XXXXXX и MOVE-XXXXXX
- Убрано дублирование функции _extract_number_from_document_number
- Оптимизирована инициализация счетчика incoming: быстрая проверка перед полной инициализацией
- Удален неиспользуемый файл utils.py (функциональность перенесена в document_generator.py)
- Все функции генерации номеров используют единый подход через DocumentCounter.get_next_value()
2025-12-21 00:51:08 +03:00
78dc9e9801 Добавлено разделение типов поступлений на склад
- Добавлено поле receipt_type в модель IncomingBatch с типами: supplier, inventory, adjustment
- Исправлен баг в InventoryProcessor: теперь корректно создается IncomingBatch при инвентаризации
- Создан IncomingAdjustmentCreateView для оприходования без инвентаризации
- Обновлены формы, шаблоны и админка для поддержки разных типов поступлений
- Добавлена навигация и URL для оприходования
- Тип поступления отображается в списках приходов и партий
2025-12-20 23:47:13 +03:00
f1798291e0 Добавить маски для файлов экспорта клиентов в .gitignore 2025-12-20 19:21:01 +03:00
3a10df2761 Удалить случайно добавленные файлы экспорта клиентов и обновить .gitignore 2025-12-20 19:20:51 +03:00
72b0de1863 Добавлены новые статусы заказов: Частично собран и Полностью собран
- Добавлены системные статусы partially_assembled и fully_assembled в order_status_service.py
- Создана management команда update_order_statuses для обновления статусов у всех тенантов
- Новые статусы интегрируются в существующую логику резервирования и списания товара
- Статусы располагаются между 'В сборке' и 'В доставке' в естественном порядке процесса
2025-12-20 19:19:01 +03:00
2508d85b28 Оптимизация списка категорий: устранение N+1 запросов
- Добавлен Prefetch для активных товаров и комплектов в категориях
- Фильтрация и сортировка вынесены в Prefetch (избегаем повторных запросов)
- Изменен метод build_category_tree для использования предзагруженных данных

Результаты:
- Список категорий: 12→7 запросов, 26.76→~10мс
- Устранены 4 похожих N+1 запроса (products и kits для каждой категории)
2025-12-20 18:05:44 +03:00
fed62d992a Оптимизация производительности: устранение N+1 запросов и дубликатов
- Добавлен django-debug-toolbar 6.1.0 для мониторинга производительности
- Устранен дублирующийся COUNT запрос в списке клиентов (используется paginator.count)
- Добавлен select_related('status') в списке заказов для устранения N+1

Результаты:
- Список клиентов: 6→5 запросов, 13.24→10мс
- Список заказов: 18→7 запросов, 52.68→15-20мс, устранено 11 дубликатов
2025-12-20 18:02:23 +03:00
0bf694966b chore(errors): remove temporary error log file
- Deleted the temporary Excel error log file
- Cleaned up unused binary artifact from repository
2025-12-20 12:46:21 +03:00
6c72126276 feat: Add script to ensure public tenant and its domain exist. 2025-12-18 00:59:37 +03:00
7b32cdcebf Обновления и новые функции: изменение шаблона клиента, обновление сигналов инвентаря, добавление снимков наборов и элементов заказа, обновление моделей заказов и продуктов 2025-12-18 00:14:24 +03:00
56725e8092 Добавлена система фильтрации клиентов с универсальным поиском
- Реализован универсальный поиск по имени, email и телефону в одной строке
- Добавлен счетчик общего количества клиентов
- Поиск работает по нажатию Enter или кнопке 'Поиск'
- Удалены неиспользуемые фильтры django-filter
- Упрощен интерфейс списка клиентов
- Добавлена кнопка 'Очистить' для сброса поиска
2025-12-14 22:39:32 +03:00
089ccfa8ae Рефакторинг: вынесена логика импорта/экспорта клиентов в отдельный сервис
- Создан модуль customers/services/import_export.py согласно best practices
- Класс CustomerExporter: содержит логику экспорта в CSV (ранее была в views)
- Класс CustomerImporter: заглушка для будущей реализации импорта
- Views стали тонкими: customer_export и customer_import делегируют работу сервисам
- Улучшена организация кода: соблюдён принцип Single Responsibility
- Уменьшен размер views.py на 30 строк
- Добавлена подробная документация в docstrings классов и методов
- Логику теперь легко тестировать и переиспользовать (например, в Celery tasks)

Преимущества:
- Чистое разделение ответственности
- Упрощённое тестирование
- Возможность переиспользования в асинхронных задачах
- Соответствие Django best practices
2025-12-14 20:55:21 +03:00
b41025116c Исправлен экспорт клиентов: удалены несуществующие поля
- Исправлена ошибка AttributeError: Customer не имеет полей first_name и last_name
- Модель Customer имеет только поле name (полное имя)
- Удалён экспорт баланса кошелька по требованию пользователя
- Обновлена инструкция в шаблоне импорта: убраны фамилия и баланс
- Добавлены пометки об уникальности email и телефона
- Теперь экспорт работает корректно с полями: ID, Имя, Email, Телефон, Дата создания
2025-12-14 20:45:29 +03:00
778c979aa3 Добавлены кнопки Импорт и Экспорт на страницу списка клиентов
- Добавлены кнопки Импорт и Экспорт в header страницы customers/
- Создан URL-маршрут для customer-import и customer-export
- Реализована функция customer_export: экспорт всех клиентов в CSV файл с BOM для Excel
- Экспортируются поля: ID, Имя, Фамилия, Email, Телефон, Баланс кошелька, Дата создания
- Создан шаблон customer_import.html с инструкцией и формой загрузки файла
- Функция customer_import пока заглушка (TODO: реализовать парсинг CSV/Excel)
- Кнопки оформлены в btn-group с иконками Bootstrap Icons
- Системный клиент исключён из экспорта
2025-12-14 20:41:21 +03:00
34e5a0143b Исправлено время заказов: переход на минский часовой пояс (Europe/Minsk)
- Изменён TIME_ZONE с Europe/Moscow на Europe/Minsk в settings.py
- Исправлено создание заказов в POS: теперь используется timezone.localtime() для корректной конвертации UTC → Minsk
- delivery_date и delivery_time_start/end теперь сохраняются в минском времени, а не UTC
- Исправлена разница в 3 часа между временем создания заказа и фактическим временем
- Удалено устаревшее поле payment_method из showcase_manager.py (поле было удалено из модели Order)

Теперь все временные метки заказов соответствуют реальному минскому времени
2025-12-14 15:27:03 +03:00
ce2cfca3f2 Добавлена возможность добавлять новые товары в витринный комплект при редактировании
- Интегрирован готовый компонент ProductSearchPicker в модалку редактирования
- Добавлен collapse-блок с поиском товаров, который отображается только в режиме редактирования
- При выборе товара он автоматически добавляется в tempCart или увеличивается количество если уже есть
- Добавлен CSS и JS для компонента product-search-picker
- В view передаётся categories QuerySet для работы фильтров компонента
- Блок добавления товаров показывается только при редактировании, скрыт при создании нового комплекта
2025-12-14 14:00:51 +03:00
aff25d0317 Реализована возможность редактирования состава витринного комплекта с поддержкой отрицательных резервов
- Добавлены методы reserve_product_to_showcase и release_showcase_reservation в ShowcaseManager
- Методы работают с резервами для всех активных экземпляров витринного комплекта
- НЕ блокируют создание резерва при нехватке товара, возвращают информацию о дефиците (overdraft)
- Обновлён API endpoint update_product_kit для корректировки резервов при изменении состава
- Добавлено визуальное предупреждение на фронте о нехватке товара на складе
- В модалке редактирования комплекта добавлены контролы для изменения количества товаров (+/-, поле ввода, удаление)
- Автоматический пересчёт цен при изменении состава
- Очистка корзины POS после успешного создания витринного комплекта
2025-12-14 13:49:13 +03:00
835d6020e2 Добавлен переключатель видимости пароля на всех страницах входа 2025-12-14 01:56:50 +03:00
f03e750030 feat: Add Docker entrypoint script for application orchestration and implement cleanup for stuck photo processing tasks with improved error handling. 2025-12-13 22:42:58 +03:00
ea1d9546b9 Добавлен алиас celery для совместимости с командами Celery CLI в Docker 2025-12-13 01:33:43 +03:00
87079deca1 Исправлены команды запуска Celery в Docker (worker и beat) для корректного автоопределения приложения 2025-12-13 01:31:33 +03:00
b5e1372cfc Исправлены окончания строк в entrypoint.sh (CRLF -> LF) и добавлен .gitattributes 2025-12-13 01:28:55 +03:00
f2549d7789 Исправлена команда запуска Celery Beat в Docker для корректной загрузки приложения 2025-12-13 01:26:09 +03:00
a1d77d778a Добавлен функционал деактивации/реактивации ролей пользователей 2025-12-13 01:25:19 +03:00
6470fb7588 Fix volume mounts for media and celere worker paths 2025-12-12 20:48:16 +03:00
6023496a7d Fix Celery startup and ImageService temp handling 2025-12-12 20:23:00 +03:00
f320eafc55 Фикс деплоя на NAS: статика, медиа, автоматическое создание системного покупателя 2025-12-12 19:21:45 +03:00
4cbc5c07b9 feat: Implement Dockerized multi-tenant Django application with initial setup for database, migrations, and superuser creation. 2025-12-12 18:04:36 +03:00
0046b36e89 fix: Исправлена работа кликабельного dropdown-меню Заказы
- Убраны атрибуты data-bs-toggle и role, блокирующие переход по ссылке
- Теперь клик на Заказы открывает список заказов
- Dropdown продолжает работать при наведении благодаря CSS стилям
- Исправлена проблема, когда клик не приводил к переходу на страницу
2025-12-12 05:07:35 +03:00
48223e32d8 feat: Сделан кликабельным заголовок dropdown-меню Заказы
- Заголовок 'Заказы' теперь ведёт на список заказов при клике
- Сохранена функциональность dropdown-меню при наведении
- Улучшена навигация - теперь можно перейти к заказам напрямую без раскрытия меню
2025-12-12 05:04:52 +03:00
a8066d87ed refactor: Реструктуризация навигационного меню с группировкой по функциональным блокам
Изменения в navbar.html:
- Объединены ссылки в логические dropdown-группы
- Уменьшено количество пунктов верхнего уровня с 10+ до 6
- Добавлены эмодзи-иконки для визуальной идентификации разделов

Структура меню:
📦 Товары (dropdown)
   - Все товары, Каталог, Вариативные товары
   - Категории, Теги, Варианты (группы)

📋 Заказы (dropdown)
   - Список заказов
   - Статусы заказов

👥 Клиенты (одиночная ссылка)

🏭 Склад (dropdown)
   - Управление складом
   - Витрины

💰 Касса (одиночная ссылка)

⚙️ Настройки (dropdown, только для owner/superuser)
   - Роли пользователей
   - Debug (только для superuser)

Преимущества:
- Компактная навигация - проще найти нужный раздел
- Логическая группировка связанных функций
- Сохранена подсветка активного раздела
- Улучшена визуальная идентификация с помощью иконок
2025-12-12 05:02:29 +03:00
e35d3d642c feat: Добавлена ссылка на CRUD категорий в навигацию
- Добавлена ссылка 'Категории' в главное меню навигации
- Ссылка размещена логически между 'Варианты' и 'Теги'
- Добавлена подсветка активного пункта меню при работе с категориями
- Теперь доступ к управлению категориями товаров доступен из главного меню
2025-12-12 05:00:00 +03:00
e54d7d04d7 feat: Добавлены команды управления данными тенантов и исправлены фильтры по статусу товаров
Добавлено:
- Команда clear_tenant_data для полной очистки данных тенанта без удаления схемы
  * Очищает все таблицы через TRUNCATE CASCADE
  * Сбрасывает ID-последовательности
  * Сохраняет схему БД и запись Client
  * Поддержка флага --noinput для автоматизации

- Команда init_tenant_data для инициализации системных данных тенанта
  * Создаёт системного клиента (АНОНИМНЫЙ ПОКУПАТЕЛЬ для POS)
  * Создаёт 8 системных статусов заказов
  * Создаёт 5 системных способов оплаты
  * Поддержка флага --reset для пересоздания данных

Исправлено:
- Заменены устаревшие фильтры is_active на status='active' для Product и ProductKit
  * products/views/category_views.py: исправлены фильтры в build_category_tree и get_context_data
  * products/services/kit_pricing.py: исправлены фильтры при получении товаров из variant_group
  * products/models/kits.py: исправлен фильтр в get_available_products
  * Устранена ошибка FieldError при работе со списком категорий

Улучшено:
- Команда clear_tenant_data теперь предлагает пользователю инициализировать системные данные после очистки
- Добавлена детальная информация о процессе очистки и инициализации данных
2025-12-12 04:58:26 +03:00
2d253584ba Добавлена обработка ValidationError в AJAX API и Bootstrap alert на странице списка заказов
Проблема:
На странице списка заказов (order_list) при изменении статуса 'на лету':
- ValidationError показывался через alert() - страшно
- Сообщение содержало служебные элементы
- Статус всё равно менялся визуально (нет отката)

Решение Backend (views.py):
- В set_order_status добавлена обработка ValidationError ПЕРЕД ValueError
- Извлекается чистое сообщение (e.messages[0] или str(e))
- Возвращается JSON: {success: false, error: 'чистое сообщение'}

Решение Frontend (order_list.html):
- Добавлен контейнер для динамических Bootstrap alert
- Создана функция showAlert() для показа красивых alert-danger
- При ошибке:
  * Показывается Bootstrap alert с иконкой
  * Прокрутка к верху страницы
  * Автоскрытие через 5 секунд
  * Возврат select к предыдущему значению (откат визуально)
- Больше НЕТ страшных alert()

Теперь пользователь видит:
[красный Bootstrap alert вверху страницы]
⚠️ Заказ 134 был отменён, товары проданы в другом заказе.
Невозможно изменить статус. Для новой продажи создайте новый заказ.
[X]

User-friendly на обеих страницах (форма редактирования + список)!
2025-12-12 00:18:09 +03:00
49cfec3088 Добавлена обработка ValidationError с выводом через Django messages
Проблема:
ValidationError из сигналов отображался как:
'Server error: [\'Заказ 134 был отменён...\']'
со служебными элементами (Server error, квадратные скобки).

Решение:
В order_update добавлена обработка ValidationError перед ValueError:
- Извлекаем чистое сообщение из исключения (e.messages[0] или str(e))
- Показываем через messages.error() — Django автоматически отобразит
  красивым Bootstrap alert-danger
- Транзакция откатывается, изменения не сохраняются

Теперь пользователь видит:
[красный Bootstrap alert]
'Заказ 134 был отменён, товары проданы в другом заказе.
Невозможно изменить статус. Для новой продажи создайте новый заказ.'

Без технических префиксов и форматирования - user-friendly.
2025-12-12 00:08:53 +03:00
449b693ab5 Улучшено сообщение об ошибке для возвращённых заказов - убраны переносы строк
Проблема:
Сообщение ValidationError с переносами строк \\n отображалось как текст,
а не как реальные переносы, плюс выглядело как 'Server error' - страшно.

Решение:
Сделано короткое однострочное сообщение без \\n:
'Заказ 134 был отменён, товары проданы в другом заказе.
Невозможно изменить статус. Для новой продажи создайте новый заказ.'

Теперь user-friendly, без технических деталей и пугающих форматирований.
2025-12-12 00:04:24 +03:00
2dcdc0941f Расширена валидация для возвращённых заказов: запрет любых статусов кроме отрицательных
Проблема:
Для заказа с is_returned=True без резервов (товар продан в другом заказе)
можно было установить промежуточные статусы (В доставке, Черновик и т.п.),
что не имеет смысла, т.к. физически продавать уже нечего.

Решение:
Валидация теперь проверяет ДО проверки is_positive_end:
- Если is_returned=True И резервов нет И статус НЕ отрицательный →
  запрещаем ЛЮБОЕ изменение статуса
- Разрешены только статусы с is_negative_end=True (отменён и т.п.)

Улучшено сообщение об ошибке:
- Убраны длинные объяснения
- Короткая структура с переносами строк
- Чёткое указание: «товары проданы в другом заказе»
- Действие: «создайте новый заказ»

Теперь возвращённый заказ без резервов навсегда остаётся в статусе
отрицательного исхода — как и должно быть в реальности.
2025-12-12 00:03:10 +03:00
503a00de74 Улучшена логика флага is_returned и добавлен запрет повторного completed для возвращённых заказов
Проблема:
1. Флаг is_returned управлялся в разных местах непоследовательно
2. При цепочке completed → cancelled → completed флаг оставался True
3. Можно было установить положительный статус для заказа с is_returned=True
   без резервов (товар уже продан в другом заказе)

Решение:

1. ЕДИНАЯ ФУНКЦИЯ update_is_returned_flag():
   - Флаг основан на РЕАЛЬНОМ состоянии заказа (наличие Sale)
   - Логика: есть Sale → is_returned=False
   - Нет Sale + был когда-то в положительном финальном статусе → is_returned=True
   - Нет Sale + никогда не был в положительном статусе → is_returned=False

2. ВЫЗОВ update_is_returned_flag() в ключевых точках:
   - После создания Sale (create_sale_on_order_completion)
   - После отката Sale (rollback_sale_on_status_change)
   - После освобождения резервов (release_reservations_on_cancellation)

3. ВАЛИДАЦИЯ в create_sale_on_order_completion:
   - Запрещаем переход в положительный финальный статус (is_positive_end=True)
     для заказов с is_returned=True, у которых нет резервов
   - Даём понятное сообщение: резервы отсутствуют, товары могли быть проданы
     в другом заказе, оставьте статус отрицательного исхода или создайте новый заказ

4. АВТОМАТИЧЕСКИЙ СБРОС is_returned:
   - При законном переходе в положительный статус с резервами флаг сбрасывается
   - Это позволяет исправить ошибочную отмену: cancelled → completed работает,
     если резервы на месте (товар не ушёл в другой заказ)

5. УДАЛЕНА ДУБЛИРУЮЩАЯ ЛОГИКА:
   - Убрали ручное управление is_returned в rollback_sale_on_status_change
   - Убрали ручное управление is_returned в release_reservations_on_cancellation
   - Теперь один источник истины через update_is_returned_flag()

Результат:
- Флаг is_returned всегда соответствует реальности (наличию Sale)
- Невозможно установить completed для возвращённого заказа без резервов
- Защита от двойного списания при переиспользовании витринных комплектов
- Понятные сообщения об ошибках для пользователя
- Предсказуемое поведение при любых комбинациях смены статусов
2025-12-11 23:54:48 +03:00
2a3898fb44 Исправлена повторная продажа витринных комплектов при возврате в статус completed
Проблема:
При отмене заказа (completed → cancelled) и последующем возврате в completed
витринные комплекты оставались зарезервированными и не уходили со склада.
Резервы не конвертировались в продажи, Sale не создавались.

Причина:
При откате заказа (уход от completed) мы обнуляли reservation.order_item = None
для витринных комплектов. Это разрывало связь между резервом и позицией заказа.

При повторном переходе в completed сигнал create_sale_on_order_completion
искал резервы по фильтру:
  Reservation.objects.filter(order_item=item, product_kit=kit)

Но так как order_item был None, резервы не находились и Sale не создавались.

Решение:
Разделили семантику полей Reservation:
- order_item - принадлежность к позиции заказа (часть жизненного цикла заказа)
- cart_lock_expires_at, locked_by_user, cart_session_id - блокировки корзины

При откате заказа (completed → другой_статус):
- НЕ трогаем order_item - он остаётся привязанным к OrderItem
- Очищаем ТОЛЬКО cart lock поля (expires_at, locked_by_user, session_id)
- Резервы витринных комплектов: status = reserved

При повторном переходе в completed:
- create_sale_on_order_completion находит резервы (order_item сохранён!)
- Создаёт Sale для каждого компонента
- Конвертирует резервы: reserved → converted_to_sale
- Витринный экземпляр помечается как проданный через ShowcaseManager

Изменения в 3 местах:
1. rollback_sale_on_status_change - откат от completed
2. release_reservations_on_cancellation - переход к cancelled
3. release_stock_on_order_delete - удаление заказа

Во всех случаях для витринных комплектов сохраняем order_item, очищаем
только cart lock поля.

Результат:
Теперь витринные комплекты можно продавать/отменять/продавать снова
через смену статуса заказа в админке - как в реальной жизни.
2025-12-11 23:17:12 +03:00
d44687649c Добавлен автоматический возврат витринных экземпляров на витрину при откате заказа
Проблема:
При отмене заказа (completed → cancelled) резервы корректно возвращались
в статус 'reserved', но ShowcaseItem оставались в статусе 'sold'.
Из-за этого витринные букеты не отображались в POS после отмены заказа,
хотя физически должны были вернуться на витрину.

Решение:
В существующий сигнал rollback_sale_on_status_change добавлена логика
возврата витринных экземпляров на витрину:

1. После отката Sale и Reservation находим все ShowcaseItem, проданные
   в рамках отменяемого заказа (sold_order_item__order=instance)

2. Для каждого экземпляра:
   - Меняем status: sold → available
   - Очищаем sold_order_item = None
   - Очищаем sold_at = None
   - НЕ трогаем showcase и product_kit (букет остаётся на той же витрине)

3. Логируем количество возвращённых экземпляров

Преимущества:
- Элегантно: вся логика отката в одном месте (сигнал)
- Транзакционно: откат Sale, Reservation и ShowcaseItem в одной транзакции
- Универсально: работает для POS и обычных заказов
- Без костылей: используем существующую архитектуру сигналов

Теперь при отмене заказа витринный букет автоматически возвращается
на витрину и снова виден в POS - как в реальной жизни.
2025-12-11 23:09:56 +03:00