Исправить: показывать существующие платежи информационно при редактировании заказа
ПРОБЛЕМА: При редактировании заказа с уже существующими платежами из кошелька, 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' ✅ Правильный подсчет итоговой суммы (существующие + новые)
This commit is contained in:
@@ -608,55 +608,54 @@
|
|||||||
<!-- Скрытые поля для formset management -->
|
<!-- Скрытые поля для formset management -->
|
||||||
{{ payment_formset.management_form }}
|
{{ payment_formset.management_form }}
|
||||||
|
|
||||||
<!-- Контейнер для платежей -->
|
<!-- Уже сохраненные платежи (информационно) -->
|
||||||
<div id="payments-container">
|
{% if order.pk and order.payments.exists %}
|
||||||
{% for payment_form in payment_formset %}
|
<div class="mb-4">
|
||||||
<div class="payment-form border rounded p-3 mb-3" data-form-index="{{ forloop.counter0 }}">
|
<h6 class="text-muted mb-3"><i class="bi bi-check-circle"></i> Проведенные платежи</h6>
|
||||||
{{ payment_form.id }}
|
{% for payment in order.payments.all %}
|
||||||
{{ payment_form.DELETE }}
|
<div class="border rounded p-3 mb-2 bg-light">
|
||||||
|
<div class="row align-items-center">
|
||||||
<div class="row align-items-end">
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-2">
|
<small class="text-muted d-block">Способ оплаты</small>
|
||||||
<label class="form-label">Способ оплаты</label>
|
<strong>{{ payment.payment_method.name }}</strong>
|
||||||
{{ payment_form.payment_method }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="mb-2">
|
<small class="text-muted d-block">Сумма</small>
|
||||||
<label class="form-label">Сумма</label>
|
<strong class="text-success">{{ payment.amount|floatformat:2 }} руб.</strong>
|
||||||
{{ payment_form.amount }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="mb-2">
|
<small class="text-muted d-block">Примечания</small>
|
||||||
<label class="form-label">Примечания</label>
|
<span class="text-muted">{{ payment.notes|default:"—" }}</span>
|
||||||
{{ payment_form.notes }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="col-md-1 text-end">
|
||||||
<div class="col-md-1">
|
<form method="post" style="display: inline;" onsubmit="return confirm('Удалить этот платеж?');">
|
||||||
<button type="button" class="btn btn-danger btn-sm w-100 remove-payment-btn">
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="delete_payment_id" value="{{ payment.id }}">
|
||||||
|
<button type="submit" class="btn btn-outline-danger btn-sm" title="Удалить платеж">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if payment_form.errors %}
|
|
||||||
<div class="alert alert-danger mt-2">{{ payment_form.errors }}</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Контейнер для НОВЫХ платежей -->
|
||||||
|
<div id="payments-container">
|
||||||
|
<!-- Здесь будут добавляться новые платежи -->
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Итоговая сумма платежей -->
|
<!-- Итоговая сумма платежей -->
|
||||||
<div id="payments-total-section" class="border-top pt-3 mt-3 mb-3">
|
<div id="payments-total-section" class="border-top pt-3 mt-3 mb-3">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<p class="mb-0 text-muted"><i class="bi bi-cash-stack"></i> Внесено платежей:</p>
|
<p class="mb-0 text-muted"><i class="bi bi-cash-stack"></i> Всего внесено:</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<h5 class="mb-0 text-success">
|
<h5 class="mb-0 text-success">
|
||||||
<span id="payments-total-value">0.00</span> руб.
|
<span id="payments-total-value">{{ order.amount_paid|default:"0.00"|floatformat:2 }}</span> руб.
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1631,20 +1630,29 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Функция для расчета итоговой суммы платежей
|
// Функция для расчета итоговой суммы платежей
|
||||||
function calculatePaymentsTotal() {
|
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'))
|
const visiblePaymentForms = Array.from(document.querySelectorAll('.payment-form'))
|
||||||
.filter(form => !form.classList.contains('deleted'));
|
.filter(form => !form.classList.contains('deleted'));
|
||||||
|
|
||||||
let total = 0;
|
let newTotal = 0;
|
||||||
|
|
||||||
visiblePaymentForms.forEach((form) => {
|
visiblePaymentForms.forEach((form) => {
|
||||||
const amountField = form.querySelector('[name$="-amount"]');
|
const amountField = form.querySelector('[name$="-amount"]');
|
||||||
if (amountField) {
|
if (amountField) {
|
||||||
const amount = parseFloat(amountField.value.replace(',', '.')) || 0;
|
const amount = parseFloat(amountField.value.replace(',', '.')) || 0;
|
||||||
total += amount;
|
newTotal += amount;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return total;
|
return existingTotal + newTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePaymentsTotal() {
|
function updatePaymentsTotal() {
|
||||||
@@ -1735,19 +1743,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
addPaymentBtn.addEventListener('click', addNewPayment);
|
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();
|
updatePaymentsTotal();
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,31 @@ def order_update(request, order_number):
|
|||||||
order = get_object_or_404(Order, order_number=order_number)
|
order = get_object_or_404(Order, order_number=order_number)
|
||||||
|
|
||||||
if request.method == 'POST':
|
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)
|
form = OrderForm(request.POST, instance=order)
|
||||||
formset = OrderItemFormSet(request.POST, instance=order)
|
formset = OrderItemFormSet(request.POST, instance=order)
|
||||||
payment_formset = PaymentFormSet(request.POST, instance=order, prefix='payments')
|
payment_formset = PaymentFormSet(request.POST, instance=order, prefix='payments')
|
||||||
|
|||||||
Reference in New Issue
Block a user