1
0
forked from erp-dev/erp

feat: added print_count api, added merchant setting model, added manual mode setting for purchase order

This commit is contained in:
2025-11-28 15:01:11 +08:00
parent 53f2a24b20
commit 0c62398120
36 changed files with 1634 additions and 478 deletions

View File

@@ -2,9 +2,99 @@
Printing API 序列化器
"""
from rest_framework import serializers
from api_v1.models import UploadedFile
from printing import models
from .services import PrintingOrderService, PrintingJobService
from basic_info.models import Customer, Employee
from django.conf import settings
def _build_absolute_media_url(url: str | None, request):
if not url:
return None
if isinstance(url, str) and url.startswith(('http://', 'https://')):
return url
if not isinstance(url, str):
return None
if request:
if url.startswith('/'):
return request.build_absolute_uri(url)
media_prefix = (settings.MEDIA_URL or '/media/').rstrip('/')
return request.build_absolute_uri(f'{media_prefix}/{url.lstrip("/")}')
return url
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):
@@ -20,10 +110,10 @@ class PrintingOrderListSerializer(serializers.ModelSerializer):
'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',
'process', 'process_name', 'progress', 'position', 'print_count',
'created_at', 'updated_at',
]
read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress']
read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress', 'print_count']
class PrintingOrderDetailSerializer(serializers.ModelSerializer):
@@ -42,10 +132,10 @@ class PrintingOrderDetailSerializer(serializers.ModelSerializer):
'is_fabric_received', 'craft', 'description', 'outgoing_date',
'curve', 'new_curve', 'position',
'printing_warn', 'rolling_warn', 'production_warn',
'is_invalid', 'process', 'process_name', 'progress',
'is_invalid', 'process', 'process_name', 'progress', 'print_count',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress']
read_only_fields = ['id', 'human_id', 'created_at', 'updated_at', 'progress', 'print_count']
class PrintingOrderCreateUpdateSerializer(serializers.ModelSerializer):
@@ -214,6 +304,7 @@ class PlateOrderDesignCodeMixin:
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
@@ -244,7 +335,7 @@ class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSeria
'sample_rating', 'difficulty_rating',
'sample_meter', 'required_sample_meters',
'required_completion_date', 'completion_date',
'approval_result', 'is_ordered', 'customer_feedback',
'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
'process', 'process_name',
'status', 'status_id', 'is_completed', 'has_started',
'progress_percentage', 'business_object_id',
@@ -252,17 +343,12 @@ class PlateOrderListSerializer(PlateOrderDesignCodeMixin, serializers.ModelSeria
]
read_only_fields = [
'id', 'status', 'progress_percentage',
'created_at', 'updated_at'
'created_at', 'updated_at', 'print_count'
]
def get_plate_image_url(self, obj):
"""获取图片完整URL"""
if obj.plate_image:
request = self.context.get("request")
if request:
return request.build_absolute_uri(obj.plate_image.url)
return obj.plate_image.url
return None
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:
@@ -308,7 +394,7 @@ class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSer
'sample_rating', 'difficulty_rating',
'sample_meter', 'required_sample_meters',
'required_completion_date', 'completion_date',
'approval_result', 'is_ordered', 'customer_feedback',
'approval_result', 'is_ordered', 'customer_feedback', 'print_count',
'process', 'process_name',
'status', 'status_id', 'is_completed', 'has_started',
'progress_percentage', 'business_object_id',
@@ -317,7 +403,7 @@ class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSer
read_only_fields = [
'id', 'status', 'status_id', 'is_completed', 'has_started',
'progress_percentage', 'business_object_id',
'created_at', 'updated_at'
'created_at', 'updated_at', 'print_count'
]
def get_business_object_id(self, obj):
@@ -325,13 +411,8 @@ class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSer
return obj.business_object.id if obj.business_object else None
def get_plate_image_url(self, obj):
"""获取图片完整URL"""
if obj.plate_image:
request = self.context.get("request")
if request:
return request.build_absolute_uri(obj.plate_image.url)
return obj.plate_image.url
return None
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):
"""获取流程名称"""
@@ -347,7 +428,13 @@ class PlateOrderDetailSerializer(PlateOrderDesignCodeMixin, serializers.ModelSer
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 = [
@@ -365,7 +452,10 @@ class PlateOrderCreateUpdateSerializer(serializers.ModelSerializer):
"process"
]
read_only_fields = ["id"]
def validate_plate_image(self, value):
return value or []
def validate_customer(self, value):
"""验证客户是否存在"""
if not value:
@@ -415,18 +505,18 @@ class PlateOrderCreateUpdateSerializer(serializers.ModelSerializer):
return attrs
def create(self, validated_data):
"""创建开版订单"""
user = self.context["request"].user
# 创建 PlateOrdersave 方法会自动创建 BusinessObject
plate_order = models.PlateOrder.objects.create(**validated_data)
return plate_order
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):
"""更新开版订单"""
# 直接更新字段save 方法会自动处理 BusinessObject 的创建
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
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)