Commit Graph

250 Commits

Author SHA1 Message Date
0ed60954c4 UX: Удалена лишняя кнопка "Оплатить" из виджета оплаты
Убрана кнопка "Оплатить" внутри PaymentWidget, чтобы избежать путаницы.
Теперь есть только одна кнопка "Подтвердить продажу" внизу модального окна.

При смешанной оплате:
- Кнопка "Добавить платеж" для добавления способов оплаты
- Кнопка "Подтвердить продажу" для завершения (внизу модалки)

Это удобнее и безопаснее - пользователи не будут случайно нажимать
не ту кнопку при добавлении нескольких платежей.

Версия JS: v4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 20:42:48 +03:00
1cda9086d0 Реализована полноценная система оплаты для POS-терминала
Добавлена интеграция оплаты в 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>
2025-12-03 15:38:35 +03:00
9dab280def Рабочие изменения: улучшения UI, настройки и бэкенд авторизации
Собрал накопившиеся изменения из рабочей директории:

UI улучшения:
- customer_detail.html: Расширен интерфейс детальной страницы клиента
- order_detail.html: Добавлены элементы отображения деталей заказа
- order_list.html: Улучшена визуализация списка заказов

Бэкенд:
- customers/views.py: Доработаны представления для работы с клиентами
- products/views/product_views.py: Минорные правки
- user_roles/auth_backend.py: Добавлен кастомный бэкенд авторизации

Настройки:
- myproject/settings.py: Обновлены конфигурации
- .gitignore: Добавлен для игнорирования служебных файлов
- requirements.txt: Удален (вероятно заменен на poetry/pipenv)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 01:08:53 +03:00
dcfb76121d SECURITY: Защита критичных полей системных статусов от редактирования
Заблокировал изменение полей 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>
2025-12-03 01:07:15 +03:00
eef2cb820f refactor: remove unused cleanup_draft_orders management command
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>
2025-12-01 16:38:00 +03:00
702d42e943 Removed 'Not set' status option from order list dropdown 2025-12-01 12:19:16 +03:00
8a64b569bd Добавлены тесты для способов оплаты
Создан файл 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 тестов, все прошли успешно ✓
2025-12-01 01:30:23 +03:00
7188b11f65 Единый источник истины для способов оплаты
Проблема #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 - скрипт для существующих тенантов
2025-12-01 01:22:40 +03:00
e0437cdb5a Исправлено двойное списание товаров при смене статуса заказа
Проблема:
- При изменении статуса заказа на 'Выполнен' товар списывался дважды
- Заказ на 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 - документация по исправлению
2025-12-01 00:56:26 +03:00
920dbf4273 Добавлена защита от повторного списания + команда исправления дубликатов
Проблема: Сигнал 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>
2025-11-30 22:15:33 +03:00
4343b2eb5b Улучшен UI формы оплаты: красивый бейдж НЕ ОПЛАЧЕНО с пульсирующей анимацией, убран устаревший код смешанной оплаты, исправлены критичные ошибки JavaScript 2025-11-29 22:47:56 +03:00
9c6092262c Добавлена автоподстановка суммы для оплаты с кошелька и подсказка при нулевом балансе 2025-11-29 22:29:28 +03:00
4d197790fc Упрощена форма оплаты заказа: единая форма платежа/возврата с переключателем режимов 2025-11-29 22:23:42 +03:00
7d45106411 Убрана автоматическая обработка переплаты - только ручное управление 2025-11-29 20:42:58 +03:00
8cd2cfdb84 Уточнен текст предупреждения о переплате - объяснена автоматика 2025-11-29 20:40:37 +03:00
7c1780697a Добавлено свойство overpayment для корректного отображения переплаты 2025-11-29 20:38:15 +03:00
42eddc0fd1 Упрощена логика обработки переплаты - убрана автоматика из calculate_total
Проблема:
- calculate_total() пытался автоматически обрабатывать переплату
- Это приводило к дублированию логики и сложной отладке
- Нарушался принцип единственной ответственности

Решение:
- Удалена автоматическая обработка переплаты из Order.calculate_total()
- Теперь calculate_total() ТОЛЬКО считает сумму - всё
- Переплата обрабатывается ТОЛЬКО в TransactionService при создании платежей/возвратов
- Добавлено предупреждение в UI о переплате с инструкцией

Как работает теперь:
1. При оплате - TransactionService автоматически вызывает add_overpayment()
2. При изменении товаров - calculate_total() только пересчитывает сумму
3. Если появилась переплата - оператор видит предупреждение
4. Оператор вручную создаёт возврат в кошелёк через форму

Преимущества:
- Одно место ответственности за переплаты
- Прозрачность для оператора
- Нет скрытых автоматизмов
- Легко обслуживать и отлаживать
- Стандартный подход для e-commerce
2025-11-29 20:34:20 +03:00
575c5d0c2f Исправлена двойная обработка переплаты при изменении суммы заказа
Проблема:
- 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 (есть переплата)
- Это предотвращает двойную обработку при первичной оплате

Теперь переплата обрабатывается корректно только при изменении суммы заказа
2025-11-29 20:28:00 +03:00
a7ccbbec48 Исправлен баг с переплатой при изменении суммы заказа
Проблема:
- Создан заказ на 150 руб, оплачено 300 руб → 150 руб в кошелёк
- Изменены товары, сумма стала 100 руб
- amount_paid остался 300, total_amount стал 100
- Новая переплата 200 руб НЕ переносилась в кошелёк автоматически

Решение:
- В Order.calculate_total() добавлена проверка переплаты после пересчёта суммы
- Если amount_paid > total_amount, вызывается WalletService.add_overpayment()
- Излишек автоматически переносится в кошелёк, amount_paid нормализуется до total_amount
- Создаётся WalletTransaction для аудита

Теперь при уменьшении суммы заказа переплата корректно возвращается клиенту
2025-11-29 20:23:25 +03:00
3f22677573 Защита от переплаты кошельком и улучшение отображения транзакций
Изменения в UI (order_form.html):
- Добавлен data-code к опциям способов оплаты для идентификации метода кошелька
- ID для селекта способа оплаты (payment-method-select) и поля суммы (payment-amount-input)
- Динамическое ограничение max на поле суммы платежа при выборе кошелька
- Подсказка 'Макс: X руб.' отображается только для оплаты кошельком
- Для внешних методов (карта, наличные) ограничение отсутствует — переплата допустима

Логика JS:
- При выборе метода с code == 'account_balance' устанавливается max = order.amount_due
- Для остальных методов max удаляется — оператор может внести сумму больше остатка
- Переплата по внешним методам корректно зачисляется в кошелёк через WalletService.add_overpayment

Серверная защита (transaction_service.py):
- В TransactionService.create_payment добавлена проверка:
  если payment_method.code == 'account_balance' и amount > order.amount_due — ValidationError
- Сообщение: 'Сумма оплаты из кошелька (X руб.) не может превышать остаток к оплате (Y руб.)'
- Защита от обхода UI через API или прямой вызов

Улучшение отображения (order_form.html, order_detail.html):
- Для возврата в кошелёк (transaction_type == 'refund' и code == 'account_balance') показываем 'на баланс счёта' вместо названия метода
- История становится понятнее: '−750,00 руб. Возврат 29.11.2025 на баланс счёта'

Сценарий:
- Кошелёк клиента 500 руб., заказ 65 руб.
- Оператор выбирает оплату из кошелька — поле суммы ограничено 65 руб.
- Попытка ввести 500 заблокирована UI и серверной валидацией
- Для внешней оплаты (карта онлайн) можно внести 500 руб. — остаток 435 автоматически зачислится в кошелёк как переплата

Цель:
- Исключить путаницу в истории транзакций при оплате кошельком
- Разграничить поведение: кошелёк строго ограничен, внешние методы допускают переплату
- Обеспечить прозрачность движения средств для операторов
2025-11-29 16:54:24 +03:00
312cd808e6 Уточнение UI возвратов: пометка метода кошелька и корректный текст предупреждения
Изменения:
- Добавлена пометка '(кошелёк клиента)' к методу оплаты с кодом account_balance в селектах платежа и возврата
- Обновлён текст предупреждения о возврате: теперь явно указано, что зачисление в кошелёк происходит только при выборе метода 'кошелёк клиента'
- Для всех остальных методов (наличные, карта и т.п.) возврат — это информационная метка для истории, без фактического движения средств

Цель:
- Устранить путаницу операторов относительно поведения возвратов
- Чётко разделить возврат клиенту (внешними способами) и зачисление в кошелёк (только для account_balance)
- UI теперь соответствует фактической серверной логике в Transaction.save()

Защита от переплаты:
- Серверная валидация в TransactionService.create_refund проверяет amount <= order.amount_paid
- UI ограничение max на поле ввода суммы возврата
- ValidationError с понятным сообщением при попытке превысить лимит
2025-11-29 15:47:47 +03:00
2ec6d1935d Refactor order edit page layout and payment UI
- Move order form scope to left column only to avoid nested forms
- Place payment/refund forms in right column within same grid row
- Remove transaction delete button - use refunds instead for audit trail
- Simplify transaction history table: show only Date, Payment Method, Amount, and Created By
- Fix form submit buttons to use form attribute for proper association
- Improve visual alignment of two-column layout without empty gaps

This ensures valid HTML (no nested forms), clean financial audit history, and better UX with aligned columns.
2025-11-29 15:12:07 +03:00
c1351e1f49 Исправлена форма заказа: две колонки и корректная работа кнопки сохранения
- Разделен экран на две колонки: заказ слева, оплата справа
- Форма оплаты вынесена за пределы основной формы заказа (устранена проблема вложенных форм)
- Исправлен метод calculate_total() для сохранения итоговой суммы в БД
- Добавлена модель Transaction для учета платежей и возвратов
- Добавлена модель PaymentMethod для методов оплаты
- Удалена старая модель Payment, заменена на Transaction
- Добавлен TransactionService для управления транзакциями
- Обновлен интерфейс форм оплаты для правой колонки
- Кнопка 'Сохранить изменения' теперь работает корректно
2025-11-29 14:33:23 +03:00
438ca5d515 Move payment form back to Payment section (outside main form)\n\nPayment add form is now positioned right after the Payment card section,\nmaking it more logical - all payment-related UI is together.\n\nThe form remains OUTSIDE the main order form to avoid nested forms issue. 2025-11-29 02:34:08 +03:00
84ed3a0c7d Рефакторинг: отдельные endpoints для управления платежами (Django best practices)
ПРОБЛЕМА:
Использование PaymentFormSet для платежей было НЕПРАВИЛЬНЫМ подходом:
1. Платежи = финансовые транзакции (не должны редактироваться inline)
2. Формы валидировали существующие платежи как новые
3. Сложная логика с formset management forms
4. Конфликты валидации кошелька

РЕШЕНИЕ (Django Best Practices):
Разделили управление платежами на отдельные операции:

АРХИТЕКТУРА:
`
POST /orders/111/payments/add/          # Добавить платеж
POST /orders/111/payments/123/delete/   # Удалить платеж
`

ПРЕИМУЩЕСТВА:
 Чистая архитектура (separation of concerns)
 Платежи = неизменяемые транзакции
 Простая валидация (только для новых)
 Легко тестировать
 API-ready структура

ИЗМЕНЕНИЯ:

1. orders/views.py:
   - Убран PaymentFormSet из order_create и order_update
   - Добавлен payment_add(request, order_number)
   - Добавлен payment_delete(request, order_number, payment_id)
   - Используется простой PaymentForm вместо formset
   - Payment.save() автоматически обрабатывает:
     * Списание из кошелька
     * Обработку переплаты
     * Обновление amount_paid

2. orders/urls.py:
   - Добавлены URL patterns для payment-add и payment-delete
   - Структура: /orders/<number>/payments/add|<id>/delete/

3. orders/templates/orders/order_form.html:
   - Убран PaymentFormSet и все его скрипты (~265 строк)
   - Простая HTML форма для добавления платежа
   - Существующие платежи: read-only список с кнопками удаления
   - Каждое удаление = отдельный POST запрос
   - Для создания: показываем предупреждение вместо формы

4. orders/templatetags/orders_tags.py (NEW):
   - Template tag get_payment_methods
   - Загружает активные способы оплаты
   - Использование: {% get_payment_methods as payment_methods %}

РЕЗУЛЬТАТ:
- Код: -191 строка
- Логика: простая и понятная
- Архитектура: правильная (как в учебнике)
- Платежи: только add/delete (без edit)
- Валидация: работает корректно
- UX: чище и понятнее
2025-11-29 02:27:50 +03:00
ee002d5fed Исправить: кнопка сохранения заказа не работала (вложенная форма)
ПРОБЛЕМА:
После предыдущего коммита кнопка сохранения заказа перестала работать.
Клик на кнопку не приводил к отправке формы - ноль реакции.

ПРИЧИНА:
Вложенная форма удаления платежа внутри основной формы order-form.
Вложенные формы недопустимы в HTML и браузер неправильно обрабатывает
submit-события.

РЕШЕНИЕ:
Заменил вложенную форму на JavaScript обработчик:
- Кнопка удаления теперь type=button (не submit)
- Добавлены data-атрибуты: payment-id, payment-name, payment-amount
- JavaScript создает временную форму для POST-запроса с delete_payment_id
- Форма отправляется программно через form.submit()

ИЗМЕНЕНИЯ:
- Заменена форма на button для удаления платежей
- Добавлен JavaScript обработчик .delete-existing-payment-btn
- Подтверждение удаления с именем и суммой платежа

РЕЗУЛЬТАТ:
 Кнопка сохранения заказа работает
 Удаление существующих платежей работает
 Нет вложенных форм (валидный HTML)
2025-11-29 02:18:05 +03:00
f9e086fd89 Исправить: показывать существующие платежи информационно при редактировании заказа
ПРОБЛЕМА:
При редактировании заказа с уже существующими платежами из кошелька,
formset пытался валидировать ВСЕ платежи как новые, включая уже
проведенные. Это вызывало ошибки валидации кошелька, даже когда
пользователь просто хотел добавить новый платеж другим методом.

РЕШЕНИЕ:
Разделили отображение платежей на две части:

1. УЖЕ ПРОВЕДЕННЫЕ ПЛАТЕЖИ (информационный блок):
   - Показываются в виде read-only карточек (bg-light)
   - Не проходят через formset валидацию
   - Можно удалить через отдельную форму с POST-запросом
   - Содержат: способ оплаты, сумму, примечания, кнопку удаления

2. НОВЫЕ ПЛАТЕЖИ (formset):
   - Добавляются через кнопку 'Добавить платеж'
   - Проходят валидацию только для новых записей
   - Контейнер изначально пустой (#payments-container)

ИЗМЕНЕНИЯ:

orders/templates/orders/order_form.html:
- Добавлен блок 'Проведенные платежи' с информационным отображением
- Каждый существующий платеж с формой удаления (delete_payment_id)
- Контейнер для новых платежей теперь пустой при загрузке
- Обновлен calculatePaymentsTotal(): считает существующие + новые
- Убраны обработчики для несуществующих элементов formset
- Итоговая сумма инициализируется из order.amount_paid

orders/views.py (order_update):
- Добавлена обработка delete_payment_id из POST
- При удалении платежа из кошелька - возврат средств через WalletService
- Пересчет amount_paid после удаления
- Редирект обратно в форму после удаления

РЕЗУЛЬТАТ:
 Существующие платежи не валидируются повторно
 Можно свободно добавлять новые платежи любым методом
 Удаление существующих платежей работает корректно
 Возврат в кошелек при удалении платежа 'account_balance'
 Правильный подсчет итоговой суммы (существующие + новые)
2025-11-29 02:14:54 +03:00
65ab153f9e Исправить: дублирование обработки переплаты (баг с двойным возвратом в кошелек)
ПРОБЛЕМА:
При оплате заказа с переплатой (например, 15000 руб за заказ 7770 руб),
сдача возвращалась в кошелек клиента дважды:
- 1 раз: 7230 руб (правильно)
- 2 раз: 7230 руб (дубль!)
- ИТОГО: 14460 руб вместо 7230 руб

ПРИЧИНА:
Обработка переплаты вызывалась в двух местах:
1. Payment.save() → вызывал WalletService.add_overpayment() ✓
2. order_create/order_update в views.py → еще раз вызывал add_overpayment() ✗

РЕШЕНИЕ:
Убраны дублирующие вызовы WalletService.add_overpayment() из views.py.
Теперь переплата обрабатывается ТОЛЬКО в Payment.save() - это правильное
место, т.к. переплата появляется именно при сохранении нового платежа.

ИЗМЕНЕНИЯ:
- orders/views.py (order_create): убран вызов add_overpayment
- orders/views.py (order_update): убран вызов add_overpayment

Теперь при переплате сдача возвращается ровно 1 раз.
2025-11-29 02:07:36 +03:00
fa845ada29 миграции 2025-11-29 02:01:33 +03:00
cf1dce2621 Удалить поле discount_amount из модели Order
Убрано поле скидки из системы для последующей реализации полноценной системы скидок.

Изменения:
- Удалено поле discount_amount из модели Order
- Убрано из формы OrderForm
- Удалено из шаблонов order_form.html и order_detail.html
- Убрано из админки OrderAdmin
- Обновлен метод calculate_total() (без вычитания скидки)

В будущем будет создана отдельная модель Discount с промокодами, процентными скидками и автоматическими акциями.

ВАЖНО: После этого коммита нужно создать и применить миграцию:
  python manage.py makemigrations orders -n remove_discount_amount
  python manage.py migrate orders
2025-11-29 02:00:23 +03:00
a97fc39a2c Рефакторинг: убрана финализация черновиков и улучшены шаблоны заказов
- Убран черновик как отдельная сущность с процессом финализации
- Черновик теперь просто обычный OrderStatus
- Удалены кнопки 'Сохранить как черновик' и 'Финализировать черновик'
- Унифицирована логика сохранения/обновления заказов для всех статусов

Улучшения шаблонов:
- Стандартизировано форматирование валюты через floatformat:2
- Исправлено отображение статуса (используется OrderStatus.label и color)
- Исправлено отображение способа оплаты (корректное использование ForeignKey)
- Добавлены иконки к заголовкам секций для лучшего UX
- Удалены избыточные console.log (~160 строк)
- Очищены комментарии и улучшена читаемость кода
- Убрано использование переменной is_draft в контексте
- Добавлена визуальная согласованность между шаблонами заказов
2025-11-29 01:51:19 +03:00
9415aca63d Исправлена проблема с сохранением платежей и автоматический пересчёт статуса оплаты
- Добавлен префикс 'payments' для PaymentFormSet во всех представлениях
- Добавлен атрибут form='order-form' для динамически создаваемых полей платежей
- Убрано переопределение has_changed() в PaymentForm (использует стандартную логику Django)
- Автоматическая установка created_by для новых платежей
- Автоматический пересчёт payment_status при изменении суммы заказа
- Автоматическая обработка переплаты с возвратом в кошелёк клиента
- Убран весь отладочный код
2025-11-29 00:48:04 +03:00
a101d2919c fix: Payment formset not saving - fixed template replacement and has_changed()
Проблема: Платежи не сохранялись при создании/редактировании заказа.

Причины:
1. JavaScript функция addNewPayment() использовала неправильный метод
   замены __prefix__. При clone().innerHTML.replace() атрибуты name
   оставались с буквальным "__prefix__" вместо номера формы.

2. PaymentForm не переопределял has_changed(), из-за чего Django formset
   считал заполненные формы "пустыми" и не сохранял их.

Исправления:
- order_form.html: Переписана addNewPayment() - теперь клонирует
  template.content, конвертирует в HTML строку, делает replace,
  и только потом парсит обратно в DOM элемент

- forms.py: Добавлен метод PaymentForm.has_changed() который правильно
  определяет что форма заполнена если указан payment_method ИЛИ amount

- views.py: Добавлена отладочная информация для диагностики проблем
  с formset (TODO: удалить после тестирования)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 23:58:39 +03:00
ffdab80698 fix: Allow payment saving during new order creation
Fixed PaymentForm.clean() validation that was preventing payments from
being saved on new orders. The validation required order to exist, but
during creation self.instance.order is None until formset is saved.

Changes:
- Removed hard requirement for order in PaymentForm.clean()
- Wallet balance checks now only run when order exists
- Empty payment forms still allowed (for deletion in formset)
- Basic amount validation maintained

This fixes the issue where payments wouldn't persist when creating
a new order, even though no validation errors were shown to user.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 23:47:50 +03:00
39ab474a3c Fix: Order creation error - avoid calling reset_delivery_cost before items are saved
The error occurred because:
1. OrderForm.save(commit=False) was calling reset_delivery_cost()
2. reset_delivery_cost() uses DeliveryCostCalculator which accesses order.items
3. But items don't exist yet when order is not saved to DB

Solution:
- OrderForm.save() now only calls reset_delivery_cost() when commit=True
- order_create() explicitly calls reset_delivery_cost() AFTER saving items
- This ensures items exist in DB before delivery cost calculation

Error was: 'Order' instance needs to have a primary key value before this relationship can be used.
2025-11-28 23:34:53 +03:00
9a44c98e6e Simplify order creation and editing - remove autosave
- Removed autosave.js (665 lines) and draft-creator.js (441 lines)
- Removed draft_service.py (~500 lines) and DraftOrderService
- Removed AJAX endpoints: autosave and create-draft
- Updated order_create() to add is_create_page flag
- Updated order_update() to finalize drafts without DraftOrderService
- Added get_new_status() method to OrderStatusService
- Updated order_form.html:
  - Removed old JS includes
  - Added beforeunload warning for unsaved data
  - Updated buttons: separate buttons for create/draft/finalize
- Total code reduction: ~1600 lines (92% removed)

New workflow:
- /orders/create/ - user fills form, chooses button
- /orders/<id>/edit/ - simple editing without autosave
- beforeunload warning when leaving page (except on submit)
2025-11-28 23:29:19 +03:00
f911a57640 Before simplifying order creation and editing 2025-11-28 23:11:34 +03:00
82ed5a409e Добавлена функциональность редактирования заказов с обновлением резервов товаров 2025-11-27 21:13:42 +03:00
c62cdb0298 feat: Add customer prefill from URL parameter in order creation
- Modified order_create view to read customer from GET parameter
- Pass preselected_customer to template context
- Template renders select with preselected option for Select2
- Fixed draft creation timing with callback after Select2 initialization
- Auto-create draft when customer is preselected from URL
- Graceful handling if customer not found or invalid ID
2025-11-27 00:17:02 +03:00
5ead7fdd2e Реализация системы кошелька клиента для переплат
- Добавлено поле wallet_balance в модель Customer
- Создана модель WalletTransaction для истории операций
- Реализован сервис WalletService с методами:
  * add_overpayment - автоматическое зачисление переплаты
  * pay_with_wallet - оплата заказа из кошелька
  * adjust_balance - ручная корректировка баланса
- Интеграция с Payment.save() для автоматической обработки переплат
- UI для оплаты из кошелька в деталях заказа
- Отображение баланса и долга на странице клиента
- Админка с inline транзакций и запретом ручного создания
- Добавлен способ оплаты account_balance
- Миграция 0004 для customers приложения
2025-11-26 14:47:11 +03:00
0653ec0545 Рефакторинг моделей заказов и добавление методов оплаты 2025-11-26 13:38:02 +03:00
08e8409a66 Fix: Restore checkbox values on page reload for recipient fields
Adds proper initialization for checkbox fields on page load:
- address_confirm_with_recipient (Уточнить адрес у получателя)
- customer_is_recipient (Покупатель является получателем)

These fields are already tracked by autosave.js and properly saved
by backend, but were not displaying saved values after page reload.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 06:34:44 +03:00
3b4785e2ad Fix: Implement proper deletion of order items with confirmation dialog
Fixes deletion functionality for order items across frontend and backend:
- Remove restriction preventing deletion of last item
- Add confirmation dialog before deletion
- Properly track and send deleted item IDs to backend via autosave
- Update backend to handle item deletion by ID instead of index
- Fix visual feedback: deleted items are hidden immediately
- Auto-recalculate total sum after deletion

Technical changes:
- order_form.html: Add confirmation dialog, trigger autosave on delete
- autosave.js: Collect deleted item IDs, send to backend
- draft_service.py: Process deleted_item_ids, update items by ID

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 06:27:27 +03:00
5df182e030 Fix: Auto-fill product prices when empty or zero in autosave
Исправлена проблема с пропаданием цен товаров при автосохранении и
перезагрузке страницы.

ПРОБЛЕМА:
- При автосохранении пустые поля цен отправлялись как '0'
- Backend сохранял 0 в базу данных
- При перезагрузке страницы поля цен оставались пустыми
- Итоговая сумма товаров показывала 0.00 руб.

РЕШЕНИЕ 1 - Backend (draft_service.py):
- Изменена логика обработки цен в update_draft()
- Если цена пустая или равна 0, используется actual_price из каталога
- Добавлена корректная обработка price_raw перед конвертацией в Decimal
- Улучшена логика определения is_custom_price

Логика обработки цены:
1. Получаем price_raw из items_data
2. Если price_raw пустой или 0 → используем original_price из каталога
3. Если price_raw заполнен → используем его и сравниваем с original_price
4. is_custom_price = True только если разница больше 0.01

РЕШЕНИЕ 2 - Frontend (order_form.html):
- Добавлена fallback логика в шаблоне для отображения цены
- Если item_form.instance.price пустой/None/0 → показываем actual_price
- Используется inline условие {% if %} для проверки наличия цены
- Отдельная логика для product и product_kit

Теперь работает корректно:
 При выборе товара цена автоматически заполняется из каталога
 Автосохранение сохраняет правильную цену (из каталога или изменённую)
 При перезагрузке страницы цены отображаются корректно
 Итоговая сумма товаров рассчитывается правильно
 Бейдж "Изменена" показывается только для реально изменённых цен

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 20:15:50 +03:00
80260c8a34 Fix: Restore delivery address and order items data on page reload
Исправлены две критические проблемы с автосохранением и восстановлением
данных при перезагрузке страницы редактирования заказа:

1. ПРОБЛЕМА: Адрес доставки не восстанавливался после перезагрузки
   РЕШЕНИЕ (forms.py):
   - Добавлена инициализация полей адреса в OrderForm.__init__()
   - Поля заполняются из order.delivery_address при редактировании
   - Инициализируются все поля: улица, дом, квартира, подъезд, этаж, домофон, инструкции

2. ПРОБЛЕМА: Цены и количество товаров не сохранялись через автосохранение
   РЕШЕНИЕ (draft_service.py):
   - Добавлена обработка items в DraftOrderService.update_draft()
   - Автосохранение теперь обновляет/создаёт/удаляет позиции заказа
   - Сохраняются: product/product_kit, quantity, price, is_custom_price
   - Корректно определяется is_custom_price через сравнение с оригинальной ценой

Логика обработки items:
- Существующие позиции обновляются (product, quantity, price)
- Новые позиции создаются
- Лишние позиции удаляются
- Поддержка как товаров (product_id), так и комплектов (product_kit_id)

Теперь при перезагрузке страницы:
 Адрес доставки полностью восстанавливается во всех полях
 Товары сохраняются с правильными ценами и количествами
 Изменённые цены корректно отмечаются бейджем "Изменена"
 Все данные синхронизируются между автосохранением и базой данных

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 19:57:32 +03:00
a3f5ac4c08 Fix: Enable autosave for delivery address fields with partial data
Исправлена проблема с автосохранением полей адреса доставки. Теперь адрес
сохраняется при заполнении любого поля, а не только при наличии улицы и
номера дома.

Изменения в Backend (address_service.py):
- Изменена логика валидации в process_address_from_form()
- Теперь адрес сохраняется если заполнено хотя бы одно поле
- Удалено жёсткое требование заполнения street и building_number
- Проверяем наличие данных во всех полях адреса

Изменения в Frontend (autosave.js):
- Переработана логика сбора данных адреса
- address_mode='new' устанавливается при заполнении любого поля адреса
- Все непустые поля адреса отправляются на сервер
- Используется более элегантный подход с Object.values() и .some()

Теперь автосохранение работает корректно:
- Заполнение поля "Улица" → автосохранение срабатывает
- Заполнение "Номер дома" → автосохранение срабатывает
- Заполнение любого другого поля адреса → автосохранение срабатывает
- После перезагрузки страницы все данные восстанавливаются

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 19:49:58 +03:00
fbc9d86a62 Fix: Set original price for existing order items on page load
Исправлена проблема с отображением бейджа "Изменена" для существующих
товаров в заказе. Теперь при загрузке страницы для всех существующих
позиций устанавливается атрибут data-original-price с актуальной ценой
из каталога, что позволяет корректно отслеживать изменения цены.

Изменения:
- Добавлена условная логика рендеринга поля price с атрибутом data-original-price
- Для товаров используется item_form.instance.product.actual_price
- Для комплектов используется item_form.instance.product_kit.actual_price
- Бейдж "Изменена" теперь работает одинаково для всех форм

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 19:42:18 +03:00
436cac28ed Feat: Add order items total sum display with real-time calculation
Добавлено отображение общей суммы всех товаров в заказе с автоматическим
обновлением в реальном времени при изменении количества, цены, добавлении
или удалении товаров.

Изменения:
- HTML: Добавлен блок отображения итоговой суммы товаров под списком позиций
- JavaScript: Реализованы функции calculateOrderItemsTotal() и updateOrderItemsTotal()
- Интеграция: Подключены слушатели событий на поля quantity и price
- Обновление суммы происходит при:
  * Изменении количества или цены товара
  * Добавлении новой позиции
  * Удалении позиции
  * Загрузке страницы

Сумма автоматически исключает удалённые формы и корректно обрабатывает
пустые значения. Форматирование: 2 знака после запятой.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 19:39:03 +03:00
fdd14804a7 Улучшена таблица статусов заказов: заменены иконки Font Awesome на Bootstrap Icons, увеличены бейджи, полные названия типов и итогов, кнопки действий только с иконками 2025-11-19 00:53:16 +03:00
a1888b7745 Optimize order statuses list page with compact card layout
- Changed from table to card-based design for better space efficiency
- Reduced padding and margins to fit 15+ statuses on screen without scrolling
- Minimized font sizes and icon sizes for compact display
- Added proper styling for edit and delete buttons with hover effects
- Improved visual hierarchy with color indicators and badges
- Maintained all functionality while improving UX

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 23:00:29 +03:00