Реализация системы кошелька клиента для переплат

- Добавлено поле wallet_balance в модель Customer
- Создана модель WalletTransaction для истории операций
- Реализован сервис WalletService с методами:
  * add_overpayment - автоматическое зачисление переплаты
  * pay_with_wallet - оплата заказа из кошелька
  * adjust_balance - ручная корректировка баланса
- Интеграция с Payment.save() для автоматической обработки переплат
- UI для оплаты из кошелька в деталях заказа
- Отображение баланса и долга на странице клиента
- Админка с inline транзакций и запретом ручного создания
- Добавлен способ оплаты account_balance
- Миграция 0004 для customers приложения
This commit is contained in:
2025-11-26 14:47:11 +03:00
parent 0653ec0545
commit 5ead7fdd2e
16 changed files with 1401 additions and 3 deletions

View File

@@ -32,6 +32,15 @@ class Customer(models.Model):
verbose_name="Общая сумма покупок"
)
# Wallet balance for overpayments
wallet_balance = models.DecimalField(
max_digits=10,
decimal_places=2,
default=0,
verbose_name="Баланс кошелька",
help_text="Остаток переплат клиента, доступный для оплаты заказов"
)
# System customer flag
is_system_customer = models.BooleanField(
default=False,
@@ -207,3 +216,74 @@ class Customer(models.Model):
)
return customer, created
class WalletTransaction(models.Model):
"""
Транзакция по кошельку клиента.
Хранит историю всех пополнений, списаний и корректировок баланса.
"""
TRANSACTION_TYPE_CHOICES = [
('deposit', 'Пополнение'),
('spend', 'Списание'),
('adjustment', 'Корректировка'),
]
customer = models.ForeignKey(
'Customer',
on_delete=models.PROTECT,
related_name='wallet_transactions',
verbose_name="Клиент"
)
amount = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name="Сумма"
)
transaction_type = models.CharField(
max_length=20,
choices=TRANSACTION_TYPE_CHOICES,
verbose_name="Тип транзакции"
)
order = models.ForeignKey(
'orders.Order',
null=True,
blank=True,
on_delete=models.PROTECT,
verbose_name="Заказ",
help_text="Заказ, к которому относится транзакция (если применимо)"
)
description = models.TextField(
blank=True,
verbose_name="Описание"
)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата создания"
)
created_by = models.ForeignKey(
'accounts.CustomUser',
null=True,
blank=True,
on_delete=models.SET_NULL,
verbose_name="Создано пользователем"
)
class Meta:
verbose_name = "Транзакция кошелька"
verbose_name_plural = "Транзакции кошелька"
ordering = ['-created_at']
indexes = [
models.Index(fields=['customer', '-created_at']),
models.Index(fields=['transaction_type']),
models.Index(fields=['order']),
]
def __str__(self):
return f"{self.get_transaction_type_display()} {self.amount} руб. для {self.customer}"