236 lines
9.4 KiB
Python
236 lines
9.4 KiB
Python
"""完整财务 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)
|