Feature: Добавлены методы получения суммы заказов клиента
Добавлены методы в модель Customer для расчета суммы успешных заказов: - get_successful_orders_total() - гибкий метод с фильтрацией по датам - get_last_year_orders_total() - сумма за последний год Удалено устаревшее поле total_spent: - Методы предоставляют более точные и актуальные данные - Используют агрегацию на уровне БД для производительности Обновлен UI карточки клиента: - Отображается сумма всех успешных заказов - Отображается сумма заказов за последний год - Убрана колонка total_spent из списка клиентов Изменения: - customers/models.py: добавлены методы, удалено поле total_spent - customers/views.py: добавлен расчет сумм в контекст - customers/templates: обновлены шаблоны - customers/admin.py: удалены упоминания total_spent - Создана миграция 0005_remove_total_spent 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -30,7 +30,6 @@ class CustomerAdmin(admin.ModelAdmin):
|
||||
'email',
|
||||
'phone',
|
||||
'wallet_balance_display',
|
||||
'total_spent',
|
||||
'is_system_customer',
|
||||
'created_at'
|
||||
)
|
||||
@@ -45,7 +44,7 @@ class CustomerAdmin(admin.ModelAdmin):
|
||||
)
|
||||
date_hierarchy = 'created_at'
|
||||
ordering = ('-created_at',)
|
||||
readonly_fields = ('created_at', 'updated_at', 'total_spent', 'is_system_customer', 'wallet_balance')
|
||||
readonly_fields = ('created_at', 'updated_at', 'is_system_customer', 'wallet_balance')
|
||||
|
||||
fieldsets = (
|
||||
('Основная информация', {
|
||||
@@ -54,10 +53,6 @@ class CustomerAdmin(admin.ModelAdmin):
|
||||
('Кошелёк', {
|
||||
'fields': ('wallet_balance',),
|
||||
}),
|
||||
('Статистика покупок', {
|
||||
'fields': ('total_spent',),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
('Заметки', {
|
||||
'fields': ('notes',)
|
||||
}),
|
||||
@@ -82,7 +77,7 @@ class CustomerAdmin(admin.ModelAdmin):
|
||||
"""Делаем все поля read-only для системного клиента"""
|
||||
if obj and obj.is_system_customer:
|
||||
# Для системного клиента все поля только для чтения
|
||||
return ['name', 'email', 'phone', 'total_spent', 'is_system_customer', 'wallet_balance', 'notes', 'created_at', 'updated_at']
|
||||
return ['name', 'email', 'phone', 'is_system_customer', 'wallet_balance', 'notes', 'created_at', 'updated_at']
|
||||
return self.readonly_fields
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
|
||||
17
myproject/customers/migrations/0005_remove_total_spent.py
Normal file
17
myproject/customers/migrations/0005_remove_total_spent.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.0.10 on 2025-12-05 21:17
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('customers', '0004_customer_wallet_balance_wallettransaction'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='customer',
|
||||
name='total_spent',
|
||||
),
|
||||
]
|
||||
@@ -25,13 +25,6 @@ class Customer(models.Model):
|
||||
# Temporary field to store raw phone number during initialization
|
||||
_raw_phone = None
|
||||
|
||||
total_spent = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=2,
|
||||
default=0,
|
||||
verbose_name="Общая сумма покупок"
|
||||
)
|
||||
|
||||
# Wallet balance for overpayments
|
||||
wallet_balance = models.DecimalField(
|
||||
max_digits=10,
|
||||
@@ -259,6 +252,56 @@ class Customer(models.Model):
|
||||
"""
|
||||
return self.wallet_transactions.all()
|
||||
|
||||
def get_successful_orders_total(self, start_date=None, end_date=None):
|
||||
"""
|
||||
Получить сумму успешных заказов за указанный период.
|
||||
|
||||
Args:
|
||||
start_date: Дата начала периода (DateField или None)
|
||||
end_date: Дата окончания периода (DateField или None)
|
||||
|
||||
Returns:
|
||||
Decimal: Сумма успешных заказов
|
||||
"""
|
||||
from django.db.models import Sum, Value, DecimalField
|
||||
from django.db.models.functions import Coalesce
|
||||
from decimal import Decimal
|
||||
|
||||
# Базовый queryset: только успешные заказы
|
||||
queryset = self.orders.filter(status__is_positive_end=True)
|
||||
|
||||
# Фильтрация по датам (используем delivery_date)
|
||||
if start_date:
|
||||
queryset = queryset.filter(delivery_date__gte=start_date)
|
||||
if end_date:
|
||||
queryset = queryset.filter(delivery_date__lte=end_date)
|
||||
|
||||
# Агрегация суммы
|
||||
result = queryset.aggregate(
|
||||
total=Coalesce(
|
||||
Sum('total_amount'),
|
||||
Value(0),
|
||||
output_field=DecimalField()
|
||||
)
|
||||
)
|
||||
|
||||
return result['total'] or Decimal('0')
|
||||
|
||||
def get_last_year_orders_total(self):
|
||||
"""
|
||||
Получить сумму успешных заказов за последний календарный год.
|
||||
(С этой даты прошлого года по текущую дату)
|
||||
|
||||
Returns:
|
||||
Decimal: Сумма успешных заказов за год
|
||||
"""
|
||||
from datetime import date, timedelta
|
||||
|
||||
today = date.today()
|
||||
year_ago = today - timedelta(days=365)
|
||||
|
||||
return self.get_successful_orders_total(start_date=year_ago, end_date=today)
|
||||
|
||||
|
||||
class WalletTransaction(models.Model):
|
||||
"""
|
||||
|
||||
@@ -39,8 +39,12 @@
|
||||
<td>{{ customer.phone|default:"Не указано" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Сумма покупок:</th>
|
||||
<td>{{ customer.total_spent|floatformat:2 }} руб.</td>
|
||||
<th>Сумма всех успешных заказов:</th>
|
||||
<td><strong>{{ total_orders_sum|floatformat:2 }} руб.</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Сумма заказов за последний год:</th>
|
||||
<td><strong>{{ last_year_orders_sum|floatformat:2 }} руб.</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Общий долг по активным заказам:</th>
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
<th>Имя</th>
|
||||
<th>Email</th>
|
||||
<th>Телефон</th>
|
||||
<th>Сумма покупок</th>
|
||||
<th class="text-end">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -95,8 +94,6 @@
|
||||
<td>{{ customer.email|default:'—' }}</td>
|
||||
<td>{{ customer.phone|default:'—' }}</td>
|
||||
|
||||
<td>{{ customer.total_spent|default:0|floatformat:2 }} руб.</td>
|
||||
|
||||
<td class="text-end" onclick="event.stopPropagation();">
|
||||
<a href="{% url 'customers:customer-detail' customer.pk %}"
|
||||
class="btn btn-sm btn-outline-primary">👁</a>
|
||||
|
||||
@@ -122,6 +122,12 @@ def customer_detail(request, pk):
|
||||
)
|
||||
refund_amount = refund_amount_result['total_refund'] or Decimal('0')
|
||||
|
||||
# Сумма всех успешных заказов
|
||||
total_orders_sum = customer.get_successful_orders_total()
|
||||
|
||||
# Сумма успешных заказов за последний год
|
||||
last_year_orders_sum = customer.get_last_year_orders_total()
|
||||
|
||||
# История транзакций кошелька (последние 20)
|
||||
from .models import WalletTransaction
|
||||
wallet_transactions = WalletTransaction.objects.filter(
|
||||
@@ -141,6 +147,8 @@ def customer_detail(request, pk):
|
||||
'refund_amount': refund_amount,
|
||||
'wallet_transactions': wallet_transactions,
|
||||
'orders_page': orders_page,
|
||||
'total_orders_sum': total_orders_sum,
|
||||
'last_year_orders_sum': last_year_orders_sum,
|
||||
}
|
||||
return render(request, 'customers/customer_detail.html', context)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user