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:
220
myproject/orders/services/order_status_service.py
Normal file
220
myproject/orders/services/order_status_service.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""
|
||||
Сервис для управления статусами заказов.
|
||||
Содержит бизнес-логику для работы со статусами и их переходами.
|
||||
"""
|
||||
from django.db import transaction
|
||||
from orders.models import OrderStatus, Order
|
||||
|
||||
|
||||
class OrderStatusService:
|
||||
"""Сервис для работы со статусами заказов"""
|
||||
|
||||
@staticmethod
|
||||
def get_default_status():
|
||||
"""
|
||||
Возвращает статус по умолчанию для новых заказов ('new')
|
||||
"""
|
||||
try:
|
||||
return OrderStatus.objects.get(code='new', is_system=True)
|
||||
except OrderStatus.DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_draft_status():
|
||||
"""Возвращает системный статус 'draft' (черновик)"""
|
||||
try:
|
||||
return OrderStatus.objects.get(code='draft', is_system=True)
|
||||
except OrderStatus.DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_system_status(code):
|
||||
"""Получить системный статус по коду"""
|
||||
try:
|
||||
return OrderStatus.objects.get(code=code, is_system=True)
|
||||
except OrderStatus.DoesNotExist:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def create_default_statuses():
|
||||
"""
|
||||
Создает системные статусы для тенанта.
|
||||
Вызывается при первом использовании или миграции.
|
||||
"""
|
||||
default_statuses = [
|
||||
{
|
||||
'code': 'draft',
|
||||
'name': 'Черновик',
|
||||
'label': 'Черновик',
|
||||
'is_system': True,
|
||||
'order': 0,
|
||||
'color': '#9E9E9E',
|
||||
'description': 'Заказ находится в процессе создания/редактирования'
|
||||
},
|
||||
{
|
||||
'code': 'new',
|
||||
'name': 'Новый',
|
||||
'label': 'Новый',
|
||||
'is_system': True,
|
||||
'order': 10,
|
||||
'color': '#2196F3',
|
||||
'description': 'Новый заказ, ожидающий обработки'
|
||||
},
|
||||
{
|
||||
'code': 'confirmed',
|
||||
'name': 'Подтвержден',
|
||||
'label': 'Подтвержден',
|
||||
'is_system': True,
|
||||
'order': 20,
|
||||
'color': '#FF9800',
|
||||
'description': 'Заказ подтвержден и одобрен'
|
||||
},
|
||||
{
|
||||
'code': 'in_assembly',
|
||||
'name': 'В сборке',
|
||||
'label': 'В сборке',
|
||||
'is_system': True,
|
||||
'order': 30,
|
||||
'color': '#FF9800',
|
||||
'description': 'Заказ находится в процессе сборки/подготовки'
|
||||
},
|
||||
{
|
||||
'code': 'in_delivery',
|
||||
'name': 'В доставке',
|
||||
'label': 'В доставке',
|
||||
'is_system': True,
|
||||
'order': 40,
|
||||
'color': '#9C27B0',
|
||||
'description': 'Заказ в пути к клиенту'
|
||||
},
|
||||
{
|
||||
'code': 'completed',
|
||||
'name': 'Выполнен',
|
||||
'label': 'Выполнен',
|
||||
'is_system': True,
|
||||
'is_positive_end': True,
|
||||
'order': 50,
|
||||
'color': '#4CAF50',
|
||||
'description': 'Заказ успешно доставлен/выполнен'
|
||||
},
|
||||
{
|
||||
'code': 'return',
|
||||
'name': 'Возврат',
|
||||
'label': 'Возврат',
|
||||
'is_system': True,
|
||||
'order': 60,
|
||||
'color': '#FF5722',
|
||||
'description': 'Заказ возвращен клиентом'
|
||||
},
|
||||
{
|
||||
'code': 'cancelled',
|
||||
'name': 'Отменен',
|
||||
'label': 'Отменен',
|
||||
'is_system': True,
|
||||
'is_negative_end': True,
|
||||
'order': 70,
|
||||
'color': '#F44336',
|
||||
'description': 'Заказ отменен'
|
||||
},
|
||||
]
|
||||
|
||||
for status_data in default_statuses:
|
||||
OrderStatus.objects.get_or_create(
|
||||
code=status_data['code'],
|
||||
defaults=status_data
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@transaction.atomic
|
||||
def change_order_status(order, new_status, user, notes=""):
|
||||
"""
|
||||
Меняет статус заказа и выполняет соответствующую бизнес-логику.
|
||||
|
||||
Args:
|
||||
order (Order): Экземпляр заказа
|
||||
new_status (OrderStatus): Новый статус
|
||||
user (CustomUser): Пользователь, делающий изменение
|
||||
notes (str): Опциональные заметки
|
||||
|
||||
Returns:
|
||||
Order: Обновленный экземпляр заказа
|
||||
|
||||
Raises:
|
||||
ValueError: Если статус не может быть применен
|
||||
"""
|
||||
old_status = order.status
|
||||
order.status = new_status
|
||||
order.modified_by = user
|
||||
order.save()
|
||||
|
||||
# Запустить бизнес-логику в зависимости от нового статуса
|
||||
if new_status.code == 'completed':
|
||||
_handle_order_completion(order, user)
|
||||
|
||||
elif new_status.code == 'cancelled':
|
||||
_handle_order_cancellation(order, old_status, user)
|
||||
|
||||
elif new_status.code == 'return':
|
||||
_handle_order_return(order, user)
|
||||
|
||||
return order
|
||||
|
||||
@staticmethod
|
||||
def get_all_statuses():
|
||||
"""Возвращает все статусы, отсортированные по порядку"""
|
||||
return OrderStatus.objects.all().order_by('order', 'name')
|
||||
|
||||
@staticmethod
|
||||
def get_system_statuses():
|
||||
"""Возвращает только системные статусы"""
|
||||
return OrderStatus.objects.filter(is_system=True).order_by('order')
|
||||
|
||||
@staticmethod
|
||||
def get_custom_statuses():
|
||||
"""Возвращает только пользовательские статусы"""
|
||||
return OrderStatus.objects.filter(is_system=False).order_by('order', 'name')
|
||||
|
||||
|
||||
def _handle_order_completion(order, user):
|
||||
"""
|
||||
Обработка при переводе в статус 'Выполнен'.
|
||||
Здесь происходит списание товаров со склада.
|
||||
|
||||
TODO: Интеграция с inventory приложением
|
||||
"""
|
||||
# from inventory.services import InventoryService
|
||||
# InventoryService.process_order_completion(order)
|
||||
pass
|
||||
|
||||
|
||||
def _handle_order_cancellation(order, old_status, user):
|
||||
"""
|
||||
Обработка при переводе в статус 'Отменен'.
|
||||
Если заказ был выполнен - возвращаем товары и деньги.
|
||||
"""
|
||||
if old_status and old_status.code == 'completed':
|
||||
# Заказ был выполнен - нужно вернуть товары и деньги
|
||||
order.is_returned = True
|
||||
order.save()
|
||||
|
||||
# TODO: Интеграция с inventory - возврат товаров
|
||||
# InventoryService.process_order_return(order)
|
||||
|
||||
# TODO: Интеграция с платежами - создать возврат
|
||||
# PaymentService.create_refund(order)
|
||||
else:
|
||||
# Заказ отменен до выполнения - просто отменить резервы
|
||||
# TODO: InventoryService.cancel_order_reservation(order)
|
||||
pass
|
||||
|
||||
|
||||
def _handle_order_return(order, user):
|
||||
"""
|
||||
Обработка при переводе в статус 'Возврат'.
|
||||
Это промежуточный статус перед окончательной отменой.
|
||||
"""
|
||||
order.is_returned = True
|
||||
order.save()
|
||||
|
||||
# TODO: Интеграция с inventory - вернуть товары на склад
|
||||
# InventoryService.process_order_return(order)
|
||||
Reference in New Issue
Block a user