Files
octopus/myproject/orders/admin.py
Andrey Smakotin d7cfb07695 Add OrderStatus to Django admin with Russian translations
- Add OrderStatusAdmin to admin panel with custom display methods
- Implement color preview, order badge, and order count displays
- Protect system statuses from deletion and code modification
- Add orders_count property to OrderStatus model
- Create migration to translate status names to Russian
- Use format_html for safe HTML rendering in admin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 18:07:34 +03:00

379 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
from django.contrib import admin
from django.utils.html import format_html
from .models import Order, OrderItem, Payment, Address, OrderStatus
class PaymentInline(admin.TabularInline):
"""
Inline для управления платежами по заказу.
"""
model = Payment
extra = 1
fields = ['amount', 'payment_method', 'payment_date', 'created_by', 'notes']
readonly_fields = ['payment_date']
class OrderItemInline(admin.TabularInline):
"""
Inline для управления позициями заказа прямо в форме заказа.
"""
model = OrderItem
extra = 1
fields = ['product', 'product_kit', 'quantity', 'price']
readonly_fields = []
def get_readonly_fields(self, request, obj=None):
"""Делаем цену readonly для существующих позиций"""
if obj and obj.pk:
return ['price']
return []
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
"""
Админ-панель для управления заказами.
"""
list_display = [
'order_number',
'customer',
'is_delivery',
'delivery_date',
'status',
'total_amount',
'payment_status',
'amount_paid',
'created_at',
]
list_filter = [
'status',
'is_delivery',
'payment_status',
'delivery_date',
'created_at',
]
search_fields = [
'order_number',
'customer__name',
'customer__phone',
'customer__email',
'delivery_address__recipient_name',
'delivery_address__street',
]
readonly_fields = [
'order_number',
'created_at',
'updated_at',
'delivery_info',
'delivery_time_window',
'amount_due',
'payment_status',
]
fieldsets = (
('Основная информация', {
'fields': ('order_number', 'customer', 'status')
}),
('Доставка', {
'fields': (
'is_delivery',
'customer_is_recipient',
'delivery_address',
'pickup_shop',
'delivery_date',
'delivery_time_start',
'delivery_time_end',
'delivery_cost',
'delivery_info',
'delivery_time_window',
)
}),
('Оплата', {
'fields': (
'payment_method',
'total_amount',
'discount_amount',
'amount_paid',
'amount_due',
'payment_status',
)
}),
('Дополнительно', {
'fields': ('is_anonymous', 'special_instructions'),
'classes': ('collapse',)
}),
('Системная информация', {
'fields': ('created_at', 'updated_at', 'modified_by'),
'classes': ('collapse',)
}),
)
inlines = [OrderItemInline, PaymentInline]
actions = [
'mark_as_confirmed',
'mark_as_in_assembly',
'mark_as_in_delivery',
'mark_as_delivered',
'mark_as_paid',
]
def mark_as_confirmed(self, request, queryset):
"""Отметить заказы как подтвержденные"""
updated = queryset.update(status='confirmed')
self.message_user(request, f'{updated} заказ(ов) отмечено как подтвержденные')
mark_as_confirmed.short_description = 'Отметить как подтвержденные'
def mark_as_in_assembly(self, request, queryset):
"""Отметить заказы как в сборке"""
updated = queryset.update(status='in_assembly')
self.message_user(request, f'{updated} заказ(ов) отмечено как в сборке')
mark_as_in_assembly.short_description = 'Отметить как в сборке'
def mark_as_in_delivery(self, request, queryset):
"""Отметить заказы как в доставке"""
updated = queryset.update(status='in_delivery')
self.message_user(request, f'{updated} заказ(ов) отмечено как в доставке')
mark_as_in_delivery.short_description = 'Отметить как в доставке'
def mark_as_delivered(self, request, queryset):
"""Отметить заказы как доставленные"""
updated = queryset.update(status='delivered')
self.message_user(request, f'{updated} заказ(ов) отмечено как доставленные')
mark_as_delivered.short_description = 'Отметить как доставленные'
def mark_as_paid(self, request, queryset):
"""Отметить заказы как оплаченные"""
updated = queryset.update(is_paid=True)
self.message_user(request, f'{updated} заказ(ов) отмечено как оплаченные')
mark_as_paid.short_description = 'Отметить как оплаченные'
@admin.register(Payment)
class PaymentAdmin(admin.ModelAdmin):
"""
Админ-панель для управления платежами.
"""
list_display = [
'order',
'amount',
'payment_method',
'payment_date',
'created_by',
]
list_filter = [
'payment_method',
'payment_date',
]
search_fields = [
'order__order_number',
'notes',
]
readonly_fields = ['payment_date']
fieldsets = (
('Информация о платеже', {
'fields': ('order', 'amount', 'payment_method', 'payment_date')
}),
('Дополнительно', {
'fields': ('created_by', 'notes')
}),
)
@admin.register(OrderItem)
class OrderItemAdmin(admin.ModelAdmin):
"""
Админ-панель для управления позициями заказов.
"""
list_display = [
'order',
'item_name',
'quantity',
'price',
'get_total_price',
]
list_filter = [
'order__status',
'order__created_at',
]
search_fields = [
'order__order_number',
'product__name',
'product_kit__name',
]
readonly_fields = ['created_at', 'get_total_price']
fieldsets = (
('Заказ', {
'fields': ('order',)
}),
('Товар/Комплект', {
'fields': ('product', 'product_kit')
}),
('Информация', {
'fields': ('quantity', 'price', 'get_total_price')
}),
('Системная информация', {
'fields': ('created_at',),
'classes': ('collapse',)
}),
)
@admin.register(Address)
class AddressAdmin(admin.ModelAdmin):
"""
Админ-панель для управления адресами доставки заказов.
"""
list_display = [
'recipient_name',
'recipient_phone',
'full_address',
'entrance',
'floor',
'confirm_address_with_recipient',
'created_at',
]
list_filter = [
'confirm_address_with_recipient',
'created_at',
]
search_fields = [
'recipient_name',
'street',
'building_number',
]
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
('Информация о получателе', {
'fields': ('recipient_name', 'recipient_phone')
}),
('Адрес доставки', {
'fields': ('street', 'building_number', 'apartment_number', 'entrance', 'floor')
}),
('Доступ в здание', {
'fields': ('intercom_code',),
'classes': ('collapse',)
}),
('Дополнительная информация', {
'fields': ('delivery_instructions', 'confirm_address_with_recipient'),
'classes': ('collapse',)
}),
('Даты', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
@admin.register(OrderStatus)
class OrderStatusAdmin(admin.ModelAdmin):
"""
Админ-панель для управления статусами заказов.
"""
list_display = [
'order_display',
'name',
'code',
'color_preview',
'is_system',
'is_positive_end',
'is_negative_end',
'orders_count',
]
list_filter = [
'is_system',
'is_positive_end',
'is_negative_end',
]
search_fields = [
'name',
'code',
'label',
]
readonly_fields = ['created_at', 'updated_at', 'created_by', 'updated_by']
fieldsets = (
('Основная информация', {
'fields': ('code', 'name', 'label', 'order')
}),
('Визуальное оформление', {
'fields': ('color',)
}),
('Тип статуса', {
'fields': ('is_system', 'is_positive_end', 'is_negative_end')
}),
('Дополнительно', {
'fields': ('description',),
'classes': ('collapse',)
}),
('Системная информация', {
'fields': ('created_at', 'updated_at', 'created_by', 'updated_by', 'get_orders_count'),
'classes': ('collapse',)
}),
)
ordering = ['order', 'name']
def get_readonly_fields(self, request, obj=None):
"""Делаем код readonly для системных статусов"""
readonly = list(self.readonly_fields)
readonly.append('get_orders_count')
if obj and obj.is_system:
readonly.append('code')
return readonly
def color_preview(self, obj):
"""Превью цвета статуса"""
return format_html(
'<div style="width: 30px; height: 20px; background-color: {}; border: 1px solid #ccc; border-radius: 3px;"></div>',
obj.color
)
color_preview.short_description = 'Цвет'
def order_display(self, obj):
"""Отображение порядкового номера с бейджем"""
return format_html(
'<span style="display: inline-block; background-color: #6c757d; color: white; padding: 2px 8px; border-radius: 10px; font-size: 11px;">{}</span>',
obj.order
)
order_display.short_description = 'Порядок'
def orders_count(self, obj):
"""Количество заказов в этом статусе"""
count = obj.orders_count
if count == 0:
return format_html('<span style="color: #999;">{}</span>', count)
return format_html('<span style="font-weight: bold;">{}</span>', count)
orders_count.short_description = 'Заказов'
def get_orders_count(self, obj):
"""Количество заказов для readonly поля"""
return obj.orders_count if obj else 0
get_orders_count.short_description = 'Количество заказов'
def has_delete_permission(self, request, obj=None):
"""Запрещаем удаление системных статусов и статусов с заказами"""
if obj:
if obj.is_system or obj.orders_count > 0:
return False
return super().has_delete_permission(request, obj)