1
0
forked from erp-dev/erp
Files
erpnew/api_v1/views/printing/views.py

880 lines
33 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.
"""
Printing API ViewSet
"""
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import BasePermission
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.permissions import DjangoModelPermissions
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as django_filters
from django_filters import IsoDateTimeFilter
from printing import models
from basic_info.models import MerchantTypeEnum
from .serializers import (
PrintingOrderListSerializer,
PrintingOrderDetailSerializer,
PrintingOrderCreateUpdateSerializer,
PrintingJobListSerializer,
PrintingJobDetailSerializer,
PrintingJobCreateUpdateSerializer,
)
class IsPrintingFactory(BasePermission):
"""自定义权限类,允许印染工厂用户访问"""
message = '您没有访问印染订单的权限'
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
if hasattr(request.user, 'employee'):
return request.user.employee.merchant.type == MerchantTypeEnum.FACTORY
return False
class HasInvalidatePrintingOrderPermission(BasePermission):
"""自定义权限类,检查用户是否有作废印染订单的权限"""
message = '您没有权限作废订单'
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
return request.user.has_perm('printing.can_invalidate_printingorder')
class HasActivatePrintingOrderPermission(BasePermission):
"""自定义权限类,检查用户是否有恢复印染订单的权限"""
message = '您没有权限恢复订单'
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
return request.user.has_perm('printing.can_activate_printingorder')
class PrintingOrderFilterSet(django_filters.FilterSet):
"""印染订单过滤器"""
customer_name = django_filters.CharFilter(field_name='customer__name', lookup_expr='icontains')
customer_phone = django_filters.CharFilter(field_name='customer__phone', lookup_expr='icontains')
fabric = django_filters.CharFilter(lookup_expr='icontains')
is_urgent = django_filters.BooleanFilter()
is_fabric_received = django_filters.BooleanFilter()
is_invalid = django_filters.BooleanFilter()
area = django_filters.CharFilter(lookup_expr='icontains')
outgoing_date_from = django_filters.DateFilter(field_name='outgoing_date', lookup_expr='gte')
outgoing_date_to = django_filters.DateFilter(field_name='outgoing_date', lookup_expr='lte')
created_date_from = django_filters.DateFilter(field_name='created_at', lookup_expr='gte')
created_date_to = django_filters.DateFilter(field_name='created_at', lookup_expr='lte')
class Meta:
model = models.PrintingOrder
fields = ['customer', 'is_urgent', 'is_fabric_received', 'is_invalid']
class PrintingOrderViewSet(viewsets.ModelViewSet):
"""
印染订单 ViewSet
提供印染订单的增改查功能(不支持删除)
list: 获取印染订单列表
retrieve: 获取印染订单详情
create: 创建印染订单
update: 更新印染订单
partial_update: 部分更新印染订单
查询参数:
- customer: 客户ID
- customer_name: 客户名称(模糊查询)
- customer_phone: 客户电话(模糊查询)
- fabric: 面料(模糊查询)
- is_urgent: 是否紧急true/false
- is_fabric_received: 布料是否已收true/false
- is_invalid: 是否作废true/false
- area: 地区(模糊查询)
- outgoing_date_from: 出货日期起始
- outgoing_date_to: 出货日期结束
- created_date_from: 创建日期起始
- created_date_to: 创建日期结束
- search: 全文搜索(客户名称、面料、地区、工艺)
- ordering: 排序字段
"""
queryset = models.PrintingOrder.objects.all()
permission_classes = [DjangoModelPermissions]
pagination_class = LimitOffsetPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = PrintingOrderFilterSet
search_fields = ['customer__name', 'fabric', 'area', 'craft', 'description']
ordering_fields = [
'id', 'created_at', 'updated_at', 'outgoing_date',
'is_urgent', 'is_fabric_received', 'is_invalid'
]
ordering = ['-created_at']
def get_permissions(self):
permissions = super().get_permissions() + [IsPrintingFactory()]
if self.action == 'invalidate':
permissions.append(HasInvalidatePrintingOrderPermission())
elif self.action == 'activate':
permissions.append(HasActivatePrintingOrderPermission())
return permissions
def get_serializer_class(self):
"""根据动作选择序列化器"""
if self.action == 'list':
return PrintingOrderListSerializer
elif self.action in ['create', 'update', 'partial_update']:
return PrintingOrderCreateUpdateSerializer
else: # retrieve
return PrintingOrderDetailSerializer
def get_queryset(self):
"""优化查询"""
queryset = super().get_queryset()
if self.action in ['list', 'retrieve']:
queryset = queryset.select_related('customer')
return queryset
def destroy(self, request, *args, **kwargs):
"""禁用删除操作"""
return Response(
{'detail': '印染订单不支持删除操作,请使用作废功能'},
status=status.HTTP_405_METHOD_NOT_ALLOWED
)
@action(detail=True, methods=['post'])
def invalidate(self, request, pk=None):
"""
作废订单
需要权限: printing.can_invalidate_printingorder
"""
# 检查权限
if not request.user.has_perm('printing.can_invalidate_printingorder'):
return Response(
{'detail': '您没有权限作废订单'},
status=status.HTTP_403_FORBIDDEN
)
printing_order = self.get_object()
if printing_order.is_invalid:
return Response(
{'detail': '该订单已经作废'},
status=status.HTTP_400_BAD_REQUEST
)
printing_order.is_invalid = True
printing_order.save()
serializer = PrintingOrderDetailSerializer(printing_order)
return Response({
'detail': '订单已作废',
'data': serializer.data
})
@action(detail=True, methods=['post'])
def activate(self, request, pk=None):
"""
恢复订单
需要权限: printing.can_activate_printingorder
"""
# 检查权限
if not request.user.has_perm('printing.can_activate_printingorder'):
return Response(
{'detail': '您没有权限恢复订单'},
status=status.HTTP_403_FORBIDDEN
)
printing_order = self.get_object()
if not printing_order.is_invalid:
return Response(
{'detail': '该订单未作废,无需恢复'},
status=status.HTTP_400_BAD_REQUEST
)
printing_order.is_invalid = False
printing_order.save()
serializer = PrintingOrderDetailSerializer(printing_order)
return Response({
'detail': '订单已恢复',
'data': serializer.data
})
@action(detail=True, methods=['post'])
def mark_fabric_received(self, request, pk=None):
"""标记布料已收"""
printing_order = self.get_object()
if printing_order.is_fabric_received:
return Response(
{'detail': '布料已经标记为已收'},
status=status.HTTP_400_BAD_REQUEST
)
printing_order.is_fabric_received = True
printing_order.save()
serializer = PrintingOrderDetailSerializer(printing_order)
return Response({
'detail': '已标记布料已收',
'data': serializer.data
})
class PrintingJobFilterSet(django_filters.FilterSet):
"""印染款式明细过滤器"""
printing_order = django_filters.NumberFilter()
product = django_filters.NumberFilter()
product_name = django_filters.CharFilter(field_name='product__name', lookup_expr='icontains')
unit = django_filters.CharFilter(lookup_expr='icontains')
quantity_min = django_filters.NumberFilter(field_name='quantity', lookup_expr='gte')
quantity_max = django_filters.NumberFilter(field_name='quantity', lookup_expr='lte')
pieces_min = django_filters.NumberFilter(field_name='pieces', lookup_expr='gte')
pieces_max = django_filters.NumberFilter(field_name='pieces', lookup_expr='lte')
class Meta:
model = models.PrintingJob
fields = ['printing_order', 'product']
class PrintingJobViewSet(viewsets.ModelViewSet):
"""
印染款式明细 ViewSet
提供印染款式明细的增改查功能(不支持删除)
list: 获取款式明细列表
retrieve: 获取款式明细详情
create: 创建款式明细
update: 更新款式明细
partial_update: 部分更新款式明细
advance_to_next_state: 推进到下一个状态
step_back_one_state: 回退一步
completed_states: 查询已完成的流程列表
timeline: 获取流程时间线
注意:状态流转接口是对 stateflow 模块的便捷包装
- 前端只需提供 printing_job 的 ID
- 系统会自动查找关联的 business_object 并进行状态流转
- 也可以直接使用 stateflow 的原始接口(需要 business_object_id
查询参数:
- printing_order: 印染订单ID
- product: 产品ID
- product_name: 产品名称(模糊查询)
- unit: 单位(模糊查询)
- quantity_min: 最小数量
- quantity_max: 最大数量
- pieces_min: 最小件数
- pieces_max: 最大件数
- search: 全文搜索(产品名称、单位、尺寸、备注)
- ordering: 排序字段
"""
queryset = models.PrintingJob.objects.all()
permission_classes = [DjangoModelPermissions, IsPrintingFactory]
pagination_class = LimitOffsetPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = PrintingJobFilterSet
search_fields = ['product__name', 'unit', 'size', 'description']
ordering_fields = ['id', 'created_at', 'updated_at', 'quantity', 'pieces']
ordering = ['-created_at']
def get_serializer_class(self):
"""根据动作选择序列化器"""
if self.action == 'list':
return PrintingJobListSerializer
elif self.action in ['create', 'update', 'partial_update']:
return PrintingJobCreateUpdateSerializer
else: # retrieve
return PrintingJobDetailSerializer
def get_queryset(self):
"""优化查询"""
queryset = super().get_queryset()
if self.action in ['list', 'retrieve']:
queryset = queryset.select_related('printing_order', 'product')
return queryset
def destroy(self, request, *args, **kwargs):
"""禁用删除操作"""
return Response(
{'detail': '印染款式明细不支持删除操作'},
status=status.HTTP_405_METHOD_NOT_ALLOWED
)
@action(detail=True, methods=['post'], url_path='advance-to-next-state')
def advance_to_next_state(self, request, pk=None):
"""
推进到下一个状态
将印染任务推进到下一个流程状态
请求体:
{
"parameters": {
"temperature": "25.5",
"operator": "张三"
}
}
参数可选,但如果下一个状态有必填参数,则必须提供
注意:这是对 stateflow.BusinessObject.advance 接口的便捷包装,
自动通过 printing_job 查找关联的 business_object 并调用状态流转服务。
前端只需提供 printing_job 的 ID无需关心 business_object_id。
"""
job = self.get_object()
if not job.business_object:
return Response(
{'detail': '该任务没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
# 获取请求参数
parameters = request.data.get('parameters', {})
# 调用 stateflow 统一服务进行状态流转
from stateflow import services as stateflow_services
success, message, state_log = stateflow_services.advance_to_next_state(
job.business_object, request.user, **parameters
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 job 以刷新状态
job.refresh_from_db()
serializer = PrintingJobDetailSerializer(job)
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['post'], url_path='step-back-one-state')
def step_back_one_state(self, request, pk=None):
"""
回退一步
将印染任务回退到上一个流程状态
注意:这是对 stateflow.BusinessObject.step_back 接口的便捷包装,
自动通过 printing_job 查找关联的 business_object 并调用状态流转服务。
前端只需提供 printing_job 的 ID无需关心 business_object_id。
"""
job = self.get_object()
if not job.business_object:
return Response(
{'detail': '该任务没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
# 调用 stateflow 统一服务进行状态回退
from stateflow import services as stateflow_services
success, message = stateflow_services.step_back_one_state(
job.business_object, request.user
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 job 以刷新状态
job.refresh_from_db()
serializer = PrintingJobDetailSerializer(job)
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['get'], url_path='completed-states')
def completed_states(self, request, pk=None):
"""
查询已完成的流程列表
查询参数:
- include_cancelled: 是否包含已撤销的流程true/false默认 false
"""
job = self.get_object()
if not job.business_object:
return Response({
'count': 0,
'results': []
})
# 获取查询参数
include_cancelled = request.query_params.get('include_cancelled', 'false').lower() == 'true'
# 查询已完成的状态
state_logs = job.business_object.state_logs.select_related('state', 'completed_by').order_by('completed_at')
if not include_cancelled:
state_logs = state_logs.filter(is_cancelled=False)
results = []
for log in state_logs:
results.append({
'id': log.id,
'state_id': log.state.id,
'state_name': log.state.name,
'completed_at': log.completed_at,
'completed_by': log.completed_by.username if log.completed_by else None,
'completed_by_name': getattr(log.completed_by, 'employee', None) and log.completed_by.employee.name or None,
'is_cancelled': log.is_cancelled,
'cancelled_at': log.cancelled_at,
})
return Response({
'count': len(results),
'results': results
})
@action(detail=True, methods=['get'], url_path='timeline')
def timeline(self, request, pk=None):
"""
获取流程时间线
返回该任务的完整流程时间线,包括未开始、进行中和已完成的状态
不包含已撤销的流程记录
"""
job = self.get_object()
if not job.business_object or not job.printing_order.process:
return Response({
'count': 0,
'results': []
})
from stateflow import services as stateflow_services
timeline_data = stateflow_services.get_business_object_state_timeline(job.business_object)
# 过滤掉已撤销的记录
timeline_data = [item for item in timeline_data if not item.get('is_cancelled')]
results = []
for item in timeline_data:
results.append({
'state_id': item['state'].id,
'state_name': item['state'].name,
'state_description': item['state'].description,
'order': item['order'],
'status': item['status'], # not_started, in_progress, completed
'completed_at': item['completed_at'],
'completed_by': item['completed_by'].username if item['completed_by'] else None,
'completed_by_name': getattr(item['completed_by'], 'employee', None) and item['completed_by'].employee.name or None if item['completed_by'] else None,
})
return Response({
'count': len(results),
'results': results
})
class HasInvalidatePlateOrderPermission(BasePermission):
"""自定义权限类,检查用户是否有作废开版订单的权限"""
message = '您没有权限作废开版订单'
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
return request.user.has_perm('printing.can_invalidate_plateorder')
class HasActivatePlateOrderPermission(BasePermission):
"""自定义权限类,检查用户是否有恢复开版订单的权限"""
message = '您没有权限恢复开版订单'
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False
return request.user.has_perm('printing.can_activate_plateorder')
class PlateOrderFilterSet(django_filters.FilterSet):
"""开版订单过滤器"""
customer_name = django_filters.CharFilter(field_name='customer__name', lookup_expr='icontains')
customer_phone = django_filters.CharFilter(field_name='customer__mobile', lookup_expr='icontains')
image_name = django_filters.CharFilter(lookup_expr='icontains')
salesperson = django_filters.NumberFilter()
merchandiser = django_filters.NumberFilter()
plate_type = django_filters.CharFilter(lookup_expr='icontains')
urgency_level = django_filters.CharFilter(lookup_expr='icontains')
is_invalid = django_filters.BooleanFilter()
is_ordered = django_filters.BooleanFilter()
is_mark_frame = django_filters.BooleanFilter()
fabric = django_filters.CharFilter(lookup_expr='icontains')
style_name = django_filters.CharFilter(lookup_expr='icontains')
plate_date_from = django_filters.DateFilter(field_name='plate_date', lookup_expr='gte')
plate_date_to = django_filters.DateFilter(field_name='plate_date', lookup_expr='lte')
required_completion_date_from = IsoDateTimeFilter(field_name='required_completion_date', lookup_expr='gte')
required_completion_date_to = IsoDateTimeFilter(field_name='required_completion_date', lookup_expr='lte')
created_date_from = django_filters.DateFilter(field_name='created_at', lookup_expr='gte')
created_date_to = django_filters.DateFilter(field_name='created_at', lookup_expr='lte')
class Meta:
model = models.PlateOrder
fields = ['customer', 'salesperson', 'merchandiser', 'urgency_level', 'is_invalid', 'is_ordered']
class PlateOrderViewSet(viewsets.ModelViewSet):
"""
开版订单 ViewSet
提供开版订单的增改查功能(不支持删除)
list: 获取开版订单列表
retrieve: 获取开版订单详情
create: 创建开版订单
image_name: 图片名称(模糊查询)
update: 更新开版订单
partial_update: 部分更新开版订单
invalidate: 作废开版订单
activate: 恢复开版订单
advance_to_next_state: 推进到下一个状态(便捷包装)
step_back_one_state: 回退一步(便捷包装)
completed_states: 查询已完成的流程列表
timeline: 获取流程时间线
注意:状态流转接口是对 stateflow 模块的便捷包装
- 前端只需提供 plate_order 的 ID
- 系统会自动查找关联的 business_object 并进行状态流转
- 也可以直接使用 stateflow 的原始接口(需要 business_object_id
查询参数:
- customer: 客户ID
- customer_name: 客户名称(模糊查询)
- customer_phone: 客户电话(模糊查询)
- salesperson: 业务员ID
- merchandiser: 跟单员ID
- plate_type: 版型(模糊查询)
- urgency_level: 紧急程度1-5
- is_invalid: 是否作废true/false
- is_ordered: 是否已下单true/false
- is_mark_frame: 是否打样架true/false
- fabric: 面料(模糊查询)
- style_name: 款式名称(模糊查询)
- plate_date_from: 开版日期起始
- plate_date_to: 开版日期结束
- required_completion_date_from: 要求完成时间起始(含时分秒)
- required_completion_date_to: 要求完成时间结束
- created_date_from: 创建日期起始
- created_date_to: 创建日期结束
- search: 全文搜索(设计编号、款式名称、客户名称、面料)
- ordering: 排序字段
"""
queryset = models.PlateOrder.objects.all()
permission_classes = [DjangoModelPermissions]
parser_classes = [MultiPartParser, FormParser, JSONParser] # 支持文件上传
pagination_class = LimitOffsetPagination
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = PlateOrderFilterSet
search_fields = ['design_code', 'style_name', 'customer__name', 'fabric']
ordering_fields = [
'id', 'created_at', 'updated_at', 'plate_date',
'required_completion_date', 'completion_date',
'urgency_level', 'is_ordered', 'is_invalid'
]
ordering = ['-created_at']
def get_serializer_class(self):
"""根据动作选择序列化器"""
from .serializers import (
PlateOrderListSerializer,
PlateOrderDetailSerializer,
PlateOrderCreateUpdateSerializer,
)
if self.action == 'list':
return PlateOrderListSerializer
elif self.action in ['create', 'update', 'partial_update']:
return PlateOrderCreateUpdateSerializer
else: # retrieve
return PlateOrderDetailSerializer
def get_queryset(self):
"""优化查询"""
queryset = super().get_queryset()
if self.action in ['list', 'retrieve']:
queryset = queryset.select_related('customer', 'salesperson', 'merchandiser', 'business_object')
return queryset
def destroy(self, request, *args, **kwargs):
"""禁用删除操作"""
return Response(
{'detail': '开版订单不支持删除操作,请使用作废功能'},
status=status.HTTP_405_METHOD_NOT_ALLOWED
)
@action(detail=True, methods=['post'])
def invalidate(self, request, pk=None):
"""
作废开版订单
需要权限: printing.can_invalidate_plateorder
"""
# 检查权限
if not request.user.has_perm('printing.can_invalidate_plateorder'):
return Response(
{'detail': '您没有权限作废开版订单'},
status=status.HTTP_403_FORBIDDEN
)
plate_order = self.get_object()
if plate_order.is_invalid:
return Response(
{'detail': '该开版订单已经作废'},
status=status.HTTP_400_BAD_REQUEST
)
plate_order.is_invalid = True
plate_order.save()
from .serializers import PlateOrderDetailSerializer
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
return Response({
'detail': '开版订单已作废',
'data': serializer.data
})
@action(detail=True, methods=['post'])
def activate(self, request, pk=None):
"""
恢复开版订单
需要权限: printing.can_activate_plateorder
"""
# 检查权限
if not request.user.has_perm('printing.can_activate_plateorder'):
return Response(
{'detail': '您没有权限恢复开版订单'},
status=status.HTTP_403_FORBIDDEN
)
plate_order = self.get_object()
if not plate_order.is_invalid:
return Response(
{'detail': '该开版订单未作废,无需恢复'},
status=status.HTTP_400_BAD_REQUEST
)
plate_order.is_invalid = False
plate_order.save()
from .serializers import PlateOrderDetailSerializer
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
return Response({
'detail': '开版订单已恢复',
'data': serializer.data
})
@action(detail=True, methods=['post'], url_path='advance-to-next-state')
def advance_to_next_state(self, request, pk=None):
"""
推进到下一个状态
将开版订单推进到下一个流程状态
请求体:
{
"parameters": {
"inspector": "李四",
"quality_score": "95"
}
}
参数可选,但如果下一个状态有必填参数,则必须提供
注意:这是对 stateflow.BusinessObject.advance 接口的便捷包装,
自动通过 plate_order 查找关联的 business_object 并调用状态流转服务。
"""
plate_order = self.get_object()
if not plate_order.business_object:
return Response(
{'detail': '该开版订单没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
# 获取请求参数
parameters = request.data.get('parameters', {})
# 调用 stateflow 统一服务进行状态流转
from stateflow import services as stateflow_services
success, message, state_log = stateflow_services.advance_to_next_state(
plate_order.business_object, request.user, **parameters
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 plate_order 以刷新状态
plate_order.refresh_from_db()
from .serializers import PlateOrderDetailSerializer
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['post'], url_path='step-back-one-state')
def step_back_one_state(self, request, pk=None):
"""
回退一步
将开版订单回退到上一个流程状态
注意:这是对 stateflow.BusinessObject.step_back 接口的便捷包装,
自动通过 plate_order 查找关联的 business_object 并调用状态流转服务。
"""
plate_order = self.get_object()
if not plate_order.business_object:
return Response(
{'detail': '该开版订单没有关联的流程实例'},
status=status.HTTP_400_BAD_REQUEST
)
# 调用 stateflow 统一服务进行状态回退
from stateflow import services as stateflow_services
success, message = stateflow_services.step_back_one_state(
plate_order.business_object, request.user
)
if not success:
return Response(
{'detail': message},
status=status.HTTP_400_BAD_REQUEST
)
# 重新获取 plate_order 以刷新状态
plate_order.refresh_from_db()
from .serializers import PlateOrderDetailSerializer
serializer = PlateOrderDetailSerializer(plate_order, context={'request': request})
return Response({
'detail': message,
'data': serializer.data
})
@action(detail=True, methods=['get'], url_path='completed-states')
def completed_states(self, request, pk=None):
"""
查询已完成的流程列表
查询参数:
- include_cancelled: 是否包含已撤销的流程true/false默认 false
"""
plate_order = self.get_object()
if not plate_order.business_object:
return Response({
'count': 0,
'results': []
})
# 获取查询参数
include_cancelled = request.query_params.get('include_cancelled', 'false').lower() == 'true'
# 查询已完成的状态
state_logs = plate_order.business_object.state_logs.select_related('state', 'completed_by').order_by('completed_at')
if not include_cancelled:
state_logs = state_logs.filter(is_cancelled=False)
results = []
for log in state_logs:
results.append({
'id': log.id,
'state_id': log.state.id,
'state_name': log.state.name,
'completed_at': log.completed_at,
'completed_by': log.completed_by.username if log.completed_by else None,
'completed_by_name': getattr(log.completed_by, 'employee', None) and log.completed_by.employee.name or None,
'is_cancelled': log.is_cancelled,
'cancelled_at': log.cancelled_at,
})
return Response({
'count': len(results),
'results': results
})
@action(detail=True, methods=['get'], url_path='timeline')
def timeline(self, request, pk=None):
"""
获取流程时间线
返回该开版订单的完整流程时间线,包括未开始、进行中和已完成的状态
不包含已撤销的流程记录
"""
plate_order = self.get_object()
if not plate_order.business_object:
return Response({
'count': 0,
'results': []
})
from stateflow import services as stateflow_services
# 获取流程
from stateflow.models import Process
try:
process = Process.objects.get(id=plate_order.process)
except Process.DoesNotExist:
return Response({
'count': 0,
'results': []
})
timeline_data = stateflow_services.get_business_object_state_timeline(plate_order.business_object)
# 过滤掉已撤销的记录
timeline_data = [item for item in timeline_data if not item.get('is_cancelled')]
results = []
for item in timeline_data:
results.append({
'state_id': item['state'].id,
'state_name': item['state'].name,
'state_description': item['state'].description,
'order': item['order'],
'status': item['status'], # not_started, in_progress, completed
'completed_at': item['completed_at'],
'completed_by': item['completed_by'].username if item['completed_by'] else None,
'completed_by_name': getattr(item['completed_by'], 'employee', None) and item['completed_by'].employee.name or None if item['completed_by'] else None,
})
return Response({
'count': len(results),
'results': results
})