forked from erp-dev/erp
feat: red flush and cost
This commit is contained in:
@@ -54,12 +54,14 @@ class PurchaseOrderSerializer(serializers.ModelSerializer):
|
||||
'total_amount', 'diff_quantity', 'total_quantity',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
'remarks', 'created_at', 'updated_at', 'items', 'quantity_of_rolls',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items', 'supplier_name',
|
||||
'operator_name', 'warehouse_name', 'is_red_flushed',
|
||||
'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
]
|
||||
|
||||
|
||||
@@ -142,6 +144,7 @@ class PurchaseOrderView(StockChangeViewMixin, views.APIView):
|
||||
'id': purchase_order.id,
|
||||
'human_id': purchase_order.human_id,
|
||||
'status': purchase_order.status,
|
||||
'balance_before_snapshot': purchase_order.balance_before_snapshot,
|
||||
'message': '采购单创建成功,等待审批',
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
|
||||
@@ -58,12 +58,14 @@ class PurchaseReturnOrderSerializer(serializers.ModelSerializer):
|
||||
'total_amount', 'diff_quantity', 'total_quantity',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
'remarks', 'created_at', 'updated_at', 'items', 'quantity_of_rolls',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items',
|
||||
'supplier_name', 'operator_name', 'warehouse_name',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
]
|
||||
|
||||
|
||||
@@ -144,6 +146,7 @@ class PurchaseReturnOrderView(StockChangeViewMixin, views.APIView):
|
||||
'id': purchase_return.id,
|
||||
'human_id': purchase_return.human_id,
|
||||
'status': purchase_return.status,
|
||||
'balance_before_snapshot': purchase_return.balance_before_snapshot,
|
||||
'message': '采购退货单创建成功,等待审批',
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
|
||||
@@ -55,12 +55,14 @@ class SalesOrderSerializer(serializers.ModelSerializer):
|
||||
'total_amount', 'diff_quantity', 'total_quantity',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
'remarks', 'created_at', 'updated_at', 'items', 'quantity_of_rolls',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items', 'customer_name',
|
||||
'operator_name', 'warehouse_name', 'is_red_flushed',
|
||||
'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
]
|
||||
|
||||
|
||||
@@ -83,6 +85,29 @@ class SalesOrderView(StockChangeViewMixin, views.APIView):
|
||||
queryset = business_models.SalesOrder.objects.filter(merchant=merchant).prefetch_related(
|
||||
'items', 'customer', 'operator', 'warehouse'
|
||||
)
|
||||
|
||||
# Query 参数过滤
|
||||
qp = request.query_params
|
||||
customer_id = qp.get('customer')
|
||||
warehouse_id = qp.get('warehouse')
|
||||
status_value = qp.get('status')
|
||||
kind = qp.get('kind')
|
||||
|
||||
if customer_id:
|
||||
try:
|
||||
queryset = queryset.filter(customer_id=int(customer_id))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
if warehouse_id:
|
||||
try:
|
||||
queryset = queryset.filter(warehouse_id=int(warehouse_id))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
if status_value:
|
||||
queryset = queryset.filter(status=status_value)
|
||||
if kind:
|
||||
queryset = queryset.filter(kind=kind)
|
||||
|
||||
paginator = self.pagination_class()
|
||||
page = paginator.paginate_queryset(queryset.order_by('-created_at'), request, view=self)
|
||||
serializer = SalesOrderSerializer(page, many=True)
|
||||
@@ -145,6 +170,7 @@ class SalesOrderView(StockChangeViewMixin, views.APIView):
|
||||
'id': sales_order.id,
|
||||
'human_id': sales_order.human_id,
|
||||
'status': sales_order.status,
|
||||
'balance_before_snapshot': sales_order.balance_before_snapshot,
|
||||
'message': '销售单创建成功,等待审批',
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
|
||||
@@ -57,12 +57,14 @@ class SalesReturnOrderSerializer(serializers.ModelSerializer):
|
||||
'total_amount', 'diff_quantity', 'total_quantity', 'quantity_of_rolls',
|
||||
'operator', 'operator_name', 'warehouse', 'warehouse_name',
|
||||
'status', 'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
'remarks', 'created_at', 'updated_at', 'items',
|
||||
]
|
||||
read_only_fields = [
|
||||
'id', 'created_at', 'updated_at', 'items',
|
||||
'customer_name', 'operator_name', 'warehouse_name',
|
||||
'is_red_flushed', 'red_flush_id', 'red_flushed_at',
|
||||
'balance_before_snapshot',
|
||||
]
|
||||
|
||||
|
||||
@@ -143,6 +145,7 @@ class SalesReturnOrderView(StockChangeViewMixin, views.APIView):
|
||||
'id': sales_return.id,
|
||||
'human_id': sales_return.human_id,
|
||||
'status': sales_return.status,
|
||||
'balance_before_snapshot': sales_return.balance_before_snapshot,
|
||||
'message': '销售退货单创建成功,等待审批',
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
|
||||
@@ -303,7 +303,7 @@ class PrintingOrderCreateUpdateSerializer(serializers.ModelSerializer):
|
||||
'fabric_source', 'is_fabric_received', 'craft', 'description',
|
||||
'outgoing_date', 'curve', 'new_curve', 'position',
|
||||
'printing_warn', 'rolling_warn', 'production_warn', 'is_invalid',
|
||||
'process'
|
||||
'process', 'external_order_id',
|
||||
]
|
||||
read_only_fields = ['id']
|
||||
|
||||
@@ -651,6 +651,58 @@ class PlateOrderDesignCodeMixin:
|
||||
return data
|
||||
|
||||
|
||||
class PlateOrderKanbanListSerializer(serializers.ModelSerializer):
|
||||
"""开版订单看板列表序列化器(精简版,仅包含看板实际使用的字段,消除 N+1 查询)"""
|
||||
customer_name = serializers.CharField(source="customer.name", read_only=True)
|
||||
salesperson_name = serializers.CharField(source="salesperson.name", read_only=True)
|
||||
merchandiser_name = serializers.CharField(source="merchandiser.name", read_only=True)
|
||||
created_by_name = serializers.CharField(source="created_by.employee.name", read_only=True, default=None)
|
||||
process_name = serializers.CharField(source="annotated_process_name", read_only=True, default=None)
|
||||
plate_image = serializers.SerializerMethodField()
|
||||
plate_image_url = serializers.SerializerMethodField()
|
||||
last_completed_state = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = models.PlateOrder
|
||||
fields = [
|
||||
'id', 'design_code', 'customer_name', 'area',
|
||||
'salesperson_name', 'merchandiser_name', 'fabric_source', 'fabric',
|
||||
'width', 'plate_type', 'plate_date', 'plate_method',
|
||||
'plate_image', 'plate_image_url', 'plate_notes', 'reprint_reason',
|
||||
'urgency_level', 'production_method', 'is_mark_frame',
|
||||
'drawing_rating', 'color_matching_rating', 'sample_rating',
|
||||
'difficulty_rating', 'style_name', 'required_sample_meters',
|
||||
'required_completion_date', 'completion_date', 'approval_result',
|
||||
'customer_feedback', 'last_completed_state', 'business_object_id',
|
||||
'original_id', 'process_name', 'print_count', 'created_at',
|
||||
'created_by_name',
|
||||
]
|
||||
|
||||
def _get_cached_plate_images(self, obj):
|
||||
cache_attr = '_cached_plate_images'
|
||||
images = getattr(obj, cache_attr, None)
|
||||
if images is None:
|
||||
images = _serialize_plate_images(
|
||||
getattr(obj, 'plate_image', None),
|
||||
self.context.get('request'),
|
||||
)
|
||||
setattr(obj, cache_attr, images)
|
||||
return images
|
||||
|
||||
def get_plate_image(self, obj):
|
||||
return self._get_cached_plate_images(obj)
|
||||
|
||||
def get_plate_image_url(self, obj):
|
||||
images = self._get_cached_plate_images(obj)
|
||||
return [entry.get('url') for entry in images if entry.get('url')]
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
if not data.get('design_code') and instance.id:
|
||||
data['design_code'] = str(instance.id)
|
||||
return data
|
||||
|
||||
|
||||
class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSerializer):
|
||||
"""开版订单列表序列化器"""
|
||||
customer_name = serializers.CharField(source="customer.name", read_only=True)
|
||||
|
||||
@@ -10,7 +10,7 @@ from rest_framework.permissions import BasePermission
|
||||
from rest_framework.permissions import DjangoModelPermissions
|
||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import CharField, Exists, OuterRef, Prefetch
|
||||
from django.db.models import CharField, Exists, OuterRef, Prefetch, Subquery
|
||||
from django.db.models.functions import Cast, Coalesce
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.decorators.http import condition
|
||||
@@ -1065,13 +1065,14 @@ class PlateOrderViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
def get_serializer_class(self):
|
||||
"""根据动作选择序列化器"""
|
||||
from .serializers import (
|
||||
PlateOrderKanbanListSerializer,
|
||||
PlateOrderListSerializer,
|
||||
PlateOrderDetailSerializer,
|
||||
PlateOrderCreateUpdateSerializer,
|
||||
)
|
||||
|
||||
if self.action == "list":
|
||||
return PlateOrderListSerializer
|
||||
return PlateOrderKanbanListSerializer
|
||||
elif self.action in ["create", "update", "partial_update"]:
|
||||
return PlateOrderCreateUpdateSerializer
|
||||
else: # retrieve
|
||||
@@ -1092,13 +1093,24 @@ class PlateOrderViewSet(CustomerVisibilityFilterMixin, LimitedModelViewSet):
|
||||
|
||||
if self.action in ["list", "retrieve"]:
|
||||
queryset = queryset.select_related(
|
||||
"customer", "salesperson", "merchandiser", "business_object"
|
||||
"customer",
|
||||
"salesperson",
|
||||
"merchandiser",
|
||||
"business_object",
|
||||
"created_by__employee",
|
||||
)
|
||||
# 为搜索提供 design_code 的兜底(为空时使用主键字符串)
|
||||
|
||||
# 预取流程名称(process 是 IntegerField 非外键,用 Subquery 避免 N+1)
|
||||
from stateflow.models import Process
|
||||
|
||||
queryset = queryset.annotate(
|
||||
design_code_normalized=Coalesce(
|
||||
"design_code", Cast("id", output_field=CharField())
|
||||
)
|
||||
),
|
||||
annotated_process_name=Subquery(
|
||||
Process.objects.filter(id=OuterRef("process")).values("name")[:1],
|
||||
output_field=CharField(),
|
||||
),
|
||||
)
|
||||
return queryset
|
||||
|
||||
|
||||
Reference in New Issue
Block a user