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>
221 lines
8.2 KiB
Python
221 lines
8.2 KiB
Python
"""
|
||
Сервис для управления статусами заказов.
|
||
Содержит бизнес-логику для работы со статусами и их переходами.
|
||
"""
|
||
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)
|