"""完整财务 serializers: - 往来:应收单、应付单、收款单、付款单、核销明细 - 总账:科目、期间、凭证(含分录)、凭证创建 """ from rest_framework import serializers from apps.core.serializers import AsyncModelSerializer from .models import ( Account, Period, Voucher, VoucherEntry, Receivable, Payable, Receipt, Payment, Allocation, ) # ============================================================ # 1. 往来序列化(AR / AP) # ============================================================ class ReceivableSerializer(AsyncModelSerializer): customer_code = serializers.CharField(source="customer.code", read_only=True) customer_name = serializers.CharField(source="customer.name", read_only=True) balance = serializers.DecimalField(max_digits=18, decimal_places=4, read_only=True) class Meta: model = Receivable fields = [ "id", "bill_no", "customer", "customer_code", "customer_name", "source_type", "source_ref", "bill_date", "due_date", "total_amount", "paid_amount", "balance", "status", "remark", "created_at", "updated_at", ] read_only_fields = [ "id", "bill_no", "paid_amount", "balance", "status", "created_at", "updated_at", ] class PayableSerializer(AsyncModelSerializer): supplier_code = serializers.CharField(source="supplier.code", read_only=True) supplier_name = serializers.CharField(source="supplier.name", read_only=True) balance = serializers.DecimalField(max_digits=18, decimal_places=4, read_only=True) class Meta: model = Payable fields = [ "id", "bill_no", "supplier", "supplier_code", "supplier_name", "source_type", "source_ref", "bill_date", "due_date", "total_amount", "paid_amount", "balance", "status", "remark", "created_at", "updated_at", ] read_only_fields = [ "id", "bill_no", "paid_amount", "balance", "status", "created_at", "updated_at", ] class ReceiptSerializer(AsyncModelSerializer): customer_code = serializers.CharField(source="customer.code", read_only=True) customer_name = serializers.CharField(source="customer.name", read_only=True) class Meta: model = Receipt fields = [ "id", "bill_no", "customer", "customer_code", "customer_name", "bill_date", "amount", "method", "status", "remark", "created_at", "updated_at", ] read_only_fields = ["id", "status", "created_at", "updated_at"] class PaymentSerializer(AsyncModelSerializer): supplier_code = serializers.CharField(source="supplier.code", read_only=True) supplier_name = serializers.CharField(source="supplier.name", read_only=True) class Meta: model = Payment fields = [ "id", "bill_no", "supplier", "supplier_code", "supplier_name", "bill_date", "amount", "method", "status", "remark", "created_at", "updated_at", ] read_only_fields = ["id", "status", "created_at", "updated_at"] class AllocationSerializer(AsyncModelSerializer): receivable_no = serializers.CharField(source="receivable.bill_no", read_only=True, default=None) payable_no = serializers.CharField(source="payable.bill_no", read_only=True, default=None) receipt_no = serializers.CharField(source="receipt.bill_no", read_only=True, default=None) payment_no = serializers.CharField(source="payment.bill_no", read_only=True, default=None) class Meta: model = Allocation fields = [ "id", "kind", "amount", "receivable", "receivable_no", "payable", "payable_no", "receipt", "receipt_no", "payment", "payment_no", "created_at", ] read_only_fields = ["id", "created_at"] class _AllocationItemInputSerializer(serializers.Serializer): """公共核销行输入。 ``*_id`` 是显式 API 形式;保留无后缀字段以便客户端沿用模型字段命名。 """ amount = serializers.DecimalField(max_digits=18, decimal_places=4) def _document_id(self, attrs, field_name): plain_name = field_name id_name = f"{field_name}_id" plain = attrs.pop(plain_name, None) explicit = attrs.pop(id_name, None) if plain is not None and explicit is not None and plain != explicit: raise serializers.ValidationError( {field_name: f"{plain_name} 与 {id_name} 不能同时指定不同值"} ) document_id = explicit if explicit is not None else plain if document_id is None: raise serializers.ValidationError({field_name: "必填"}) attrs[field_name] = document_id return attrs class ReceiptAllocationItemSerializer(_AllocationItemInputSerializer): receivable = serializers.IntegerField(required=False, min_value=1) receivable_id = serializers.IntegerField(required=False, min_value=1, write_only=True) def validate(self, attrs): return self._document_id(attrs, "receivable") class PaymentAllocationItemSerializer(_AllocationItemInputSerializer): payable = serializers.IntegerField(required=False, min_value=1) payable_id = serializers.IntegerField(required=False, min_value=1, write_only=True) def validate(self, attrs): return self._document_id(attrs, "payable") class ReceiptAllocationInputSerializer(serializers.Serializer): allocations = ReceiptAllocationItemSerializer(many=True, allow_empty=False) class PaymentAllocationInputSerializer(serializers.Serializer): allocations = PaymentAllocationItemSerializer(many=True, allow_empty=False) # ============================================================ # 2. 总账序列化(GL) # ============================================================ class AccountSerializer(AsyncModelSerializer): category_name = serializers.CharField(source="get_category_display", read_only=True) balance_type_name = serializers.CharField(source="get_balance_type_display", read_only=True) class Meta: model = Account fields = [ "id", "code", "name", "category", "category_name", "balance_type", "balance_type_name", "parent", "is_active", "is_system", "aux_customer", "aux_supplier", "remark", ] read_only_fields = ["id", "is_system"] class PeriodSerializer(AsyncModelSerializer): status_name = serializers.CharField(source="get_status_display", read_only=True) class Meta: model = Period fields = [ "id", "code", "start_date", "end_date", "status", "status_name", "closed_at", "closed_by", ] read_only_fields = ["id", "status", "status_name", "closed_at", "closed_by"] class VoucherEntrySerializer(AsyncModelSerializer): account_code = serializers.CharField(source="account.code", read_only=True) account_name = serializers.CharField(source="account.name", read_only=True) customer_code = serializers.CharField(source="customer.code", read_only=True, default=None) customer_name = serializers.CharField(source="customer.name", read_only=True, default=None) supplier_code = serializers.CharField(source="supplier.code", read_only=True, default=None) supplier_name = serializers.CharField(source="supplier.name", read_only=True, default=None) class Meta: model = VoucherEntry fields = [ "id", "account", "account_code", "account_name", "customer", "customer_code", "customer_name", "supplier", "supplier_code", "supplier_name", "summary", "debit", "credit", ] read_only_fields = ["id"] class VoucherSerializer(AsyncModelSerializer): period_code = serializers.CharField(source="period.code", read_only=True) source_type_name = serializers.CharField(source="get_source_type_display", read_only=True) status_name = serializers.CharField(source="get_status_display", read_only=True) entries = VoucherEntrySerializer(many=True, read_only=True) class Meta: model = Voucher fields = [ "id", "bill_no", "period", "period_code", "source_type", "source_type_name", "source_ref", "voucher_date", "total_debit", "total_credit", "status", "status_name", "summary", "posted_at", "posted_by", "entries", "created_at", "updated_at", ] read_only_fields = [ "id", "bill_no", "period", "total_debit", "total_credit", "status", "posted_at", "posted_by", "created_at", "updated_at", ] class VoucherEntryInputSerializer(serializers.Serializer): account_code = serializers.CharField(max_length=32) debit = serializers.DecimalField(max_digits=18, decimal_places=4, default=0) credit = serializers.DecimalField(max_digits=18, decimal_places=4, default=0) summary = serializers.CharField(max_length=255, required=False, allow_blank=True, default="") customer_id = serializers.IntegerField(required=False, allow_null=True, default=None) supplier_id = serializers.IntegerField(required=False, allow_null=True, default=None) class VoucherCreateInputSerializer(serializers.Serializer): voucher_date = serializers.DateField() summary = serializers.CharField(max_length=255, required=False, allow_blank=True, default="") auto_post = serializers.BooleanField(default=False) entries = VoucherEntryInputSerializer(many=True)