"""sales 序列化器(async 兼容)。""" from rest_framework import serializers from apps.core.serializers import AsyncModelSerializer from .models import SalesOrder, SalesOrderLine, SalesBill, SalesBillLine class SalesOrderLineSerializer(AsyncModelSerializer): product_code = serializers.CharField(source="product.code", read_only=True) product_name = serializers.CharField(source="product.name", read_only=True) class Meta: model = SalesOrderLine fields = ["id", "product", "product_code", "product_name", "quantity", "unit_price", "amount", "delivered_qty"] read_only_fields = ["id", "amount", "delivered_qty"] class SalesOrderSerializer(AsyncModelSerializer): customer_code = serializers.CharField(source="customer.code", read_only=True) warehouse_code = serializers.CharField(source="warehouse.code", read_only=True) lines = SalesOrderLineSerializer(many=True, required=False) class Meta: model = SalesOrder fields = [ "id", "bill_no", "customer", "customer_code", "warehouse", "warehouse_code", "bill_date", "expected_date", "total_amount", "state", "remark", "lines", ] read_only_fields = ["id", "bill_no", "total_amount", "state"] class SalesBillLineSerializer(AsyncModelSerializer): product_code = serializers.CharField(source="product.code", read_only=True) product_name = serializers.CharField(source="product.name", read_only=True) unit_name = serializers.SerializerMethodField() class Meta: model = SalesBillLine fields = ["id", "product", "product_code", "product_name", "quantity", "source_unit", "source_quantity", "unit_name", "unit_price", "amount", "tax_rate", "tax_amount"] read_only_fields = ["id", "amount", "tax_amount"] def get_unit_name(self, obj): unit = obj.source_unit or getattr(obj.product, "base_unit", None) return unit.name if unit else "" class SalesBillSerializer(AsyncModelSerializer): customer_code = serializers.CharField(source="customer.code", read_only=True) customer_name = serializers.CharField(source="customer.name", read_only=True) warehouse_code = serializers.CharField(source="warehouse.code", read_only=True) round_off = serializers.DecimalField(max_digits=18, decimal_places=4, read_only=True) # 单据税额合计 = 各行 tax_amount 之和(D4;前端展示"其中税额") tax_total = serializers.SerializerMethodField() lines = SalesBillLineSerializer(many=True, required=False) def get_tax_total(self, obj): """在内存里汇总已预取的行,避免每张单一次 aggregate(N+1)。 旧实现 `obj.lines.aggregate(Sum("tax_amount"))` 对列表页的每一行都发一条 SQL (实测 50 张单 = 50 次聚合)。列表接口已 prefetch `lines`, 这里直接遍历内存对象即可。详情接口若未预取,all() 也只多一次查询。 """ return str(sum((ln.tax_amount or 0) for ln in obj.lines.all())) class Meta: model = SalesBill fields = [ "id", "bill_no", "customer", "customer_code", "customer_name", "warehouse", "warehouse_code", "order", "bill_date", "total_amount", "round_off", "tax_total", "state", "remark", "lines", ] read_only_fields = ["id", "bill_no", "total_amount", "state"]