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>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Order, OrderItem, Payment, Address
|
from django.utils.html import format_html
|
||||||
|
from .models import Order, OrderItem, Payment, Address, OrderStatus
|
||||||
|
|
||||||
|
|
||||||
class PaymentInline(admin.TabularInline):
|
class PaymentInline(admin.TabularInline):
|
||||||
@@ -278,3 +279,100 @@ class AddressAdmin(admin.ModelAdmin):
|
|||||||
'classes': ('collapse',)
|
'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)
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Generated by Django 5.0.10 on 2025-11-13 14:19
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def update_status_names_to_russian(apps, schema_editor):
|
||||||
|
"""Обновляем названия статусов на русский язык"""
|
||||||
|
OrderStatus = apps.get_model('orders', 'OrderStatus')
|
||||||
|
|
||||||
|
status_translations = {
|
||||||
|
'draft': 'Черновик',
|
||||||
|
'new': 'Новый',
|
||||||
|
'confirmed': 'Подтвережден',
|
||||||
|
'in_assembly': 'В сборке',
|
||||||
|
'in_delivery': 'В доставке',
|
||||||
|
'completed': 'Выполнен',
|
||||||
|
'return': 'Возврат',
|
||||||
|
'cancelled': 'Отменен',
|
||||||
|
}
|
||||||
|
|
||||||
|
for code, russian_name in status_translations.items():
|
||||||
|
try:
|
||||||
|
status = OrderStatus.objects.get(code=code)
|
||||||
|
status.name = russian_name
|
||||||
|
status.label = russian_name
|
||||||
|
status.save()
|
||||||
|
except OrderStatus.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def reverse_status_names(apps, schema_editor):
|
||||||
|
"""Откатываем названия статусов обратно на английский (для отката миграции)"""
|
||||||
|
OrderStatus = apps.get_model('orders', 'OrderStatus')
|
||||||
|
|
||||||
|
status_translations = {
|
||||||
|
'draft': 'Draft',
|
||||||
|
'new': 'New',
|
||||||
|
'confirmed': 'Confirmed',
|
||||||
|
'in_assembly': 'In Assembly',
|
||||||
|
'in_delivery': 'In Delivery',
|
||||||
|
'completed': 'Completed',
|
||||||
|
'return': 'Return',
|
||||||
|
'cancelled': 'Cancelled',
|
||||||
|
}
|
||||||
|
|
||||||
|
for code, english_name in status_translations.items():
|
||||||
|
try:
|
||||||
|
status = OrderStatus.objects.get(code=code)
|
||||||
|
status.name = english_name
|
||||||
|
status.label = english_name
|
||||||
|
status.save()
|
||||||
|
except OrderStatus.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('orders', '0002_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(update_status_names_to_russian, reverse_status_names),
|
||||||
|
]
|
||||||
@@ -99,6 +99,11 @@ class OrderStatus(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def orders_count(self):
|
||||||
|
"""Количество заказов в этом статусе"""
|
||||||
|
return self.orders.count()
|
||||||
|
|
||||||
|
|
||||||
class Address(models.Model):
|
class Address(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user