Добавлен механизм skip_sale_creation на базе thread-local storage
для управления моментом создания Sale через сигнал.
Проблема: сигнал create_sale_on_order_completion срабатывал при
Order.objects.create(status=completed) до применения скидок.
Решение: пропускать сигнал во время создания заказа, затем явно
создавать Sale после применения всех скидок через order.save().
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавлено поле KitItem.unit_price для хранения зафиксированной цены
- Витринные комплекты больше не обновляются при изменении цен товаров
- Добавлен красный индикатор на карточке если цена неактуальна
- Добавлен warning в модалке редактирования с кнопкой "Актуализировать"
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавить display_name в CustomUser (name или email)
- Исправить get_showcase_kits_api: заменить username на display_name
- Использовать Case/When с output_field для выбора имени на уровне БД
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавить фиксированную панель корзины внизу экрана на мобильных
- Отображение количества товаров и суммы
- Кнопки "Продать" и "Очистить" всегда доступны
- Тап на панель открывает корзину как overlay
- Фиксировать поиск и категории сверху на мобильных
- Поиск всегда виден при скролле
- Категории в collapsible-блоке (сворачиваются)
- Категории в 3 колонки на мобильных
- Улучшить поиск по токенам (разбивает фразу на слова)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавить inline-редактирование цен в списке товаров
- Оптимизировать карточки товаров в POS-терминале
- Рефакторинг моделей единиц измерения
- Миграция unit -> base_unit в SalesUnit
- Улучшить UI форм создания/редактирования товаров
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавлен combine_mode в форму создания/редактирования скидок
- Добавлена колонка "Объединение" в список скидок с иконками
- Добавлен фильтр по режиму объединения скидок
- Добавлена валидация: только одна exclusive скидка на заказ
- Удалены дублирующие поля из Order и OrderItem:
- applied_discount, applied_promo_code, discount_amount
- Скидки теперь хранятся только в DiscountApplication
- Добавлены свойства для обратной совместимости
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Добавлено поле combine_mode с тремя режимами:
- stack - складывать с другими скидками
- max_only - применять только максимальную
- exclusive - отменяет все остальные скидки
Изменения:
- Модель Discount: добавлено поле combine_mode
- Calculator: новый класс DiscountCombiner, методы возвращают списки скидок
- Applier: создание нескольких DiscountApplication записей
- Admin: отображение combine_mode с иконками
- POS API: возвращает списки применённых скидок
- POS UI: отображение нескольких скидок с иконками режимов
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавлен API endpoint /pos/api/discounts/available/ для получения списка доступных скидок
- Добавлен метод DiscountApplier.apply_manual_discount() для применения ручных скидок
- Обновлен POS checkout для обработки manual_discount_id
- Расширена секция скидок в модальном окне:
* Отображение автоматических скидок (read-only)
* Dropdown для выбора скидки вручную
* Подробная детализация: подитог, общая скидка, скидки на позиции
* Поле промокода с иконкой
- Увеличен размер модального окна и изменено соотношение колонок (5/7)
- Убрана вертикальная прокрутка из модального окна
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Добавлен метод calculate_available_quantity() в модель ProductKit для точного расчёта максимального количества комплектов на основе свободных остатков компонентов
- Обновлён метод check_availability() для использования нового расчёта (обратная совместимость)
- Удалён устаревший сервис kit_availability.py
Исправлено отображение остатков комплектов:
- products_list.html: вместо прочерка показывается количество комплектов
- catalog.html: добавлено отображение доступного количества комплектов с цветовой индикацией
- POS terminal.js: в карточке товара показывается конкретное количество вместо общего 'В наличии'
Обновлены представления:
- ProductsListView: аннотирует комплекты атрибутом total_free
- CatalogView: рассчитывает доступное количество для каждого комплекта
- POS get_products(): убран хардкод, используется реальный расчёт по складу
- inventory/views/showcase.py: фильтр .exclude(status='reserved')
* Витринные букеты со статусом 'reserved' не отображаются в POS
* Защита от конфликтов: один букет - один заказ
- pos/views.py: фильтр .exclude(showcase_items__status='reserved')
* Showcase комплекты без доступных букетов скрыты в POS
* Фильтрация на уровне queryset для производительности
- Консистентная видимость витрины для всех кассиров
feat(inventory): introduce stock deficit notifications and base quantity tracking
- Added `quantity_base` field to reservation model for precise inventory calculations
- Implemented non-blocking stock deficit warnings during kit creation process
- Enhanced API responses with warning details for frontend display
- Updated terminal interface to show formatted stock shortage alerts
BREAKING CHANGE: API response structure now includes `warnings` array instead of previous stock warning format
- При создании OrderItem теперь передаётся sales_unit из данных корзины
- Это позволяет корректно рассчитывать total_amount для товаров с единицами продажи
- Исправлена ошибка когда сумма заказа была 0 при использовании единиц продажи
- Обновлён pos/views.py: метод pos_checkout теперь создаёт Order и связанную модель Delivery
- Обновлён showcase_manager.py: метод sell_showcase_item_to_customer использует новую архитектуру
- Удалён устаревший скрипт create_demo_orders.py
- Исправлена ошибка 'property is_delivery of Order object has no setter'
Проблема:
- free_qty передавался как float(decimal) в JSON API
- При конвертации Decimal→float терялась точность
- JavaScript показывал -0.050000000000044 вместо -0.05
Решение:
- free_qty теперь передаётся как строка: str(free_qty)
- Добавлено отдельное поле free_qty_sort (float) для сортировки
- После сортировки free_qty_sort удаляется из результата
- JavaScript parseFloat() корректно парсит строку без потери точности
Результат:
- Отображение остатков точное: -0.05 вместо -0.050000000000044
- Нет округления на фронте - видны реальные данные
- Сортировка по остаткам работает корректно
- Исправлен POS для использования миниатюр вместо оригиналов для быстрой загрузки
- Убран fallback на оригиналы - показываем миниатюру или ничего (лучше видно ошибки)
- Исправлен ImageService - возвращает пустую строку если миниатюра обработанного файла не найдена
- Исправлена ошибка JavaScript при массовом удалении фото (insertAdjacentElement на null)
- Добавлен контейнер photos-messages-container для надежного отображения сообщений
- Улучшено логирование ImageService для отладки путей к файлам
- Добавлена проверка exists() с детальным логированием в TenantAwareFileSystemStorage
- Изменён 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)
Теперь все временные метки заказов соответствуют реальному минскому времени
- Интегрирован готовый компонент ProductSearchPicker в модалку редактирования
- Добавлен collapse-блок с поиском товаров, который отображается только в режиме редактирования
- При выборе товара он автоматически добавляется в tempCart или увеличивается количество если уже есть
- Добавлен CSS и JS для компонента product-search-picker
- В view передаётся categories QuerySet для работы фильтров компонента
- Блок добавления товаров показывается только при редактировании, скрыт при создании нового комплекта
- Добавлены методы reserve_product_to_showcase и release_showcase_reservation в ShowcaseManager
- Методы работают с резервами для всех активных экземпляров витринного комплекта
- НЕ блокируют создание резерва при нехватке товара, возвращают информацию о дефиците (overdraft)
- Обновлён API endpoint update_product_kit для корректировки резервов при изменении состава
- Добавлено визуальное предупреждение на фронте о нехватке товара на складе
- В модалке редактирования комплекта добавлены контролы для изменения количества товаров (+/-, поле ввода, удаление)
- Автоматический пересчёт цен при изменении состава
- Очистка корзины POS после успешного создания витринного комплекта
Проблема:
Валидация showcase_items выполнялась в двух местах:
1. В pos/views.py - проверка status='in_cart' БЕЗ блокировки БД
2. В showcase_manager.py - перезагрузка с select_for_update() и повторная проверка
Это создавало:
- Дублирование кода и логики
- Возможность race condition между двумя запросами
- Избыточные обращения к БД
- Мертвый код (неработающие logger.info)
Решение (best practices):
1. Views только загружают объекты по ID без фильтров по статусу
2. ВСЯ валидация и бизнес-логика в одном месте - ShowcaseManager.sell_showcase_items
3. select_for_update() гарантирует актуальность данных и блокировку на уровне БД
4. Удален мертвый код (logger.info которые не выполнялись)
5. Убрано избыточное логирование ошибок валидации
Результат:
- Единое место ответственности (Single Responsibility)
- Нет дублирования
- Атомарная транзакция с блокировкой
- Чистый, понятный код без костылей
Для диагностики проблемы с продажей витринных комплектов добавлено
логирование ValidationError с полным traceback. Это поможет определить
в какой именно момент и почему происходит ошибка валидации.
Проблема: Продолжает возникать ошибка 'Один из экземпляров уже был продан'
при попытке продажи витринных букетов.
Изменения для диагностики:
1. Добавлена валидация showcase_item_ids ПЕРЕД передачей в sell_showcase_items
2. Проверка что все экземпляры имеют status='in_cart' и locked_by_user=текущий
3. Фильтр по статусу исключает уже проданные/разобранные экземпляры
4. Добавлено детальное логирование:
- Запрошенные showcase_item_ids
- Количество найденных заблокированных экземпляров
- Недостающие IDs если не все найдены
Улучшенное сообщение об ошибке:
Вместо 'уже был продан' теперь 'уже не заблокированы на вас' с просьбой
обновить страницу - более понятно для пользователя.
Логи помогут выявить:
- Передаются ли дубликаты в showcase_item_ids
- Истекают ли блокировки до момента продажи
- Меняется ли статус экземпляров между добавлением в корзину и checkout
Проблема: Витринные букеты исчезали из корзины после перезагрузки страницы,
но оставались заблокированными на пользователя через ShowcaseItem.
Решение:
- Изменена валидация корзины при загрузке из Redis
- Теперь проверяется наличие showcase_item_ids в данных корзины
- Блокировки валидируются через ShowcaseItem (не Reservation)
- Проверяется: status='in_cart', locked_by_user, cart_lock_expires_at > now()
- Обновляется qty на актуальное количество действующих блокировок
- Если ни один ShowcaseItem не заблокирован - не восстанавливается в корзину
Теперь витринные букеты корректно восстанавливаются при перезагрузке
и автоматически удаляются из корзины при истечении блокировки.
- Упрощено добавление в корзину: 1 клик = 1 шт (без prompt)
- API показывает все букеты (available + in_cart), не только доступные
- Карточка показывает available/total и сколько в корзине
- Корзина показывает реальное количество витринных букетов
- Кнопка "Очистить" сбрасывает блокировки и обновляет отображение
- API release-all-my-locks для сброса зависших блокировок
- Автоочистка истёкших блокировок при загрузке витрины
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Новая архитектура:
- ShowcaseItem модель - физический экземпляр букета на витрине
- OneToOneField(sold_order_item) - БД-уровневая защита от двойной продажи
- Поддержка создания нескольких экземпляров одного букета
- Возможность продавать N из M доступных (например 2 из 5)
Изменения:
- inventory/models.py: добавлена модель ShowcaseItem с методами lock/unlock/mark_sold
- inventory/services/showcase_manager.py: переработан для работы с ShowcaseItem
- pos/views.py: API поддерживает quantity и showcase_item_ids
- pos/templates/pos/terminal.html: поле "Сколько букетов создать"
- pos/static/pos/js/terminal.js: выбор количества, передача showcase_item_ids
Миграции:
- 0007: создание модели ShowcaseItem
- 0008: data migration существующих букетов
- 0009: очистка ShowcaseItem для уже проданных букетов
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Исправлен метод write_off_by_fifo() для учета зарезервированных партий
- Добавлено автоматическое проставление даты и времени при создании заказов в POS
- Исправлена ошибка фильтрации Product (is_active -> status='active') в transfers
- Предотвращает списание из зарезервированных партий, устраняя отрицательные остатки
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Проблема:
- Баланс кошелька клиента не отображался в модальном окне при нажатии "ПРОДАТЬ"
- Данные о балансе не передавались из backend в frontend
Исправления:
1. pos/views.py:
- Добавлен wallet_balance в selected_customer при загрузке из Redis
- Добавлен wallet_balance в system_customer
- Добавлен wallet_balance в API set_customer (Redis + response)
- Используется json.dumps() для корректной сериализации данных клиента
2. customers/views.py:
- Добавлен wallet_balance в API поиска клиентов (api_search_customers)
- Добавлен wallet_balance в API создания клиента (api_create_customer)
3. pos/static/pos/js/terminal.js:
- Обновлена функция selectCustomer() для получения walletBalance
- Обновлены все вызовы selectCustomer() для передачи баланса
- selectedCustomer теперь содержит wallet_balance
4. pos/templates/pos/terminal.html:
- Используются готовые JSON-строки из backend (system_customer_json, selected_customer_json)
- Исправлена проблема с локализацией чисел в JSON
Результат:
Баланс кошелька клиента теперь корректно отображается в модальном окне продажи
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Исправлена ошибка 'get_object_or_404 is not defined' при попытке
провести оплату через POS-терминал.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Добавлена интеграция оплаты в POS с поддержкой одиночной и смешанной оплаты,
работой с кошельком клиента и автоматическим созданием заказов.
Backend изменения:
- TransactionService: добавлены методы get_available_payment_methods() и create_multiple_payments()
для фильтрации способов оплаты и атомарного создания нескольких платежей
- POS API: новый endpoint pos_checkout() для создания заказов со статусом "Выполнен"
с обработкой платежей, освобождением блокировок и очисткой корзины
- Template tags: payment_tags.py для получения способов оплаты в шаблонах
Frontend изменения:
- PaymentWidget: переиспользуемый ES6 класс с поддержкой single/mixed режимов,
автоматической валидацией и интеграцией с кошельком клиента
- terminal.html: компактное модальное окно (70vw) с оптимизированной компоновкой,
удален функционал скидок, добавлен показ баланса кошелька
- terminal.js: динамическая загрузка PaymentWidget, интеграция с backend API,
обработка успешной оплаты и ошибок
Поддерживаемые способы оплаты: наличные, карта, онлайн, баланс счёта.
Смешанная оплата позволяет комбинировать несколько способов в одной транзакции.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Critical fix for Celery photo processing. The storage class now correctly
handles file reading operations by automatically adding tenant_id prefix
when opening files.
Problems fixed:
- Celery tasks could not open image files from storage
- PIL/Pillow couldn't locate files in tenant-specific directories
- temp file deletion was failing due to path validation
Changes:
- Added _open() method to add tenant_id prefix when opening files
- Added path() method to convert relative paths to full filesystem paths
- Updated delete() method to handle paths with or without tenant prefix
- All methods include security checks to prevent cross-tenant access
Testing:
- All 5 existing tests pass
- Verified photo processing task works end-to-end:
* Reads temp image file from disk
* Processes and creates all image versions
* Saves processed files to tenant-specific directory
* Cleans up temporary files correctly
- Files correctly stored in: media/tenants/{tenant_id}/products/{product_id}/{photo_id}/
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Remove select_related('locked_by_user') from select_for_update query
to avoid PostgreSQL error "FOR UPDATE cannot be applied to the
nullable side of an outer join". Username is now fetched via lazy load.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add select_for_update() with transaction.atomic() to prevent race condition
when multiple users try to add the same showcase kit simultaneously
- Validate cart lock expiration when loading cart from Redis - showcase kits
with expired locks are now filtered out instead of being displayed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix issue where temporary kits created for orders (with order field set
but showcase=NULL) were appearing in POS terminal showcase list.
Problem:
- When creating temporary kit from order edit page, it has order=<Order>
and showcase=NULL
- get_showcase_kits_for_pos() was showing ALL temporary kits with
reserved components, regardless of showcase field
- This caused order kits to appear in POS showcase view
- Users couldn't add them to cart (showcase=NULL check failed)
Solution:
- Add showcase__isnull=False filter to ProductKit query
- This ensures only kits explicitly assigned to showcases are shown
- Order kits (showcase=NULL) are now excluded from POS showcase list
Clear separation:
- Temporary kits for orders: order != NULL, showcase = NULL
- Temporary kits for showcases: showcase != NULL
File: pos/views.py:85
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## 1. Add cart lock validation to sell_from_showcase()
- Prevent selling showcase kits locked in another cashier's cart
- Check cart_lock_expires_at before allowing direct sales
- Return clear error message with lock holder's name and time remaining
- File: inventory/services/showcase_manager.py
## 2. Improve error handling in POS create_temp_kit_to_showcase()
- Add detailed logging for all error types (JSON, validation, generic)
- Provide user-friendly error messages instead of generic 500
- Log full context (kit name, showcase ID, items, user) for debugging
- Categorize errors: stock issues, integrity, locks, not found
- File: pos/views.py
## 3. Fix critical bug in create_temporary_kit()
- Replace non-existent is_active field with status='active'
- Affects 3 locations: kit creation, product lookup, kit duplication
- This was causing 500 errors when creating temporary kits from order edit
- File: products/services/kit_service.py
## 4. Improve error handling in create_temporary_kit_api()
- Add comprehensive logging for order creation endpoint
- Provide specific error messages for common failure scenarios
- Help diagnose issues when creating kits from order editing UI
- File: products/views/api_views.py
These changes complete the Soft Lock system and fix the 500 error issue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed:
- Replace is_active with status='active' for Product filtering in IncomingModelForm
- Product model uses status field instead of is_active
Added:
- Showcase field to ProductKit for tracking showcase placement
- product_kit field to Reservation for tracking kit-specific reservations
- Disassemble button in POS terminal for showcase kits
- API endpoint for kit disassembly (release reservations, mark discontinued)
- Improved reservation filtering when dismantling specific kits
Changes:
- ShowcaseManager now links reservations to specific kit instances
- POS terminal modal shows disassemble button in edit mode
- Kit disassembly properly updates stock aggregates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add is_default field to Showcase model with unique constraint per warehouse
- Implement Showcase.save() to ensure only one default per warehouse
- Add SetDefaultShowcaseView for AJAX-based default selection
- Update ShowcaseForm to include is_default checkbox
- Add interactive checkbox UI in showcase list with AJAX functionality
- Update POS API to return showcase.is_default instead of warehouse.is_default
- Update terminal.js to auto-select showcase based on its is_default flag
- Add migration for is_default field
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented Redis caching with 2-hour TTL for POS session data:
Backend changes:
- Added Redis cache configuration in settings.py
- Created save_cart() endpoint to persist cart state
- Added cart and customer loading from Redis in pos_terminal()
- Validates cart items (products/kits) still exist in DB
- Added REDIS_HOST, REDIS_PORT, REDIS_DB to .env
Frontend changes:
- Added saveCartToRedis() with 500ms debounce
- Cart auto-saves on add/remove/quantity change
- Added cart initialization from Redis on page load
- Enhanced customer button with two-line display and reset button
- Red X button appears only for non-system customers
Features:
- Cart persists across page reloads (2 hour TTL)
- Customer selection persists (2 hour TTL)
- Independent cart per user+warehouse combination
- Automatic cleanup of deleted items
- Debounced saves to reduce server load
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Аннотация товаров остатками (available_qty) и резервами (reserved_qty) через Subquery
- Компактный формат отображения: X(−Y) где X - доступно, Y - зарезервировано
- Визуальная стилизация: крупное число для остатков, мелкое для резервов
- Цветовая индикация: зелёный (≥5), жёлтый (<5), красный (≤0)
- Без дополнительных SQL-запросов, оптимизировано через подзапросы
- Убрана стартовая загрузка витринных комплектов (теперь только по API)
- showcase_kits_json теперь пустой массив на старте
- Витринные букеты загружаются динамически при клике на ВИТРИНА
- Оптимизирована get_showcase_kits_for_pos - устранены N+1 запросы
- Один запрос для всех резервов вместо N запросов на комплект
- Используется prefetch для kit_items (без дополнительных запросов)
- Добавлена группировка резервов в памяти вместо повторных обращений к БД
- Оптимизирована загрузка фото товаров и комплектов
- Используется Prefetch только для первого фото (thumbnail)
- Вместо photos.first() (который тянет все фото) - ограниченный queryset
- Prefetch с to_attr='first_photo_list' для минимизации запросов
- Результат: значительное сокращение нагрузки на БД при открытии POS
- Добавлены API endpoints для получения и обновления витринных комплектов
- GET /pos/api/product-kits/<id>/ - получение деталей комплекта
- POST /pos/api/product-kits/<id>/update/ - обновление комплекта
- Реализовано редактирование комплектов из POS интерфейса
- Кнопка редактирования (карандаш) на карточках витринных букетов
- Модальное окно предзаполняется данными комплекта
- Поддержка изменения состава, цен, описания и фото
- Умное управление резервами при изменении состава
- Введено изолированное состояние tempCart для модального окна
- Основная корзина (cart) больше не затрагивается при редактировании
- tempCart используется для создания и редактирования комплектов
- Автоочистка tempCart при закрытии модального окна
- Устранён побочный эффект загрузки состава комплекта в основную корзину
- Добавлен API endpoint GET /pos/api/showcase-kits/ для получения актуальных витринных букетов
- Изменена переменная SHOWCASE_KITS на изменяемую showcaseKits
- Добавлена функция refreshShowcaseKits() для обновления данных с сервера
- Кнопка ВИТРИНА теперь загружает свежие данные перед отображением
- После создания временного букета автоматически обновляется список и переключается вид на витрину
- Исправлена проблема с отображением только что созданных витринных букетов
- Added get_showcase_kits_for_pos() function to retrieve showcase kits with active reservations
- Modified POS terminal to show showcase kits when 'Витрина' button is clicked
- Showcase kits displayed as product cards with showcase name badge (🌺 icon)
- Added isShowcaseView flag to toggle between regular and showcase view modes
- Implemented distinct styling for active showcase button:
* Bright orange background (#ff6600)
* Black text for contrast
* Thicker border (3px)
* Enhanced shadow and scale effect (1.05)
- Showcase kits can be added to cart for sale from POS interface
- Добавлен API endpoint для создания временного комплекта из корзины
- Реализован endpoint получения списка активных витрин
- Создано модальное окно для настройки комплекта и выбора витрины
- JavaScript логика: валидация корзины, отправка данных, очистка после успеха
- Автоматическая генерация названия комплекта с датой и временем
- Агрегация дубликатов товаров в корзине перед созданием
- Резервирование компонентов на витрину через ShowcaseManager
- Расчёт и отображение итоговой цены комплекта