Рефакторинг системы кошелька клиентов
Основные изменения: - Переход от денормализованного поля wallet_balance к вычисляемому балансу - Баланс теперь вычисляется как SUM(signed_amount) транзакций - Добавлено кеширование баланса для производительности (5 минут) - Новая модель WalletTransaction с полем signed_amount (может быть +/-) - WalletService для всех операций с кошельком (deposit, spend, adjustment) - Защита от отрицательного баланса и race conditions через select_for_update - Добавлен balance_after в каждую транзакцию для аудита - Обновлены миграции для переноса данных из старой схемы Улучшения безопасности: - Атомарные транзакции для всех операций с балансом - Блокировка строк при модификации баланса - Валидация недостаточности средств - Обязательное описание для корректировок баланса UI/UX изменения: - Обновлён вывод баланса кошелька в деталях клиента - Добавлена история транзакций с типами и описаниями - Цветовая индикация положительных транзакций (зелёный) Техническая документация: - Добавлены docstrings для всех методов WalletService - Комментарии к критичным участкам кода - Примеры использования в docstrings
This commit is contained in:
@@ -125,50 +125,29 @@ class Transaction(models.Model):
|
||||
def save(self, *args, **kwargs):
|
||||
"""При сохранении обновляем баланс заказа и обрабатываем кошелёк"""
|
||||
is_new = self.pk is None
|
||||
|
||||
|
||||
with transaction.atomic():
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
# Пересчитываем баланс заказа
|
||||
self.order.recalculate_amount_paid()
|
||||
|
||||
|
||||
# Обработка кошелька только для новых транзакций
|
||||
if is_new and self.payment_method.code == 'account_balance':
|
||||
from customers.models import Customer, WalletTransaction
|
||||
|
||||
# Блокируем запись клиента
|
||||
customer = Customer.objects.select_for_update().get(pk=self.order.customer_id)
|
||||
|
||||
from customers.services.wallet_service import WalletService
|
||||
|
||||
if self.transaction_type == 'payment':
|
||||
# Списание из кошелька
|
||||
if customer.wallet_balance < self.amount:
|
||||
raise ValidationError(
|
||||
f'Недостаточно средств в кошельке '
|
||||
f'(доступно {customer.wallet_balance} руб.)'
|
||||
)
|
||||
|
||||
customer.wallet_balance = (customer.wallet_balance - self.amount).quantize(Decimal('0.01'))
|
||||
customer.save(update_fields=['wallet_balance'])
|
||||
|
||||
WalletTransaction.objects.create(
|
||||
customer=customer,
|
||||
amount=self.amount,
|
||||
transaction_type='spend',
|
||||
WalletService.create_wallet_spend(
|
||||
order=self.order,
|
||||
description=f'Оплата из кошелька по заказу #{self.order.order_number}',
|
||||
created_by=self.created_by
|
||||
amount=self.amount,
|
||||
user=self.created_by
|
||||
)
|
||||
|
||||
|
||||
elif self.transaction_type == 'refund':
|
||||
# Возврат в кошелёк
|
||||
customer.wallet_balance = (customer.wallet_balance + self.amount).quantize(Decimal('0.01'))
|
||||
customer.save(update_fields=['wallet_balance'])
|
||||
|
||||
WalletTransaction.objects.create(
|
||||
customer=customer,
|
||||
amount=self.amount,
|
||||
transaction_type='deposit',
|
||||
WalletService.create_wallet_deposit(
|
||||
order=self.order,
|
||||
description=f'Возврат в кошелёк по заказу #{self.order.order_number}',
|
||||
created_by=self.created_by
|
||||
amount=self.amount,
|
||||
user=self.created_by
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user