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

512 lines
21 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 序列化器
"""
from rest_framework import serializers
from api_v1.models import UploadedFile
from api_v1.utils.media import build_public_media_url
from printing import models
from .services import PrintingOrderService, PrintingJobService
from basic_info.models import Customer, Employee
def _build_absolute_media_url(url: str | None, request):
return build_public_media_url(url, request=request)
def _serialize_plate_images(raw_value, request):
if not raw_value:
return []
serialized = []
if isinstance(raw_value, list):
iterable = raw_value
elif isinstance(raw_value, dict):
iterable = [raw_value]
elif isinstance(raw_value, str):
iterable = [{'url': raw_value}]
else:
iterable = []
for entry in iterable:
if isinstance(entry, str):
data = {'url': entry}
elif isinstance(entry, dict):
data = dict(entry)
else:
continue
url = data.get('url') or data.get('path')
data['url'] = _build_absolute_media_url(url, request)
if 'path' not in data and isinstance(url, str):
data['path'] = url
serialized.append(data)
return serialized
def _build_plate_image_payload(items, request_user):
if items is None:
return None
if not items:
return []
file_ids = [item['file_id'] for item in items if item.get('file_id') is not None]
if not file_ids:
return []
queryset = UploadedFile.objects.filter(id__in=file_ids, is_deleted=False)
if request_user and request_user.is_authenticated:
queryset = queryset.filter(owner=request_user)
files_map = {file.id: file for file in queryset}
missing = [str(fid) for fid in file_ids if fid not in files_map]
if missing:
raise serializers.ValidationError({'plate_image': f'以下文件不存在或已删除: {", ".join(missing)}'})
payload = []
for item in items:
file = files_map[item['file_id']]
name = item.get('name') or file.original_filename or file.path.name
payload.append({
'file_id': file.id,
'name': name,
'path': file.path.name,
'url': file.file_url,
'size': file.file_size,
'content_type': file.content_type,
'uploaded_at': file.created_at.isoformat(),
})
return payload
class PlateImageInputSerializer(serializers.Serializer):
file_id = serializers.IntegerField(min_value=1, help_text='上传文件的 ID')
name = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
help_text='可选的图片名称,默认使用文件原始名称'
)
class PrintingOrderListSerializer(serializers.ModelSerializer):
"""印染订单列表序列化器"""
customer_name = serializers.CharField(source='customer.name', read_only=True)
customer_phone = serializers.CharField(source='customer.mobile', read_only=True)
process_name = serializers.CharField(source='process.name', read_only=True)
progress = serializers.IntegerField(read_only=True)
class Meta:
model = models.PrintingOrder
fields = [
'id', 'human_id', 'customer', 'customer_name', 'customer_phone',
'fabric', 'width', 'is_urgent', 'area', 'address', 'curve',
'is_fabric_received', 'outgoing_date', 'is_invalid', 'new_curve',
'process', 'process_name', 'progress', 'position', 'print_count',
'created_at', 'updated_at',
]
read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress', 'print_count']
class PrintingOrderDetailSerializer(serializers.ModelSerializer):
"""印染订单详情序列化器"""
customer_name = serializers.CharField(source='customer.name', read_only=True)
customer_phone = serializers.CharField(source='customer.mobile', read_only=True)
customer_area = serializers.CharField(source='customer.area', read_only=True)
process_name = serializers.CharField(source='process.name', read_only=True)
progress = serializers.IntegerField(read_only=True)
class Meta:
model = models.PrintingOrder
fields = [
'id', 'human_id', 'customer', 'customer_name', 'customer_phone', 'customer_area',
'fabric', 'width', 'is_urgent', 'area', 'address', 'fabric_source',
'is_fabric_received', 'craft', 'description', 'outgoing_date',
'curve', 'new_curve', 'position',
'printing_warn', 'rolling_warn', 'production_warn',
'is_invalid', 'process', 'process_name', 'progress', 'print_count',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress', 'print_count']
class PrintingOrderCreateUpdateSerializer(serializers.ModelSerializer):
"""印染订单创建/更新序列化器"""
class Meta:
model = models.PrintingOrder
fields = [
'id', 'customer', 'fabric', 'width', 'is_urgent', 'area', 'address',
'fabric_source', 'is_fabric_received', 'craft', 'description',
'outgoing_date', 'curve', 'new_curve', 'position',
'printing_warn', 'rolling_warn', 'production_warn', 'is_invalid',
'process'
]
read_only_fields = ['id']
def validate_customer(self, value):
"""验证客户是否存在"""
if not value:
raise serializers.ValidationError("客户不能为空")
return value
def validate(self, attrs):
"""验证流程修改权限"""
# 更新时验证 process 修改权限
if self.instance and 'process' in attrs:
new_process = attrs['process']
old_process = self.instance.process
if new_process != old_process:
if not PrintingOrderService.can_change_process(self.instance):
raise serializers.ValidationError({
'process': '存在已开始的印染任务,无法修改流程'
})
return attrs
def create(self, validated_data):
"""创建订单,使用 service 层"""
user = self.context['request'].user
return PrintingOrderService.create_printing_order(validated_data, user)
def update(self, instance, validated_data):
"""更新订单,使用 service 层"""
user = self.context['request'].user
success, message, updated_instance = PrintingOrderService.update_printing_order(
instance, validated_data, user
)
if not success:
raise serializers.ValidationError(message)
return updated_instance
class PrintingJobListSerializer(serializers.ModelSerializer):
"""印染款式明细列表序列化器"""
printing_order_id = serializers.CharField(source='printing_order.human_id', read_only=True)
product_name = serializers.CharField(source='product.name', read_only=True)
status = serializers.CharField(read_only=True)
is_completed = serializers.BooleanField(read_only=True)
business_object_id = serializers.SerializerMethodField()
class Meta:
model = models.PrintingJob
fields = [
'id', 'printing_order', 'printing_order_id', 'product', 'product_name',
'quantity', 'unit', 'size', 'pieces', 'description',
'status', 'is_completed', 'business_object_id',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'status', 'is_completed', 'business_object_id']
def get_business_object_id(self, obj):
"""安全地获取 business_object_id"""
return obj.business_object.id if obj.business_object else None
class PrintingJobDetailSerializer(serializers.ModelSerializer):
"""印染款式明细详情序列化器"""
printing_order_id = serializers.CharField(source='printing_order.human_id', read_only=True)
product_name = serializers.CharField(source='product.name', read_only=True)
product_code = serializers.CharField(source='product.human_id', read_only=True)
status = serializers.CharField(read_only=True)
status_id = serializers.IntegerField(read_only=True)
is_completed = serializers.BooleanField(read_only=True)
has_started = serializers.BooleanField(read_only=True)
business_object_id = serializers.SerializerMethodField()
class Meta:
model = models.PrintingJob
fields = [
'id', 'printing_order', 'printing_order_id', 'product', 'product_name', 'product_code',
'quantity', 'unit', 'size', 'pieces', 'description',
'status', 'status_id', 'is_completed', 'has_started', 'business_object_id',
'created_at', 'updated_at'
]
read_only_fields = [
'id', 'created_at', 'updated_at',
'status', 'status_id', 'is_completed', 'has_started', 'business_object_id'
]
def get_business_object_id(self, obj):
"""安全地获取 business_object_id"""
return obj.business_object.id if obj.business_object else None
class PrintingJobCreateUpdateSerializer(serializers.ModelSerializer):
"""印染款式明细创建/更新序列化器"""
class Meta:
model = models.PrintingJob
fields = [
'id', 'printing_order', 'product', 'quantity', 'unit', 'size', 'pieces', 'description'
]
read_only_fields = ['id']
extra_kwargs = {
'size': {'required': False, 'allow_null': True, 'allow_blank': True},
'pieces': {'required': False, 'allow_null': True},
'description': {'required': False, 'allow_null': True, 'allow_blank': True},
}
def validate_printing_order(self, value):
"""验证印染订单是否存在"""
if not value:
raise serializers.ValidationError("印染订单不能为空")
return value
def validate_product(self, value):
"""验证产品是否存在"""
if not value:
raise serializers.ValidationError("产品不能为空")
return value
def validate_quantity(self, value):
"""验证数量"""
if value <= 0:
raise serializers.ValidationError("数量必须大于0")
return value
def create(self, validated_data):
"""创建任务,使用 service 层"""
user = self.context['request'].user
return PrintingJobService.create_printing_job(validated_data, user)
def update(self, instance, validated_data):
"""更新任务,使用 service 层"""
user = self.context['request'].user
success, message, updated_instance = PrintingJobService.update_printing_job(
instance, validated_data, user
)
if not success:
raise serializers.ValidationError(message)
return updated_instance
class PlateOrderDesignCodeMixin:
"""确保 design_code 为空时使用主键"""
@staticmethod
def _normalize_design_code(design_code: str | None, instance_id: int | None) -> str | None:
if design_code:
return design_code
if instance_id:
return str(instance_id)
return design_code
def to_representation(self, instance):
data = super().to_representation(instance)
data['design_code'] = self._normalize_design_code(data.get('design_code'), instance.id)
data['plate_image'] = _serialize_plate_images(getattr(instance, 'plate_image', None), self.context.get('request'))
return data
class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSerializer):
"""开版订单列表序列化器"""
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)
designer_name = serializers.CharField(source="designer.name", read_only=True)
status = serializers.CharField(read_only=True)
progress_percentage = serializers.IntegerField(read_only=True)
process_name = serializers.SerializerMethodField()
plate_image_url = serializers.SerializerMethodField()
class Meta:
model = models.PlateOrder
fields = [
'id', 'design_code', 'plate_type', 'plate_date', 'plate_method',
'plate_image', 'plate_image_url', 'image_name', 'plate_notes', 'reprint_reason',
'urgency_level', 'is_invalid',
'customer', 'customer_name', 'area', 'default_address',
'salesperson', 'salesperson_name',
'merchandiser', 'merchandiser_name',
'designer', 'designer_name',
'style_name', 'fabric', 'fabric_source', 'width', 'production_method',
'is_mark_frame', 'drawing_rating', 'color_matching_rating',
'sample_rating', 'difficulty_rating',
'sample_meter', 'required_sample_meters',
'required_completion_date', 'completion_date',
'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
'process', 'process_name',
'status', 'status_id', 'is_completed', 'has_started',
'progress_percentage', 'business_object_id',
'created_at', 'updated_at'
]
read_only_fields = [
'id', 'status', 'progress_percentage',
'created_at', 'updated_at', 'print_count'
]
def get_plate_image_url(self, obj):
images = _serialize_plate_images(getattr(obj, 'plate_image', None), self.context.get('request'))
return [entry.get('url') for entry in images if entry.get('url')]
def get_process_name(self, obj) ->str | None:
"""获取流程名称"""
if obj.process:
try:
from stateflow.models import Process
process = Process.objects.get(id=obj.process)
return process.name
except Process.DoesNotExist:
return None
return None
class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSerializer):
"""开版订单详情序列化器"""
customer_name = serializers.CharField(source="customer.name", read_only=True)
customer_phone = serializers.CharField(source="customer.mobile", read_only=True)
salesperson_name = serializers.CharField(source="salesperson.name", read_only=True)
merchandiser_name = serializers.CharField(source="merchandiser.name", read_only=True)
designer_name = serializers.CharField(source="designer.name", read_only=True)
status = serializers.CharField(read_only=True)
status_id = serializers.IntegerField(read_only=True)
is_completed = serializers.BooleanField(read_only=True)
has_started = serializers.BooleanField(read_only=True)
progress_percentage = serializers.IntegerField(read_only=True)
business_object_id = serializers.SerializerMethodField()
plate_image_url = serializers.SerializerMethodField()
process_name = serializers.SerializerMethodField()
class Meta:
model = models.PlateOrder
fields = [
'id', 'design_code', 'plate_type', 'plate_date', 'plate_method',
'plate_image', 'plate_image_url', 'image_name', 'plate_notes', 'reprint_reason',
'urgency_level', 'is_invalid',
'customer', 'customer_name', 'customer_phone', 'area', 'default_address',
'salesperson', 'salesperson_name',
'merchandiser', 'merchandiser_name',
'designer', 'designer_name',
'style_name', 'fabric', 'fabric_source', 'width', 'production_method',
'is_mark_frame', 'drawing_rating', 'color_matching_rating',
'sample_rating', 'difficulty_rating',
'sample_meter', 'required_sample_meters',
'required_completion_date', 'completion_date',
'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
'process', 'process_name',
'status', 'status_id', 'is_completed', 'has_started',
'progress_percentage', 'business_object_id',
'created_at', 'updated_at'
]
read_only_fields = [
'id', 'status', 'status_id', 'is_completed', 'has_started',
'progress_percentage', 'business_object_id',
'created_at', 'updated_at', 'print_count'
]
def get_business_object_id(self, obj):
"""安全地获取 business_object_id"""
return obj.business_object.id if obj.business_object else None
def get_plate_image_url(self, obj):
images = _serialize_plate_images(getattr(obj, 'plate_image', None), self.context.get('request'))
return [entry.get('url') for entry in images if entry.get('url')]
def get_process_name(self, obj):
"""获取流程名称"""
if obj.process:
try:
from stateflow.models import Process
process = Process.objects.get(id=obj.process)
return process.name
except Process.DoesNotExist:
return None
return None
class PlateOrderCreateUpdateSerializer(serializers.ModelSerializer):
"""开版订单创建/更新序列化器"""
plate_image = PlateImageInputSerializer(
many=True,
required=False,
allow_null=True,
help_text="开版图片列表,需提供已上传的 file_id可选 name 字段"
)
class Meta:
model = models.PlateOrder
fields = [
"id", "design_code", "plate_type", "plate_date", "plate_method",
"plate_image", "image_name", "plate_notes", "reprint_reason",
"urgency_level", "is_invalid",
"customer", "area", "default_address",
"salesperson", "merchandiser", "designer",
"style_name", "fabric", "fabric_source", "width", "production_method",
"is_mark_frame", "drawing_rating", "color_matching_rating",
"sample_rating", "difficulty_rating",
"sample_meter", "required_sample_meters",
"required_completion_date", "completion_date",
"approval_result", "is_ordered", "customer_feedback",
"process"
]
read_only_fields = ["id"]
def validate_plate_image(self, value):
return value or []
def validate_customer(self, value):
"""验证客户是否存在"""
if not value:
raise serializers.ValidationError("客户不能为空")
return value
def validate_salesperson(self, value):
"""验证业务员是否存在"""
if value and not Employee.objects.filter(id=value.id).exists():
raise serializers.ValidationError("业务员不存在")
return value
def validate_merchandiser(self, value):
"""验证跟单员是否存在"""
if value and not Employee.objects.filter(id=value.id).exists():
raise serializers.ValidationError("跟单员不存在")
return value
def validate_designer(self, value):
"""验证设计师是否存在"""
if value and not Employee.objects.filter(id=value.id).exists():
raise serializers.ValidationError("设计师不存在")
return value
def validate_sample_meter(self, value):
"""验证样品米数"""
if value is not None and value < 0:
raise serializers.ValidationError("样品米数不能为负数")
return value
def validate_required_sample_meters(self, value):
"""验证所需样品米数"""
if value is not None and value < 0:
raise serializers.ValidationError("所需样品米数不能为负数")
return value
def validate_process(self, value):
"""验证流程是否存在"""
if value:
from stateflow.models import Process
if not Process.objects.filter(id=value).exists():
raise serializers.ValidationError("流程不存在")
return value
def validate(self, attrs):
"""交叉验证"""
return attrs
def create(self, validated_data):
plate_images = validated_data.pop('plate_image', None)
if plate_images is not None:
validated_data['plate_image'] = _build_plate_image_payload(plate_images, self._request_user)
return super().create(validated_data)
def update(self, instance, validated_data):
plate_images = validated_data.pop('plate_image', None)
if plate_images is not None:
validated_data['plate_image'] = _build_plate_image_payload(plate_images, self._request_user)
return super().update(instance, validated_data)
@property
def _request_user(self):
request = self.context.get('request')
return getattr(request, 'user', None)