Implement flexible order status management system
Features: - Created OrderStatus model for managing statuses per tenant - Added system-level statuses: draft, new, confirmed, in_assembly, in_delivery, completed, return, cancelled - Implemented CRUD views for managing order statuses - Created OrderStatusService with status transitions and business logic hooks - Updated Order model to use ForeignKey to OrderStatus - Added is_returned flag for tracking returned orders - Updated filters to work with new OrderStatus model - Created management command for status initialization - Added HTML templates for status list, form, and confirmation - Fixed views.py to use OrderStatus instead of removed STATUS_CHOICES 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,99 @@ from shops.models import Shop
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
|
||||
class OrderStatus(models.Model):
|
||||
"""
|
||||
Статус заказа, управляется отдельно для каждого тенанта.
|
||||
Благодаря django-tenants в TENANT_APPS, данные изолированы по схемам.
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name="Название статуса"
|
||||
)
|
||||
|
||||
code = models.SlugField(
|
||||
unique=True,
|
||||
verbose_name="Код статуса",
|
||||
help_text="Уникальный идентификатор (например: 'completed', 'cancelled')"
|
||||
)
|
||||
|
||||
label = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name="Метка для отображения",
|
||||
blank=True
|
||||
)
|
||||
|
||||
is_system = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Системный статус",
|
||||
help_text="True для встроенных статусов (draft, completed, cancelled)"
|
||||
)
|
||||
|
||||
is_positive_end = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Положительный конец",
|
||||
help_text="True если это финальный успешный статус (Выполнен)"
|
||||
)
|
||||
|
||||
is_negative_end = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Отрицательный конец",
|
||||
help_text="True если это финальный отрицательный статус (Отменен)"
|
||||
)
|
||||
|
||||
order = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name="Порядок отображения"
|
||||
)
|
||||
|
||||
color = models.CharField(
|
||||
max_length=7,
|
||||
blank=True,
|
||||
default='#808080',
|
||||
verbose_name="Цвет (hex)",
|
||||
help_text="Например: #FF5733"
|
||||
)
|
||||
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
verbose_name="Описание"
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
created_by = models.ForeignKey(
|
||||
CustomUser,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='created_order_statuses',
|
||||
verbose_name="Создано"
|
||||
)
|
||||
|
||||
updated_by = models.ForeignKey(
|
||||
CustomUser,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='updated_order_statuses',
|
||||
verbose_name="Последнее изменение"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Статус заказа"
|
||||
verbose_name_plural = "Статусы заказов"
|
||||
ordering = ['order', 'name']
|
||||
indexes = [
|
||||
models.Index(fields=['code']),
|
||||
models.Index(fields=['is_system']),
|
||||
models.Index(fields=['order']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Address(models.Model):
|
||||
"""
|
||||
Модель адреса доставки для заказа цветочного магазина в Минске.
|
||||
@@ -233,23 +326,22 @@ class Order(models.Model):
|
||||
)
|
||||
|
||||
# Статус заказа
|
||||
STATUS_CHOICES = [
|
||||
('draft', 'Черновик'),
|
||||
('new', 'Новый'),
|
||||
('confirmed', 'Подтвержден'),
|
||||
('in_assembly', 'В сборке'),
|
||||
('in_delivery', 'В доставке'),
|
||||
('delivered', 'Доставлен'),
|
||||
('cancelled', 'Отменен'),
|
||||
]
|
||||
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=STATUS_CHOICES,
|
||||
default='new',
|
||||
status = models.ForeignKey(
|
||||
'OrderStatus',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='orders',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Статус заказа"
|
||||
)
|
||||
|
||||
# Флаг для отслеживания возвратов
|
||||
is_returned = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name="Возвращен",
|
||||
help_text="True если заказ был выполнен, но потом отменен или возвращен клиентом"
|
||||
)
|
||||
|
||||
# Автосохранение (для черновиков)
|
||||
last_autosave_at = models.DateTimeField(
|
||||
null=True,
|
||||
@@ -496,7 +588,7 @@ class Order(models.Model):
|
||||
|
||||
def is_draft(self):
|
||||
"""Проверяет, является ли заказ черновиком"""
|
||||
return self.status == 'draft'
|
||||
return self.status and self.status.code == 'draft'
|
||||
|
||||
@property
|
||||
def amount_due(self):
|
||||
|
||||
Reference in New Issue
Block a user