- Добавлены системные статусы partially_assembled и fully_assembled в order_status_service.py
- Создана management команда update_order_statuses для обновления статусов у всех тенантов
- Новые статусы интегрируются в существующую логику резервирования и списания товара
- Статусы располагаются между 'В сборке' и 'В доставке' в естественном порядке процесса
Добавлено:
- Команда 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 теперь предлагает пользователю инициализировать системные данные после очистки
- Добавлена детальная информация о процессе очистки и инициализации данных
Проблема:
На странице списка заказов (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 на обеих страницах (форма редактирования + список)!
Проблема:
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.
Проблема: При переводе заказа в статус '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>
При выборе способа оплаты сумма теперь автоматически выделяется,
позволяя пользователю сразу начать вводить новое значение без
необходимости вручную выделять текст.
Улучшение скорости ввода для оператора 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>
Добавлена интеграция оплаты в 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>
The cleanup_draft_orders command was no longer needed in the project.
Also updated test output file with latest test results.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Создан файл orders/tests/test_payment_methods.py с комплексными тестами:
1. PaymentMethodCreationTest (6 тестов)
- Проверка создания всех 5 способов оплаты через команду
- Проверка системных флагов и активности
- Проверка правильности порядка сортировки
- Проверка идемпотентности команды
- Критический тест наличия account_balance
2. PaymentMethodMultiTenantTest (2 теста)
- Проверка изоляции данных между тенантами
- Проверка кастомных способов оплаты в разных тенантах
3. PaymentMethodTransactionTest (7 тестов)
- Проверка связи PaymentMethod.transactions
- Проверка создания транзакций
- Проверка изоляции транзакций по способам оплаты
- Проверка защиты от удаления (PROTECT)
- Критический тест использования account_balance
- Исправление бага obj.payments → obj.transactions
4. PaymentMethodOrderingTest (2 теста)
- Проверка сортировки по полю order
- Проверка что account_balance первый (order=0)
Особенности тестирования:
- Использование TenantTestCase для изоляции тенантов
- Использование TransactionTestCase для мультитенантных тестов
- Ручное создание/удаление схем для безопасности
- Проверка изоляции данных между схемами
Результат: 15 тестов, все прошли успешно ✓
Проблема #1: Дублирование кода способов оплаты
- В tenants/admin.py был полный список способов оплаты (45 строк)
- В orders/management/commands/create_payment_methods.py был другой список
- При создании тенанта отсутствовал способ 'account_balance'
- Нарушение DRY принципа
Решение: Single Source of Truth
- Единственный источник истины: команда create_payment_methods
- В tenants/admin.py заменено дублирование на call_command()
- Удалено 45 строк дублирующего кода
- Теперь все тенанты получают одинаковый полный список
Проблема #2: AttributeError в админке PaymentMethod
- obj.payments.count() вызывал ошибку
- В модели Transaction связь называется 'transactions', а не 'payments'
Решение: Исправлено в orders/admin.py
- obj.payments → obj.transactions (2 места)
- Админка PaymentMethod теперь работает корректно
Для тенанта buba:
- Создан скрипт add_payment_methods_to_buba.py
- Добавлен недостающий способ оплаты 'С баланса счёта'
Изменённые файлы:
- myproject/tenants/admin.py - вызов команды вместо дублирования
- myproject/orders/admin.py - исправлено на transactions
- add_payment_methods_to_buba.py - скрипт для существующих тенантов
Проблема:
- При изменении статуса заказа на 'Выполнен' товар списывался дважды
- Заказ на 10 шт создавал Sale на 10 шт, но со склада уходило 20 шт
Найдено ДВЕ причины:
1. Повторное обновление резервов через .save() (inventory/signals.py)
- Резервы обновлялись через res.save() каждый раз при сохранении заказа
- Это вызывало сигнал update_stock_on_reservation_change
- При повторном сохранении заказа происходило двойное срабатывание
Решение:
- Проверка дубликатов ПЕРЕД обновлением резервов
- Замена .save() на .update() для массового обновления без вызова сигналов
- Ручное обновление Stock после .update()
2. Двойное FIFO-списание (inventory/services/sale_processor.py)
- Sale создавалась с processed=False
- Сигнал process_sale_fifo срабатывал и списывал товар (1-й раз)
- Затем SaleProcessor.create_sale() тоже списывал товар (2-й раз)
Решение:
- Sale создаётся сразу с processed=True
- Сигнал не срабатывает, списание только в сервисе
Дополнительно:
- Ограничен выбор статусов при создании заказа только промежуточными
- Статус 'Черновик' установлен по умолчанию
- Убран пустой выбор '-------' из поля статуса
Изменённые файлы:
- myproject/orders/forms.py - настройки статусов для формы заказа
- myproject/inventory/signals.py - исправление сигнала create_sale_on_order_completion
- myproject/inventory/services/sale_processor.py - исправление create_sale
- myproject/test_order_status_default.py - обновлён тест
- DOUBLE_SALE_FIX.md - документация по исправлению
Проблема: Сигнал post_save срабатывает несколько раз,
создавая дубликаты Sale для одного заказа.
Решения:
1. Добавлена проверка Sale.objects.filter(order=instance).exists()
перед созданием продаж (inventory/signals.py:74-75)
2. Создана management команда fix_duplicate_sales для исправления
существующих дубликатов
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Проблема:
- calculate_total() пытался автоматически обрабатывать переплату
- Это приводило к дублированию логики и сложной отладке
- Нарушался принцип единственной ответственности
Решение:
- Удалена автоматическая обработка переплаты из Order.calculate_total()
- Теперь calculate_total() ТОЛЬКО считает сумму - всё
- Переплата обрабатывается ТОЛЬКО в TransactionService при создании платежей/возвратов
- Добавлено предупреждение в UI о переплате с инструкцией
Как работает теперь:
1. При оплате - TransactionService автоматически вызывает add_overpayment()
2. При изменении товаров - calculate_total() только пересчитывает сумму
3. Если появилась переплата - оператор видит предупреждение
4. Оператор вручную создаёт возврат в кошелёк через форму
Преимущества:
- Одно место ответственности за переплаты
- Прозрачность для оператора
- Нет скрытых автоматизмов
- Легко обслуживать и отлаживать
- Стандартный подход для e-commerce
Проблема:
- add_overpayment вызывался дважды:
1. При оплате 300 руб (заказ 150) → +150 в кошелёк
2. При изменении суммы до 100 → +200 в кошелёк
- Итого: 350 руб вместо правильных 200 руб
Причина:
- calculate_total() вызывал add_overpayment при любой переплате
- Не учитывалось, что переплата уже была обработана при оплате
Решение:
- Сохраняем old_total перед пересчётом
- Вызываем add_overpayment ТОЛЬКО если:
- old_total > 0 (заказ уже существовал)
- total_amount < old_total (сумма УМЕНЬШИЛАСЬ)
- amount_paid > total_amount (есть переплата)
- Это предотвращает двойную обработку при первичной оплате
Теперь переплата обрабатывается корректно только при изменении суммы заказа