Исправлена форма заказа: две колонки и корректная работа кнопки сохранения
- Разделен экран на две колонки: заказ слева, оплата справа - Форма оплаты вынесена за пределы основной формы заказа (устранена проблема вложенных форм) - Исправлен метод calculate_total() для сохранения итоговой суммы в БД - Добавлена модель Transaction для учета платежей и возвратов - Добавлена модель PaymentMethod для методов оплаты - Удалена старая модель Payment, заменена на Transaction - Добавлен TransactionService для управления транзакциями - Обновлен интерфейс форм оплаты для правой колонки - Кнопка 'Сохранить изменения' теперь работает корректно
This commit is contained in:
@@ -8,8 +8,8 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from decimal import Decimal
|
||||
from .models import Order, OrderItem, Address, OrderStatus, Payment, PaymentMethod
|
||||
from .forms import OrderForm, OrderItemFormSet, OrderStatusForm, PaymentForm
|
||||
from .models import Order, OrderItem, Address, OrderStatus, Transaction, PaymentMethod
|
||||
from .forms import OrderForm, OrderItemFormSet, OrderStatusForm, TransactionForm
|
||||
from .filters import OrderFilter
|
||||
from .services.address_service import AddressService
|
||||
import json
|
||||
@@ -49,7 +49,7 @@ def order_detail(request, order_number):
|
||||
"""Детальная информация о заказе"""
|
||||
order = get_object_or_404(
|
||||
Order.objects.select_related('customer', 'delivery_address', 'pickup_warehouse', 'modified_by')
|
||||
.prefetch_related('items__product', 'items__product_kit', 'payments__created_by'),
|
||||
.prefetch_related('items__product', 'items__product_kit', 'transactions__created_by'),
|
||||
order_number=order_number
|
||||
)
|
||||
|
||||
@@ -133,6 +133,9 @@ def order_create(request):
|
||||
def order_update(request, order_number):
|
||||
"""Редактирование заказа"""
|
||||
order = get_object_or_404(Order, order_number=order_number)
|
||||
|
||||
# Пересчитываем amount_paid на основе транзакций (на случай миграции)
|
||||
order.recalculate_amount_paid()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = OrderForm(request.POST, instance=order)
|
||||
@@ -221,37 +224,37 @@ def order_delete(request, order_number):
|
||||
return render(request, 'orders/order_confirm_delete.html', context)
|
||||
|
||||
|
||||
# === УПРАВЛЕНИЕ ПЛАТЕЖАМИ ===
|
||||
# === УПРАВЛЕНИЕ ТРАНЗАКЦИЯМИ ===
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def payment_add(request, order_number):
|
||||
def transaction_add_payment(request, order_number):
|
||||
"""
|
||||
Добавление нового платежа к заказу.
|
||||
Отдельный endpoint для чистоты архитектуры.
|
||||
"""
|
||||
from orders.services.transaction_service import TransactionService
|
||||
|
||||
order = get_object_or_404(Order, order_number=order_number)
|
||||
|
||||
form = PaymentForm(request.POST)
|
||||
form = TransactionForm(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
payment = form.save(commit=False)
|
||||
payment.order = order
|
||||
payment.created_by = request.user
|
||||
|
||||
try:
|
||||
# save() вызовет Payment.save() который обработает:
|
||||
# - Списание из кошелька (если account_balance)
|
||||
# - Обработку переплаты
|
||||
# - Обновление amount_paid и payment_status
|
||||
payment.save()
|
||||
# Создаём транзакцию платежа
|
||||
transaction = TransactionService.create_payment(
|
||||
order=order,
|
||||
amount=form.cleaned_data['amount'],
|
||||
payment_method=form.cleaned_data['payment_method'],
|
||||
user=request.user,
|
||||
notes=form.cleaned_data.get('notes')
|
||||
)
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
f'Платеж на сумму {payment.amount} руб. '
|
||||
f'({payment.payment_method.name}) успешно добавлен.'
|
||||
f'Платёж на сумму {transaction.amount} руб. '
|
||||
f'({transaction.payment_method.name}) успешно добавлен.'
|
||||
)
|
||||
except ValidationError as e:
|
||||
except (ValidationError, ValueError) as e:
|
||||
messages.error(request, f'Ошибка при добавлении платежа: {e}')
|
||||
else:
|
||||
# Показываем ошибки валидации
|
||||
@@ -265,29 +268,68 @@ def payment_add(request, order_number):
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def payment_delete(request, order_number, payment_id):
|
||||
def transaction_add_refund(request, order_number):
|
||||
"""
|
||||
Удаление платежа.
|
||||
Возвращает средства в кошелек, если платеж был из кошелька.
|
||||
Добавление возврата по заказу.
|
||||
"""
|
||||
from orders.services.transaction_service import TransactionService
|
||||
|
||||
order = get_object_or_404(Order, order_number=order_number)
|
||||
|
||||
amount = request.POST.get('refund_amount')
|
||||
payment_method_id = request.POST.get('refund_payment_method')
|
||||
reason = request.POST.get('refund_reason')
|
||||
notes = request.POST.get('refund_notes')
|
||||
|
||||
try:
|
||||
amount = Decimal(amount)
|
||||
payment_method = get_object_or_404(PaymentMethod, pk=payment_method_id)
|
||||
|
||||
# Создаём транзакцию возврата
|
||||
transaction = TransactionService.create_refund(
|
||||
order=order,
|
||||
amount=amount,
|
||||
payment_method=payment_method,
|
||||
user=request.user,
|
||||
reason=reason,
|
||||
notes=notes
|
||||
)
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
f'Возврат на сумму {transaction.amount} руб. '
|
||||
f'({transaction.payment_method.name}) успешно создан.'
|
||||
)
|
||||
except (ValidationError, ValueError) as e:
|
||||
messages.error(request, f'Ошибка при создании возврата: {e}')
|
||||
|
||||
return redirect('orders:order-update', order_number=order.order_number)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def transaction_delete(request, order_number, transaction_id):
|
||||
"""
|
||||
Удаление транзакции (не рекомендуется, лучше использовать refund).
|
||||
Оставлено для совместимости.
|
||||
"""
|
||||
order = get_object_or_404(Order, order_number=order_number)
|
||||
payment = get_object_or_404(Payment, pk=payment_id, order=order)
|
||||
transaction_obj = get_object_or_404(Transaction, pk=transaction_id, order=order)
|
||||
|
||||
# Сохраняем данные для сообщения
|
||||
payment_info = f'{payment.payment_method.name} на сумму {payment.amount} руб.'
|
||||
transaction_info = f'{transaction_obj.get_transaction_type_display()} {transaction_obj.payment_method.name} на сумму {transaction_obj.amount} руб.'
|
||||
|
||||
# Если это платеж из кошелька - возвращаем средства
|
||||
if payment.payment_method.code == 'account_balance':
|
||||
from customers.services.wallet_service import WalletService
|
||||
WalletService.refund_wallet_payment(order, payment.amount, request.user)
|
||||
# Предупреждение: удаление транзакций нарушает историю
|
||||
transaction_obj.delete()
|
||||
|
||||
payment.delete()
|
||||
# Пересчитываем баланс
|
||||
order.recalculate_amount_paid()
|
||||
|
||||
# Пересчитываем сумму оплаты
|
||||
order.amount_paid = sum(p.amount for p in order.payments.all())
|
||||
order.update_payment_status()
|
||||
|
||||
messages.success(request, f'Платеж {payment_info} успешно удален.')
|
||||
messages.warning(
|
||||
request,
|
||||
f'Транзакция {transaction_info} удалена. '
|
||||
f'Рекомендуем использовать "Возврат" вместо удаления.'
|
||||
)
|
||||
return redirect('orders:order-update', order_number=order.order_number)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user