Новая архитектура:
- 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>
Проблема: При переводе заказа в статус 'completed' возникала ошибка
"Не удалось создать Sale для заказа", т.к. резервы этого же заказа
блокировали списание товара.
Причина: write_off_by_fifo() считал все резервы со статусом 'reserved'
как занятые, включая резервы текущего заказа, которые ещё не были
переведены в 'converted_to_sale'.
Решение:
- Добавлен параметр exclude_order в write_off_by_fifo() для исключения
резервов конкретного заказа из расчёта занятого товара
- SaleProcessor.create_sale() теперь передаёт order в write_off_by_fifo()
- Добавлены транзакции в views для атомарности операций с заказами:
при ошибке в сигналах статус заказа откатывается вместе со всеми
связанными изменениями
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Проблема: при отмене заказа с витринным временным комплектом резервы освобождались
(status='released'), и букет исчезал с витрины. Это неправильное поведение для временных
комплектов на витрине - они должны оставаться доступными для продажи.
Решение:
- В сигнале rollback_sale_on_status_change добавлено разделение резервов на:
* Обычные резервы - работают как раньше (released при отмене, reserved при возврате)
* Витринные временные комплекты (is_temporary=True, showcase!=null) - ВСЕГДА возвращаются
в статус reserved, независимо от типа отката заказа
- Для витринных комплектов сохраняются привязки к showcase и product_kit
- Букеты остаются видимыми на витрине и доступны для повторной продажи
Бизнес-логика:
- При ВОЗВРАТЕ (completed → draft/in_delivery): букет возвращается на витрину
- При ОТМЕНЕ (completed → cancelled): букет ТАКЖЕ возвращается на витрину
- Букет можно убрать только вручную через функцию разбора комплекта
- Добавлена специальная обработка витринных комплектов в сигнале update_reservation_on_item_change:
* При создании OrderItem с витринным комплектом привязываются существующие витринные резервы компонентов
* Не создаются новые резервы на уровне комплекта
- Исправлена логика создания Sale для комплектов в сигнале create_sale_on_order_completion:
* Для комплектов (витринных и обычных) создаются Sale для каждого компонента через резервы
* Используется FIFO-списание для компонентов
* Предотвращена ошибка передачи ProductKit в поле Reservation.product
Fixes: Cannot assign ProductKit to Reservation.product field
Fixes: Не удалось создать Sale для заказа с витринным комплектом
- Добавлена поддержка docker-compose для развертывания
- STATIC_ROOT автоматически переключается в prod (/Volume1/DockerAppsData/npm/data/static/)
- Добавлены ALLOWED_HOSTS и CSRF_TRUSTED_ORIGINS из env переменных
- Улучшена обработка .env файла (проверка существования)
- Добавлен gunicorn в requirements.txt
- Добавлены .dockerignore, Dockerfile, docker-compose.yml
- Добавлены example файлы для .env.docker и entrypoint.sh
- Обновлен .gitignore для исключения файлов с секретами
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Добавлены методы в модель Customer для расчета суммы успешных заказов:
- get_successful_orders_total() - гибкий метод с фильтрацией по датам
- get_last_year_orders_total() - сумма за последний год
Удалено устаревшее поле total_spent:
- Методы предоставляют более точные и актуальные данные
- Используют агрегацию на уровне БД для производительности
Обновлен UI карточки клиента:
- Отображается сумма всех успешных заказов
- Отображается сумма заказов за последний год
- Убрана колонка total_spent из списка клиентов
Изменения:
- customers/models.py: добавлены методы, удалено поле total_spent
- customers/views.py: добавлен расчет сумм в контекст
- customers/templates: обновлены шаблоны
- customers/admin.py: удалены упоминания total_spent
- Создана миграция 0005_remove_total_spent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <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>
Проблема:
- При сбросе клиента на системного в модальном окне продажи баланс кошелька
в виджете оплаты (возле кнопки "С баланса счёта") не обновлялся
- Виджет PaymentWidget сохранял данные предыдущего клиента
Исправления:
- Добавлена функция updatePaymentWidgetCustomer() для переинициализации виджета
- Функция updateCheckoutWalletBalance() теперь вызывает updatePaymentWidgetCustomer()
- При смене клиента виджет оплаты автоматически переинициализируется с новыми данными
Результат:
При смене клиента баланс кошелька обновляется везде, включая виджет оплаты
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Проблема:
- При сбросе клиента на системного в модальном окне продажи баланс кошелька
оставался от предыдущего клиента и не обновлялся
Исправления:
- Добавлена функция updateCheckoutWalletBalance() для обновления баланса
- Функция updateCustomerDisplay() теперь вызывает updateCheckoutWalletBalance()
- Исправлены все кнопки сброса/выбора системного клиента - теперь передают wallet_balance
Результат:
При смене клиента баланс кошелька в модальном окне обновляется корректно
🤖 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>
При выборе способа оплаты сумма теперь автоматически выделяется,
позволяя пользователю сразу начать вводить новое значение без
необходимости вручную выделять текст.
Улучшение скорости ввода для оператора POS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Полностью переработан интерфейс смешанной оплаты:
НОВЫЙ UI:
- Построчное добавление платежей с галочкой подтверждения
- Каждая строка: [Способ ▼] [Сумма] [✓] [🗑️]
- Зафиксированные платежи показываются отдельно с зеленой галочкой
- Кнопка "+ Добавить еще часть платежа" (скрывается при остатке = 0)
- Индикатор "Осталось оплатить" / "Оплачено полностью"
ФУНКЦИОНАЛЬНОСТЬ:
✅ Автоподстановка остаточной суммы при выборе способа оплаты
✅ Автофокус на поле суммы после выбора способа
✅ Inline валидация с красными подсказками под полем
✅ Проверка превышения остатка и баланса кошелька
✅ Удаление любого платежа (зафиксированного или нет)
✅ Автодобавление пустой строки при удалении всех платежей
ИЗМЕНЕНИЯ В КОДЕ:
+ Новые методы: addPaymentRow(), renderPaymentRows(), attachPaymentRowEvents()
+ Новые методы: confirmPaymentRow(), removePaymentRow(), updateRemainingHint()
~ Обновлены: render(), getTotalPayments(), validate(), submit(), attachEvents()
~ getTotalPayments() теперь учитывает только зафиксированные платежи (fixed: true)
- Удалены старые методы: addPayment(), removePayment(), updatePaymentsList(), renderPaymentsList()
SINGLE MODE остался без изменений.
Проблема решена: больше невозможно "забыть" добавить последний платеж,
так как каждый платеж фиксируется галочкой явно.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Убрано лишнее поле "Примечание" из PaymentWidget:
- Удалено HTML-поле для ввода примечания
- Убраны все обращения к notesInput в коде
- Примечания теперь передаются как пустая строка
Для POS-терминала это поле избыточно и только замедляет процесс оплаты.
Интерфейс стал проще и быстрее.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
1. Убран параметр версии ?v=4 из подключения terminal.js (для разработки не нужен)
2. Исправлена логика автоподстановки суммы в PaymentWidget:
- Теперь при выборе способа оплаты подставляется ОСТАТОЧНАЯ сумма
- Остаток = amount_due - уже добавленные платежи
- Добавлена подсказка "Осталось оплатить: X руб."
Пример:
- Заказ на 30 руб.
- Добавили платеж 10 руб. наличными
- Выбираем картой → автоматически подставится 20 руб. (а не 30!)
Это предотвращает ошибки и переплаты при смешанной оплате.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Убрана кнопка "Оплатить" внутри PaymentWidget, чтобы избежать путаницы.
Теперь есть только одна кнопка "Подтвердить продажу" внизу модального окна.
При смешанной оплате:
- Кнопка "Добавить платеж" для добавления способов оплаты
- Кнопка "Подтвердить продажу" для завершения (внизу модалки)
Это удобнее и безопаснее - пользователи не будут случайно нажимать
не ту кнопку при добавлении нескольких платежей.
Версия JS: v4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Исправления:
1. Удален вызов несуществующей функции updateCartCount()
2. Добавлена автоматическая перезагрузка страницы через 500ms после успешной продажи
3. Добавлены console.log для отладки процесса продажи
Теперь после успешной продажи:
- ✅ Заказ создается
- ✅ Корзина очищается
- ✅ Модалка закрывается
- ✅ Страница автоматически перезагружается
- ✅ Остатки товаров обновляются
- ❌ Никаких ошибок в консоли
Версия JS: v3
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
1. Добавлен параметр ?v=2 к terminal.js для принудительной загрузки новой версии
2. Добавлен вызов loadItems() после успешной продажи для обновления списка товаров
Теперь после продажи:
- Корзина очищается
- Список товаров слева автоматически обновляется (показывает актуальные остатки)
- Витринные комплекты перезагружаются
- Не требуется ручное обновление страницы
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Заменен вызов несуществующей функции updateCartUI() на корректный renderCart().
Теперь после успешной оплаты корзина корректно очищается и обновляется в UI.
🤖 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>
Исправлена ошибка 'currentWarehouse is not defined' при проведении продажи.
Добавлен JSON блок currentWarehouseData в template и инициализация
переменной currentWarehouse в terminal.js.
🤖 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>
Заблокировал изменение полей is_positive_end и is_negative_end для
системных статусов заказов, так как эти флаги используются в сигналах
inventory для управления резервированием и списанием товаров со склада.
Что изменено:
- OrderStatusForm: Добавлена блокировка (disabled=True) для полей
is_positive_end и is_negative_end при редактировании системных статусов
- status_form.html: Заменено информационное предупреждение на красное
с детальным описанием заблокированных полей и их влияния на систему
Почему это критично:
Эти флаги проверяются в 3 сигналах inventory/signals.py:
1. rollback_sale_on_status_change - откатывает продажи при уходе от 'completed'
2. release_reservations_on_cancellation - освобождает резервы при отмене
3. reserve_stock_on_uncancellation - резервирует при восстановлении заказа
Случайное изменение флагов может привести к:
- Неправильному освобождению резервов товара
- Двойному резервированию
- Блокировке товара навсегда
- Списанию товара для отмененных заказов
Разрешено редактировать для системных статусов: name, label, color, description
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>