feat: 财务

This commit is contained in:
Czw 2021-11-17 00:36:24 +08:00
parent b9f886c19f
commit b49aef0bf8
8 changed files with 474 additions and 13 deletions

View file

@ -1,7 +1,49 @@
from django_filters.rest_framework import FilterSet
from django_filters.filters import *
from apps.finance.models import *
class PaymentOrderFilter(FilterSet):
start_date = DateFilter(field_name='create_time', lookup_expr='gte', label='开始日期')
end_date = DateFilter(field_name='create_time', lookup_expr='lt', label='结束日期')
class Meta:
model = PaymentOrder
fields = ['number', 'supplier', 'handler', 'is_void', 'creator',
'start_date', 'end_date']
class CollectionOrderFilter(FilterSet):
start_date = DateFilter(field_name='create_time', lookup_expr='gte', label='开始日期')
end_date = DateFilter(field_name='create_time', lookup_expr='lt', label='结束日期')
class Meta:
model = CollectionOrder
fields = ['number', 'client', 'handler', 'is_void', 'creator',
'start_date', 'end_date']
class ChargeOrderFilter(FilterSet):
start_date = DateFilter(field_name='create_time', lookup_expr='gte', label='开始日期')
end_date = DateFilter(field_name='create_time', lookup_expr='lt', label='结束日期')
class Meta:
model = ChargeOrder
fields = ['number', 'type', 'supplier', 'client', 'handler', 'charge_item',
'account', 'is_void', 'creator', 'start_date', 'end_date']
class AccountTransferRecordFilter(FilterSet):
start_date = DateFilter(field_name='create_time', lookup_expr='gte', label='开始日期')
end_date = DateFilter(field_name='create_time', lookup_expr='lt', label='结束日期')
class Meta:
model = AccountTransferRecord
fields = ['out_account', 'in_account', 'handler', 'is_void',
'start_date', 'end_date']
__all__ = [
'PaymentOrderFilter', 'CollectionOrderFilter',
'ChargeOrderFilter', 'AccountTransferRecordFilter',
]

View file

@ -41,7 +41,6 @@ class PaymentAccount(Model):
related_name='payment_accounts', verbose_name='付款单据')
account = ForeignKey('data.Account', on_delete=PROTECT, related_name='payment_accounts', verbose_name='结算账户')
payment_amount = AmountField(verbose_name='付款金额')
is_void = BooleanField(default=False, verbose_name='作废状态')
team = ForeignKey('system.Team', on_delete=CASCADE, related_name='payment_accounts')
class Meta:
@ -88,7 +87,6 @@ class CollectionAccount(Model):
related_name='collection_accounts', verbose_name='收款单据')
account = ForeignKey('data.Account', on_delete=PROTECT, related_name='collection_accounts', verbose_name='结算账户')
collection_amount = AmountField(verbose_name='收款金额')
is_void = BooleanField(default=False, verbose_name='作废状态')
team = ForeignKey('system.Team', on_delete=CASCADE, related_name='collection_accounts')
class Meta:
@ -116,7 +114,8 @@ class ChargeOrder(Model):
related_name='charge_orders', verbose_name='收支项目')
charge_item_name = CharField(max_length=64, verbose_name='收支项目名称')
account = ForeignKey('data.Account', on_delete=PROTECT, related_name='charge_orders', verbose_name='结算账户')
charge_amount = AmountField(verbose_name='收支金额')
total_amount = AmountField(verbose_name='应收/付金额')
charge_amount = AmountField(verbose_name='实收/付金额')
remark = CharField(max_length=256, null=True, blank=True, verbose_name='备注')
is_void = BooleanField(default=False, verbose_name='作废状态')
creator = ForeignKey('system.User', on_delete=PROTECT,
@ -157,9 +156,9 @@ class AccountTransferRecord(Model):
related_name='in_account_transfer_records', verbose_name='转入账户')
transfer_in_time = DateTimeField(verbose_name='转入时间')
transfer_amount = AmountField(verbose_name='转账金额')
service_charge_amount = AmountField(verbose_name='手续费金额')
service_charge_amount = AmountField(default=0, verbose_name='手续费金额')
service_charge_payer = CharField(max_length=32, choices=ServiceChargePayer.choices,
null=True, verbose_name='手续费支付方')
default=ServiceChargePayer.TRANSFER_OUT, verbose_name='手续费支付方')
handler = ForeignKey('system.User', on_delete=PROTECT, related_name='account_transfer_records', verbose_name='经手人')
handle_time = DateTimeField(verbose_name='处理时间')
remark = CharField(max_length=256, null=True, blank=True, verbose_name='备注')

View file

@ -1,6 +1,23 @@
from extensions.permissions import InterfacePermission
__all__ = [
class PaymentOrderPermission(InterfacePermission):
code = 'payment_order'
class CollectionOrderPermission(InterfacePermission):
code = 'collection_order'
class ChargeOrderPermission(InterfacePermission):
code = 'charge_order'
class AccountTransferRecordPermission(InterfacePermission):
code = 'account_transfer_record'
__all__ = [
'PaymentOrderPermission', 'CollectionOrderPermission',
'ChargeOrderPermission', 'AccountTransferRecordPermission',
]

View file

@ -1,6 +1,10 @@
from extensions.serializers import *
__all__ = [
class NumberResponse(Serializer):
number = CharField(label='编号')
__all__ = [
'NumberResponse',
]

View file

@ -186,7 +186,7 @@ class ChargeOrderSerializer(BaseSerializer):
'account_number', 'account_name', 'is_void', 'creator', 'creator_name',
'create_time']
fields = ['number', 'type', 'client', 'supplier', 'handler', 'handle_time', 'charge_item',
'account', 'charge_amount', 'remark', *read_only_fields]
'account', 'total_amount', 'charge_amount', 'remark', *read_only_fields]
def validate_number(self, value):
self.validate_unique({'number': value}, message=f'编号[{value}]已存在')
@ -225,9 +225,12 @@ class ChargeOrderSerializer(BaseSerializer):
client = attrs.get('client')
if (supplier and client) or not (supplier or client):
raise ValidationError('供应商或客户选择重复')
if attrs['type'] != attrs['charge_item'].type:
raise ValidationError('收支类型与收支项目不匹配')
if attrs['charge_amount'] > attrs['total_amount']:
raise ValidationError('实收/付金额大于应收/付金额')
return super().validate(attrs)
def create(self, validated_data):
@ -252,7 +255,8 @@ class AccountTransferRecordSerializer(BaseSerializer):
class Meta:
model = AccountTransferRecord
read_only_fields = ['id', 'out_account_number', 'out_account_name', 'in_account_number',
'in_account_name', 'is_void', 'creator', 'creator_name', 'create_time']
'in_account_name', 'service_charge_payer_display', 'handler_name',
'is_void', 'creator', 'creator_name', 'create_time']
fields = ['out_account', 'transfer_out_time', 'in_account', 'transfer_in_time',
'transfer_amount', 'service_charge_amount', 'service_charge_payer', 'handler',
'handle_time', 'remark', *read_only_fields]

View file

@ -1,5 +1,10 @@
from extensions.routers import *
from apps.finance.views import *
router = BaseRouter()
router.register('payment_orders', PaymentOrderViewSet, 'payment_order')
router.register('collection_orders', CollectionOrderViewSet, 'collection_order')
router.register('charge_orders', ChargeOrderViewSet, 'charge_order')
router.register('account_transfer_records', AccountTransferRecordViewSet, 'account_transfer_record')
urlpatterns = router.urls

View file

@ -1,8 +1,397 @@
from extensions.permissions import *
from extensions.exceptions import *
from extensions.viewsets import *
from apps.finance.serializers import *
from apps.finance.permissions import *
from apps.finance.filters import *
from apps.finance.schemas import *
from apps.finance.models import *
from apps.flow.models import *
class PaymentOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, CreateModelMixin):
"""付款单据"""
serializer_class = PaymentOrderSerializer
permission_classes = [IsAuthenticated, PaymentOrderPermission]
filterset_class = PaymentOrderFilter
search_fields = ['number', 'supplier__number', 'supplier__name', 'remark']
ordering_fields = ['id', 'number', 'total_amount', 'create_time']
select_related_fields = ['supplier', 'handler', 'creator']
prefetch_related_fields = ['payment_accounts', 'payment_accounts__account']
queryset = PaymentOrder.objects.all()
@transaction.atomic
def perform_create(self, serializer):
payment_order = serializer.save()
# 同步欠款
supplier = payment_order.supplier
supplier.arrears_amount = NP.minus(supplier.arrears_amount, payment_order.total_amount)
supplier.save(update_fields=['arrears_amount'])
# 同步余额, 流水
finance_flows = []
for payment_account in payment_order.payment_accounts.all():
account = payment_account.account
amount_before = account.balance_amount
amount_change = payment_account.payment_amount
amount_after = NP.minus(amount_before, amount_change)
finance_flows.append(FinanceFlow(
account=account, type=FinanceFlow.Type.PAYMENT, amount_before=amount_before,
amount_change=amount_change, amount_after=amount_after, payment_order=payment_order,
creator=self.user, team=self.team
))
account.balance_amount = amount_after
account.save(update_fields=['balance_amount'])
else:
FinanceFlow.objects.bulk_create(finance_flows)
@extend_schema(responses={200: NumberResponse})
@action(detail=False, methods=['get'])
def number(self, request, *args, **kwargs):
"""获取编号"""
number = PaymentOrder.get_number(self.team)
return Response(data={'number': number}, status=status.HTTP_200_OK)
@transaction.atomic
@extend_schema(request=None, responses={200: PaymentOrderSerializer})
@action(detail=True, methods=['post'])
def void(self, request, *args, **kwargs):
"""作废"""
payment_order = self.get_object()
payment_order.is_void = True
payment_order.save(update_fields=['is_void'])
# 同步欠款
supplier = payment_order.supplier
supplier.arrears_amount = NP.plus(supplier.arrears_amount, payment_order.total_amount)
supplier.save(update_fields=['arrears_amount'])
# 同步余额, 流水
finance_flows = []
for payment_account in payment_order.payment_accounts.all():
account = payment_account.account
amount_before = account.balance_amount
amount_change = payment_account.payment_amount
amount_after = NP.plus(amount_before, amount_change)
finance_flows.append(FinanceFlow(
account=account, type=FinanceFlow.Type.VOID_PAYMENT, amount_before=amount_before,
amount_change=amount_change, amount_after=amount_after, void_payment_order=payment_order,
creator=self.user, team=self.team
))
account.balance_amount = amount_after
account.save(update_fields=['balance_amount'])
else:
FinanceFlow.objects.bulk_create(finance_flows)
serializer = PaymentOrderSerializer(instance=payment_order)
return Response(data=serializer.data, status=status.HTTP_200_OK)
class CollectionOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, CreateModelMixin):
"""付款单据"""
serializer_class = CollectionOrderSerializer
permission_classes = [IsAuthenticated, CollectionOrderPermission]
filterset_class = CollectionOrderFilter
search_fields = ['number', 'client__number', 'client__name', 'remark']
ordering_fields = ['id', 'number', 'total_amount', 'create_time']
select_related_fields = ['client', 'handler', 'creator']
prefetch_related_fields = ['collection_accounts', 'collection_accounts__account']
queryset = CollectionOrder.objects.all()
@transaction.atomic
def perform_create(self, serializer):
collection_order = serializer.save()
# 同步欠款
client = collection_order.client
client.arrears_amount = NP.minus(client.arrears_amount, collection_order.total_amount)
client.save(update_fields=['arrears_amount'])
# 同步余额, 流水
finance_flows = []
for collection_account in collection_order.collection_accounts.all():
account = collection_account.account
amount_before = account.balance_amount
amount_change = collection_account.collection_amount
amount_after = NP.plus(amount_before, amount_change)
finance_flows.append(FinanceFlow(
account=account, type=FinanceFlow.Type.COLLECTION, amount_before=amount_before,
amount_change=amount_change, amount_after=amount_after, collection_order=collection_order,
creator=self.user, team=self.team
))
account.balance_amount = amount_after
account.save(update_fields=['balance_amount'])
else:
FinanceFlow.objects.bulk_create(finance_flows)
@extend_schema(responses={200: NumberResponse})
@action(detail=False, methods=['get'])
def number(self, request, *args, **kwargs):
"""获取编号"""
number = PaymentOrder.get_number(self.team)
return Response(data={'number': number}, status=status.HTTP_200_OK)
@transaction.atomic
@extend_schema(request=None, responses={200: CollectionOrderSerializer})
@action(detail=True, methods=['post'])
def void(self, request, *args, **kwargs):
"""作废"""
collection_order = self.get_object()
collection_order.is_void = True
collection_order.save(update_fields=['is_void'])
# 同步欠款
client = collection_order.client
client.arrears_amount = NP.plus(client.arrears_amount, collection_order.total_amount)
client.save(update_fields=['arrears_amount'])
# 同步余额, 流水
finance_flows = []
for collection_account in collection_order.collection_accounts.all():
account = collection_account.account
amount_before = account.balance_amount
amount_change = collection_account.collection_amount
amount_after = NP.minus(amount_before, amount_change)
finance_flows.append(FinanceFlow(
account=account, type=FinanceFlow.Type.VOID_COLLECTION, amount_before=amount_before,
amount_change=amount_change, amount_after=amount_after, void_collection_order=collection_order,
creator=self.user, team=self.team
))
account.balance_amount = amount_after
account.save(update_fields=['balance_amount'])
else:
FinanceFlow.objects.bulk_create(finance_flows)
serializer = CollectionOrderSerializer(instance=collection_order)
return Response(data=serializer.data, status=status.HTTP_200_OK)
class ChargeOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, CreateModelMixin):
"""收支单据"""
serializer_class = ChargeOrderSerializer
permission_classes = [IsAuthenticated, ChargeOrderPermission]
filterset_class = ChargeOrderFilter
search_fields = ['number', 'supplier__number', 'supplier__name', 'client__number',
'client__name', 'remark']
ordering_fields = ['id', 'number', 'total_amount', 'charge_amount', 'create_time']
select_related_fields = ['supplier', 'client', 'handler', 'creator']
queryset = ChargeOrder.objects.all()
@transaction.atomic
def perform_create(self, serializer):
charge_order = serializer.save()
account = charge_order.account
amount_before = account.balance_amount
arrears_amount = NP.minus(charge_order.total_amount, charge_order.charge_amount)
if charge_order.type == ChargeOrder.Type.INCOME:
amount_change = charge_order.charge_amount
# 同步欠款
if supplier := charge_order.supplier:
supplier.arrears_amount = NP.minus(supplier.arrears_amount, arrears_amount)
supplier.save(update_fields=['arrears_amount'])
elif client := charge_order.client:
client.arrears_amount = NP.plus(client.arrears_amount, arrears_amount)
client.save(update_fields=['arrears_amount'])
elif charge_order.type == ChargeOrder.Type.EXPENDITURE:
amount_change = -charge_order.charge_amount
# 同步欠款
if supplier := charge_order.supplier:
supplier.arrears_amount = NP.plus(supplier.arrears_amount, arrears_amount)
supplier.save(update_fields=['arrears_amount'])
elif client := charge_order.client:
client.arrears_amount = NP.minus(client.arrears_amount, arrears_amount)
client.save(update_fields=['arrears_amount'])
amount_after = NP.plus(amount_before, amount_change)
FinanceFlow.objects.create(
account=account, type=FinanceFlow.Type.CHARGE, amount_before=amount_before,
amount_change=amount_change, amount_after=amount_after, charge_order=charge_order,
creator=self.user, team=self.team
)
account.balance_amount = amount_after
account.save(update_fields=['balance_amount'])
@extend_schema(responses={200: NumberResponse})
@action(detail=False, methods=['get'])
def number(self, request, *args, **kwargs):
"""获取编号"""
number = ChargeOrder.get_number(self.team)
return Response(data={'number': number}, status=status.HTTP_200_OK)
@transaction.atomic
@extend_schema(request=None, responses={200: ChargeOrderPermission})
@action(detail=True, methods=['post'])
def void(self, request, *args, **kwargs):
"""作废"""
charge_order = self.get_object()
account = charge_order.account
amount_before = account.balance_amount
arrears_amount = NP.minus(charge_order.total_amount, charge_order.charge_amount)
if charge_order.type == ChargeOrder.Type.INCOME:
amount_change = charge_order.charge_amount
# 同步欠款
if supplier := charge_order.supplier:
supplier.arrears_amount = NP.plus(supplier.arrears_amount, arrears_amount)
supplier.save(update_fields=['arrears_amount'])
elif client := charge_order.client:
client.arrears_amount = NP.minus(client.arrears_amount, arrears_amount)
client.save(update_fields=['arrears_amount'])
elif charge_order.type == ChargeOrder.Type.EXPENDITURE:
amount_change = -charge_order.charge_amount
# 同步欠款
if supplier := charge_order.supplier:
supplier.arrears_amount = NP.minus(supplier.arrears_amount, arrears_amount)
supplier.save(update_fields=['arrears_amount'])
elif client := charge_order.client:
client.arrears_amount = NP.plus(client.arrears_amount, arrears_amount)
client.save(update_fields=['arrears_amount'])
amount_after = NP.minus(amount_before, amount_change)
FinanceFlow.objects.create(
account=account, type=FinanceFlow.Type.VOID_CHARGE, amount_before=amount_before,
amount_change=amount_change, amount_after=amount_after, void_charge_order=charge_order,
creator=self.user, team=self.team
)
account.balance_amount = amount_after
account.save(update_fields=['balance_amount'])
serializer = ChargeOrderPermission(instance=charge_order)
return Response(data=serializer.data, status=status.HTTP_200_OK)
class AccountTransferRecordViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, CreateModelMixin):
"""结算账户转账记录"""
serializer_class = AccountTransferRecordSerializer
permission_classes = [IsAuthenticated, AccountTransferRecordPermission]
filterset_class = AccountTransferRecordFilter
search_fields = ['out_account__number', 'out_account__name', 'in_account__number',
'in_account__name', 'remark']
ordering_fields = ['id', 'transfer_out_time', 'transfer_in_time', 'create_time']
select_related_fields = ['out_account', 'in_account', 'handler', 'creator']
queryset = AccountTransferRecord.objects.all()
@transaction.atomic
def perform_create(self, serializer):
account_transfer_record = serializer.save()
out_account = account_transfer_record.out_account
transfer_out_amount = account_transfer_record.transfer_amount
in_account = account_transfer_record.in_account
transfer_in_amount = account_transfer_record.transfer_amount
service_charge_payer = account_transfer_record.service_charge_payer
if service_charge_payer == AccountTransferRecord.ServiceChargePayer.TRANSFER_IN:
transfer_in_amount = NP.minus(transfer_in_amount,
account_transfer_record.service_charge_amount)
elif service_charge_payer == AccountTransferRecord.ServiceChargePayer.TRANSFER_OUT:
transfer_out_amount = NP.minus(transfer_out_amount,
account_transfer_record.service_charge_amount)
# 同步账户余额
finance_flows = []
amount_before = out_account.balance_amount
amount_change = transfer_out_amount
amount_after = NP.minus(amount_before, transfer_out_amount)
finance_flows.append(FinanceFlow(
account=out_account, type=FinanceFlow.Type.ACCOUNT_TRANSFER_OUT,
amount_before=amount_before, amount_change=amount_change, amount_after=amount_after,
account_transfer_record=account_transfer_record, creator=self.user, team=self.team
))
out_account.balance_amount = amount_after
out_account.save(update_fields=['balance_amount'])
amount_before = in_account.balance_amount
amount_change = transfer_in_amount
amount_after = NP.plus(amount_before, transfer_in_amount)
finance_flows.append(FinanceFlow(
account=in_account, type=FinanceFlow.Type.ACCOUNT_TRANSFER_IN,
amount_before=amount_before, amount_change=amount_change, amount_after=amount_after,
account_transfer_record=account_transfer_record, creator=self.user, team=self.team
))
in_account.balance_amount = amount_after
in_account.save(update_fields=['balance_amount'])
@transaction.atomic
@extend_schema(request=None, responses={200: AccountTransferRecordSerializer})
@action(detail=True, methods=['post'])
def void(self, request, *args, **kwargs):
"""作废"""
account_transfer_record = self.get_object()
out_account = account_transfer_record.out_account
transfer_out_amount = account_transfer_record.transfer_amount
in_account = account_transfer_record.in_account
transfer_in_amount = account_transfer_record.transfer_amount
service_charge_payer = account_transfer_record.service_charge_payer
if service_charge_payer == AccountTransferRecord.ServiceChargePayer.TRANSFER_IN:
transfer_in_amount = NP.minus(transfer_in_amount,
account_transfer_record.service_charge_amount)
elif service_charge_payer == AccountTransferRecord.ServiceChargePayer.TRANSFER_OUT:
transfer_out_amount = NP.minus(transfer_out_amount,
account_transfer_record.service_charge_amount)
# 同步账户余额
finance_flows = []
amount_before = out_account.balance_amount
amount_change = transfer_out_amount
amount_after = NP.plus(amount_before, transfer_out_amount)
finance_flows.append(FinanceFlow(
account=out_account, type=FinanceFlow.Type.VOID_ACCOUNT_TRANSFER_OUT,
amount_before=amount_before, amount_change=amount_change, amount_after=amount_after,
void_account_transfer_record=account_transfer_record, creator=self.user, team=self.team
))
out_account.balance_amount = amount_after
out_account.save(update_fields=['balance_amount'])
amount_before = in_account.balance_amount
amount_change = transfer_in_amount
amount_after = NP.minus(amount_before, transfer_in_amount)
finance_flows.append(FinanceFlow(
account=in_account, type=FinanceFlow.Type.VOID_ACCOUNT_TRANSFER_IN,
amount_before=amount_before, amount_change=amount_change, amount_after=amount_after,
void_account_transfer_record=account_transfer_record, creator=self.user, team=self.team
))
in_account.balance_amount = amount_after
in_account.save(update_fields=['balance_amount'])
serializer = AccountTransferRecordSerializer(instance=account_transfer_record)
return Response(data=serializer.data, status=status.HTTP_200_OK)
__all__ = [
'PaymentOrderViewSet', 'CollectionOrderViewSet',
'ChargeOrderViewSet', 'AccountTransferRecordViewSet',
]

View file

@ -20,7 +20,8 @@ class StockTransferOrderSerializer(BaseSerializer):
class Meta:
model = StockTransferGoods
read_only_fields = ['id', 'enable_batch_control']
read_only_fields = ['id', 'goods_number', 'goods_name', 'goods_barcode', 'unit_name',
'batch_number', 'enable_batch_control']
fields = ['goods', 'batch', 'stock_transfer_quantity', *read_only_fields]
def validate_goods(self, instance):