diff --git a/myproject/orders/admin.py b/myproject/orders/admin.py
index fc3faa1..f5f0304 100644
--- a/myproject/orders/admin.py
+++ b/myproject/orders/admin.py
@@ -6,6 +6,7 @@
"""
from django.contrib import admin
from django.utils.html import format_html
+from django.urls import reverse
from .models import Order, OrderItem, Transaction, PaymentMethod, Address, OrderStatus, Recipient, Delivery
from tenants.admin_mixins import TenantAdminOnlyMixin
@@ -27,14 +28,21 @@ class OrderItemInline(admin.TabularInline):
"""
model = OrderItem
extra = 1
- fields = ['product', 'product_kit', 'quantity', 'price']
+ fields = ['product', 'product_kit', 'quantity', 'price', 'item_discount_inline']
readonly_fields = []
def get_readonly_fields(self, request, obj=None):
"""Делаем цену readonly для существующих позиций"""
if obj and obj.pk:
- return ['price']
- return []
+ return ['price', 'item_discount_inline']
+ return ['item_discount_inline']
+
+ def item_discount_inline(self, obj):
+ """Отображение скидки в inline"""
+ if obj.discount_amount and obj.discount_amount > 0:
+ return f'-{obj.discount_amount:.2f} руб.'
+ return '-'
+ item_discount_inline.short_description = 'Скидка'
class DeliveryInline(admin.StackedInline):
@@ -49,6 +57,46 @@ class DeliveryInline(admin.StackedInline):
verbose_name_plural = 'Доставка'
+class DiscountApplicationInline(admin.TabularInline):
+ """
+ Inline для просмотра истории применённых скидок.
+ """
+ from discounts.models import DiscountApplication
+ model = DiscountApplication
+ extra = 0
+ can_delete = False
+ verbose_name = 'Применённая скидка'
+ verbose_name_plural = 'Скидки'
+
+ fields = [
+ 'discount_link',
+ 'promo_code_display',
+ 'target',
+ 'discount_amount',
+ 'applied_at',
+ ]
+ readonly_fields = fields
+
+ def discount_link(self, obj):
+ if obj.discount:
+ return format_html(
+ '{}',
+ obj.discount.id, obj.discount.name
+ )
+ return '-'
+ discount_link.short_description = 'Скидка'
+
+ def promo_code_display(self, obj):
+ return obj.promo_code.code if obj.promo_code else '-'
+ promo_code_display.short_description = 'Промокод'
+
+ def has_add_permission(self, request, obj=None):
+ return False
+
+ def has_change_permission(self, request, obj=None):
+ return False
+
+
@admin.register(Order)
class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
"""Админ-панель для управления заказами."""
@@ -56,6 +104,8 @@ class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
'order_number',
'customer',
'status',
+ 'subtotal_display',
+ 'discount_display',
'total_amount',
'payment_status',
'amount_paid',
@@ -66,6 +116,7 @@ class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
'status',
'payment_status',
'created_at',
+ 'applied_discount',
]
search_fields = [
@@ -80,6 +131,8 @@ class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
'order_number',
'created_at',
'updated_at',
+ 'subtotal_display',
+ 'discount_display',
'amount_due',
'payment_status',
]
@@ -94,8 +147,18 @@ class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
),
'description': 'Если получатель не указан, получателем является покупатель'
}),
+ ('Скидки', {
+ 'fields': (
+ 'applied_discount',
+ 'applied_promo_code',
+ 'discount_amount',
+ ),
+ 'classes': ('collapse',)
+ }),
('Оплата', {
'fields': (
+ 'subtotal_display',
+ 'discount_display',
'total_amount',
'amount_paid',
'amount_due',
@@ -112,7 +175,7 @@ class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
}),
)
- inlines = [OrderItemInline, DeliveryInline, TransactionInline]
+ inlines = [OrderItemInline, DeliveryInline, TransactionInline, DiscountApplicationInline]
actions = [
'mark_as_confirmed',
@@ -152,6 +215,23 @@ class OrderAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
self.message_user(request, f'{updated} заказ(ов) отмечено как оплаченные')
mark_as_paid.short_description = 'Отметить как оплаченные'
+ def subtotal_display(self, obj):
+ """Отображение subtotal (сумма товаров)"""
+ return f'{obj.subtotal:.2f} руб.' if obj.subtotal else '0.00 руб.'
+ subtotal_display.short_description = 'Подытог'
+
+ def discount_display(self, obj):
+ """Отображение информации о скидке"""
+ if not obj.discount_amount or obj.discount_amount == 0:
+ return '-'
+ discount_info = f'-{obj.discount_amount:.2f} руб.'
+ if obj.applied_discount:
+ from django.utils.safestring import mark_safe
+ discount_link = f'{obj.applied_discount.name}'
+ return format_html('{}
{}', discount_info, discount_link)
+ return discount_info
+ discount_display.short_description = 'Скидка'
+
@admin.register(Transaction)
class TransactionAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
@@ -202,12 +282,14 @@ class OrderItemAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
'item_name',
'quantity',
'price',
+ 'item_discount_display',
'get_total_price',
]
list_filter = [
'order__status',
'order__created_at',
+ 'applied_discount',
]
search_fields = [
@@ -216,7 +298,7 @@ class OrderItemAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
'product_kit__name',
]
- readonly_fields = ['created_at', 'get_total_price']
+ readonly_fields = ['created_at', 'get_total_price', 'item_discount_display']
fieldsets = (
('Заказ', {
@@ -226,7 +308,11 @@ class OrderItemAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
'fields': ('product', 'product_kit')
}),
('Информация', {
- 'fields': ('quantity', 'price', 'get_total_price')
+ 'fields': ('quantity', 'price', 'item_discount_display', 'get_total_price')
+ }),
+ ('Скидка', {
+ 'fields': ('applied_discount', 'discount_amount'),
+ 'classes': ('collapse',)
}),
('Системная информация', {
'fields': ('created_at',),
@@ -234,6 +320,16 @@ class OrderItemAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):
}),
)
+ def item_discount_display(self, obj):
+ """Отображение скидки на позицию"""
+ if not obj.discount_amount or obj.discount_amount == 0:
+ return '-'
+ result = f'-{obj.discount_amount:.2f} руб.'
+ if obj.applied_discount:
+ result += format_html('
{}', obj.applied_discount.name)
+ return result
+ item_discount_display.short_description = 'Скидка'
+
@admin.register(Address)
class AddressAdmin(TenantAdminOnlyMixin, admin.ModelAdmin):