From f9e086fd89c99de1c0a1c41de3cd9d14b22e3287 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Sat, 29 Nov 2025 02:14:54 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C:=20=D0=BF=D0=BE=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D1=81=D1=83=D1=89=D0=B5=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D1=83=D1=8E=D1=89=D0=B8=D0=B5=20=D0=BF=D0=BB=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=B6=D0=B8=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=BE=D0=BD=D0=BD=D0=BE=20=D0=BF=D1=80=D0=B8=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B8=20=D0=B7=D0=B0=D0=BA=D0=B0=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ПРОБЛЕМА: При редактировании заказа с уже существующими платежами из кошелька, 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' ✅ Правильный подсчет итоговой суммы (существующие + новые) --- .../orders/templates/orders/order_form.html | 89 +++++++++---------- myproject/orders/views.py | 25 ++++++ 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/myproject/orders/templates/orders/order_form.html b/myproject/orders/templates/orders/order_form.html index 08004f5..4bcab8f 100644 --- a/myproject/orders/templates/orders/order_form.html +++ b/myproject/orders/templates/orders/order_form.html @@ -608,55 +608,54 @@ {{ payment_formset.management_form }} - -
- {% for payment_form in payment_formset %} -
- {{ payment_form.id }} - {{ payment_form.DELETE }} - -
+ + {% if order.pk and order.payments.exists %} +
+
Проведенные платежи
+ {% for payment in order.payments.all %} +
+
-
- - {{ payment_form.payment_method }} -
+ Способ оплаты + {{ payment.payment_method.name }}
-
- - {{ payment_form.amount }} -
+ Сумма + {{ payment.amount|floatformat:2 }} руб.
-
- - {{ payment_form.notes }} -
+ Примечания + {{ payment.notes|default:"—" }}
-
- +
+
+ {% csrf_token %} + + +
- - {% if payment_form.errors %} -
{{ payment_form.errors }}
- {% endif %}
{% endfor %}
+ {% endif %} + + +
+ +
-

Внесено платежей:

+

Всего внесено:

- 0.00 руб. + {{ order.amount_paid|default:"0.00"|floatformat:2 }} руб.
@@ -1631,20 +1630,29 @@ document.addEventListener('DOMContentLoaded', function() { // Функция для расчета итоговой суммы платежей function calculatePaymentsTotal() { + // 1. Считаем уже сохраненные платежи (информационные блоки) + let existingTotal = 0; + const existingPayments = document.querySelectorAll('.bg-light .text-success'); + existingPayments.forEach((el) => { + const text = el.textContent.replace(/[^0-9.,]/g, '').replace(',', '.'); + const amount = parseFloat(text) || 0; + existingTotal += amount; + }); + + // 2. Считаем новые платежи в formset const visiblePaymentForms = Array.from(document.querySelectorAll('.payment-form')) .filter(form => !form.classList.contains('deleted')); - let total = 0; - + let newTotal = 0; visiblePaymentForms.forEach((form) => { const amountField = form.querySelector('[name$="-amount"]'); if (amountField) { const amount = parseFloat(amountField.value.replace(',', '.')) || 0; - total += amount; + newTotal += amount; } }); - return total; + return existingTotal + newTotal; } function updatePaymentsTotal() { @@ -1735,19 +1743,6 @@ document.addEventListener('DOMContentLoaded', function() { addPaymentBtn.addEventListener('click', addNewPayment); } - // Добавляем обработчики удаления для существующих платежей - paymentsContainer.querySelectorAll('.remove-payment-btn').forEach(btn => { - btn.addEventListener('click', function() { - const form = this.closest('.payment-form'); - removePayment(form); - }); - }); - - // Добавляем обработчики для автоматического пересчета для существующих форм - paymentsContainer.querySelectorAll('[name$="-amount"]').forEach(field => { - field.addEventListener('input', updatePaymentsTotal); - }); - // Инициализируем итоговую сумму updatePaymentsTotal(); diff --git a/myproject/orders/views.py b/myproject/orders/views.py index c905f5e..472285b 100644 --- a/myproject/orders/views.py +++ b/myproject/orders/views.py @@ -157,6 +157,31 @@ def order_update(request, order_number): order = get_object_or_404(Order, order_number=order_number) if request.method == 'POST': + # Обработка удаления существующего платежа + delete_payment_id = request.POST.get('delete_payment_id') + if delete_payment_id: + try: + from orders.models import Payment + from customers.services.wallet_service import WalletService + + payment = Payment.objects.get(pk=delete_payment_id, order=order) + + # Если это платеж из кошелька - возвращаем средства + if payment.payment_method and payment.payment_method.code == 'account_balance': + WalletService.refund_wallet_payment(order, payment.amount, request.user) + + payment.delete() + + # Пересчитываем сумму оплаты + order.amount_paid = sum(p.amount for p in order.payments.all()) + order.update_payment_status() + + messages.success(request, 'Платеж успешно удален.') + return redirect('orders:order-update', order_number=order.order_number) + except Payment.DoesNotExist: + messages.error(request, 'Платеж не найден.') + return redirect('orders:order-update', order_number=order.order_number) + form = OrderForm(request.POST, instance=order) formset = OrderItemFormSet(request.POST, instance=order) payment_formset = PaymentFormSet(request.POST, instance=order, prefix='payments')