forked from erp-dev/erp
218 lines
7.9 KiB
Python
218 lines
7.9 KiB
Python
from rest_framework import serializers
|
||
from decimal import Decimal
|
||
from typing import List, Dict, Any
|
||
from stock import models as stock_models
|
||
from basic_info import models as basic_info_models
|
||
from . import models as api_models
|
||
from .enums import PrintCountObjectType
|
||
|
||
|
||
class ProductStockChangeSerializer(serializers.Serializer):
|
||
"""产品库存变动序列化器"""
|
||
product = serializers.IntegerField(help_text="产品ID")
|
||
quantity = serializers.ListField(
|
||
child=serializers.DecimalField(max_digits=10, decimal_places=2, min_value=Decimal('0.01')),
|
||
min_length=1,
|
||
required=False,
|
||
help_text="数量列表,每个数量对应一条明细记录"
|
||
)
|
||
consume_with = serializers.ListField(
|
||
child=serializers.IntegerField(min_value=1),
|
||
required=False,
|
||
allow_empty=False,
|
||
help_text="严进严出模式(出库)指定的入库明细ID列表"
|
||
)
|
||
|
||
def validate_product(self, value: int) -> int:
|
||
"""验证产品是否存在"""
|
||
try:
|
||
basic_info_models.Product.objects.get(id=value)
|
||
except basic_info_models.Product.DoesNotExist:
|
||
raise serializers.ValidationError(f"产品ID {value} 不存在")
|
||
return value
|
||
|
||
def validate_consume_with(self, value: List[int]) -> List[int]:
|
||
if len(value) != len(set(value)):
|
||
raise serializers.ValidationError("consume_with 中存在重复的入库明细ID")
|
||
return value
|
||
|
||
|
||
class CreateStockChangeSerializer(serializers.Serializer):
|
||
"""创建库存变动记录序列化器"""
|
||
stock_change_record_id = serializers.IntegerField(
|
||
required=False,
|
||
allow_null=True,
|
||
help_text="库存变动记录ID,如果不提供则创建新记录"
|
||
)
|
||
products = ProductStockChangeSerializer(many=True, help_text="产品列表")
|
||
|
||
def validate_stock_change_record_id(self, value: int) -> int:
|
||
"""验证库存变动记录是否存在"""
|
||
if value is not None:
|
||
try:
|
||
stock_models.StockChangeRecord.objects.get(id=value)
|
||
except stock_models.StockChangeRecord.DoesNotExist:
|
||
raise serializers.ValidationError(f"库存变动记录ID {value} 不存在")
|
||
return value
|
||
|
||
def validate_products(self, value: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||
"""验证产品列表"""
|
||
if not value:
|
||
raise serializers.ValidationError("产品列表不能为空")
|
||
|
||
# 检查是否有重复的产品ID
|
||
product_ids = [item['product'] for item in value]
|
||
if len(product_ids) != len(set(product_ids)):
|
||
raise serializers.ValidationError("产品列表中存在重复的产品ID")
|
||
|
||
return value
|
||
|
||
|
||
class RelaxedQuantitySerializer(serializers.Serializer):
|
||
"""宽松模式数量结构"""
|
||
value = serializers.DecimalField(
|
||
max_digits=10,
|
||
decimal_places=2,
|
||
min_value=Decimal('0.01'),
|
||
help_text="产品总数量"
|
||
)
|
||
unit_count = serializers.DecimalField(
|
||
max_digits=10,
|
||
decimal_places=2,
|
||
min_value=Decimal('0.01'),
|
||
default=Decimal('1.00'),
|
||
help_text="单条(匹)数量,默认 1"
|
||
)
|
||
|
||
|
||
class RelaxedProductStockChangeSerializer(serializers.Serializer):
|
||
"""宽松模式产品序列化器"""
|
||
product = serializers.IntegerField(help_text="产品ID")
|
||
quantity = RelaxedQuantitySerializer(help_text="数量定义(总数与单条数)")
|
||
|
||
def validate_product(self, value: int) -> int:
|
||
try:
|
||
basic_info_models.Product.objects.get(id=value)
|
||
except basic_info_models.Product.DoesNotExist:
|
||
raise serializers.ValidationError(f"产品ID {value} 不存在")
|
||
return value
|
||
|
||
|
||
class CreateStockChangeRelaxedSerializer(serializers.Serializer):
|
||
"""宽松模式创建库存变动记录"""
|
||
products = RelaxedProductStockChangeSerializer(many=True, help_text="产品列表")
|
||
|
||
def validate_products(self, value: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||
if not value:
|
||
raise serializers.ValidationError("产品列表不能为空")
|
||
|
||
product_ids = [item['product'] for item in value]
|
||
if len(product_ids) != len(set(product_ids)):
|
||
raise serializers.ValidationError("产品列表中存在重复的产品ID")
|
||
|
||
return value
|
||
|
||
|
||
class StockChangeRecordResponseSerializer(serializers.ModelSerializer):
|
||
"""库存变动记录响应序列化器"""
|
||
|
||
class Meta:
|
||
model = stock_models.StockChangeRecord
|
||
fields = [
|
||
'id', 'type', 'warehouse', 'source_type', 'source_id',
|
||
'is_finished', 'finished_at', 'created_at', 'updated_at'
|
||
]
|
||
|
||
|
||
class StockChangeDetailResponseSerializer(serializers.ModelSerializer):
|
||
"""库存变动明细响应序列化器"""
|
||
product_name = serializers.CharField(source='product.name', read_only=True)
|
||
unit_display = serializers.CharField(source='get_unit_display', read_only=True)
|
||
consume_with_id = serializers.IntegerField(read_only=True)
|
||
|
||
class Meta:
|
||
model = stock_models.StockChangeDetail
|
||
fields = [
|
||
'id', 'product', 'product_name', 'quantity',
|
||
'unit', 'unit_display', 'stock_change_record',
|
||
'is_consumed', 'consume_with_id',
|
||
]
|
||
|
||
|
||
class CreateStockChangeResponseSerializer(serializers.Serializer):
|
||
"""创建库存变动记录响应序列化器"""
|
||
stock_change_record = StockChangeRecordResponseSerializer()
|
||
details = StockChangeDetailResponseSerializer(many=True)
|
||
message = serializers.CharField()
|
||
created_details_count = serializers.IntegerField()
|
||
|
||
|
||
class SetMerchantAutoCompleteStockChangeSerializer(serializers.Serializer):
|
||
"""设置商户自动确认库存变动序列化器"""
|
||
auto_complete = serializers.BooleanField(
|
||
help_text="是否自动确认库存变动"
|
||
)
|
||
|
||
|
||
# ==================== 通用文件上传序列化器 ====================
|
||
|
||
class UploadedFileSerializer(serializers.ModelSerializer):
|
||
"""上传文件序列化器"""
|
||
file_url = serializers.SerializerMethodField()
|
||
owner_username = serializers.CharField(source='owner.username', read_only=True)
|
||
|
||
class Meta:
|
||
model = api_models.UploadedFile
|
||
fields = [
|
||
'id', 'path', 'file_url', 'original_filename', 'file_size',
|
||
'content_type', 'owner', 'owner_username', 'is_deleted',
|
||
'created_at', 'updated_at'
|
||
]
|
||
read_only_fields = ['id', 'owner', 'file_size', 'content_type', 'created_at', 'updated_at']
|
||
|
||
def get_file_url(self, obj):
|
||
"""获取文件完整URL"""
|
||
if obj.path:
|
||
request = self.context.get('request')
|
||
if request:
|
||
return request.build_absolute_uri(obj.path.url)
|
||
return obj.path.url
|
||
return None
|
||
|
||
|
||
class FileUploadSerializer(serializers.Serializer):
|
||
"""文件上传请求序列化器"""
|
||
file = serializers.FileField(
|
||
required=True,
|
||
help_text='要上传的文件'
|
||
)
|
||
|
||
def validate_file(self, value):
|
||
"""验证文件"""
|
||
# 限制文件大小为 100MB
|
||
max_size = 100 * 1024 * 1024
|
||
if value.size > max_size:
|
||
raise serializers.ValidationError(f'文件大小不能超过 {max_size // (1024*1024)}MB')
|
||
|
||
return value
|
||
|
||
|
||
class PrintCountDeltaSerializer(serializers.Serializer):
|
||
"""打印次数增量请求"""
|
||
|
||
object_type = serializers.ChoiceField(choices=PrintCountObjectType.choices)
|
||
object_id = serializers.IntegerField(min_value=1)
|
||
delta = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||
|
||
def validate(self, attrs: Dict[str, Any]) -> Dict[str, Any]:
|
||
raw_delta = attrs.get('delta')
|
||
try:
|
||
delta_value = int(raw_delta)
|
||
except (TypeError, ValueError):
|
||
delta_value = 1
|
||
|
||
if delta_value < 1:
|
||
delta_value = 1
|
||
|
||
attrs['delta'] = delta_value
|
||
return attrs |