mirror of
https://github.com/himool/HimoolERP.git
synced 2026-01-06 17:25:43 +08:00
feat: 采购单据
This commit is contained in:
parent
c081200340
commit
b7447c7e9c
9 changed files with 251 additions and 7 deletions
|
|
@ -11,7 +11,6 @@ class PaymentOrder(Model):
|
|||
remark = CharField(max_length=256, null=True, blank=True, verbose_name='备注')
|
||||
total_amount = AmountField(null=True, verbose_name='总金额')
|
||||
discount_amount = AmountField(default=0, verbose_name='优惠金额')
|
||||
payment_amount = AmountField(null=True, verbose_name='实付金额')
|
||||
is_void = BooleanField(default=False, verbose_name='作废状态')
|
||||
creator = ForeignKey('system.User', on_delete=PROTECT,
|
||||
related_name='created_payment_orders', verbose_name='创建人')
|
||||
|
|
@ -21,6 +20,19 @@ class PaymentOrder(Model):
|
|||
class Meta:
|
||||
unique_together = [('number', 'team')]
|
||||
|
||||
@classmethod
|
||||
def get_number(cls, team):
|
||||
start_date, end_date = pendulum.now(), pendulum.tomorrow()
|
||||
instance = cls.objects.filter(team=team, create_time__gte=start_date, create_time__lt=end_date).last()
|
||||
|
||||
try:
|
||||
result = re.match('^(.*?)([1-9]+)$', instance.number)
|
||||
number = result.group(1) + str(int(result.group(2)) + 1)
|
||||
except AttributeError:
|
||||
number = 'FK' + start_date.format('YYYYMMDD') + '0001'
|
||||
|
||||
return number
|
||||
|
||||
|
||||
class PaymentAccount(Model):
|
||||
"""付款账户"""
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from extensions.routers import *
|
||||
from apps.purchase.views import *
|
||||
|
||||
|
||||
router = BaseRouter()
|
||||
router.register('purchase_orders', PurchaseOrderViewSet, 'purchase_order')
|
||||
urlpatterns = router.urls
|
||||
|
|
|
|||
|
|
@ -1,7 +1,18 @@
|
|||
from django_filters.rest_framework import FilterSet
|
||||
from django_filters.filters import *
|
||||
from apps.purchase.models import *
|
||||
|
||||
|
||||
class PurchaseOrderFilter(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 = PurchaseOrder
|
||||
fields = ['number', 'warehouse', 'supplier', 'handler', 'is_void', 'creator',
|
||||
'start_date', 'end_date']
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
||||
'PurchaseOrderFilter',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -10,8 +10,13 @@ class PurchaseOrder(Model):
|
|||
handler = ForeignKey('system.User', on_delete=PROTECT, related_name='purchase_orders', verbose_name='经手人')
|
||||
handle_time = DateTimeField(verbose_name='处理时间')
|
||||
remark = CharField(max_length=256, null=True, blank=True, verbose_name='备注')
|
||||
total_amount = AmountField(verbose_name='总金额')
|
||||
total_quantity = FloatField(verbose_name='总数量')
|
||||
total_quantity = FloatField(verbose_name='采购总数量')
|
||||
other_amount = AmountField(default=0, verbose_name='其他费用')
|
||||
total_amount = AmountField(verbose_name='采购总金额')
|
||||
payment_amount = AmountField(default=0, verbose_name='付款金额')
|
||||
arrears_amount = AmountField(default=0, verbose_name='欠款金额')
|
||||
payment_order = OneToOneField('finance.PaymentOrder', on_delete=PROTECT, null=True,
|
||||
related_name='purchase_order', verbose_name='付款单据')
|
||||
is_void = BooleanField(default=False, verbose_name='作废状态')
|
||||
enable_auto_stock_in = BooleanField(default=False, verbose_name='启用自动入库')
|
||||
creator = ForeignKey('system.User', on_delete=PROTECT,
|
||||
|
|
@ -22,6 +27,10 @@ class PurchaseOrder(Model):
|
|||
class Meta:
|
||||
unique_together = [('number', 'team')]
|
||||
|
||||
@classmethod
|
||||
def get_number(cls, team):
|
||||
return
|
||||
|
||||
|
||||
class PurchaseGoods(Model):
|
||||
"""采购商品"""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
from extensions.permissions import InterfacePermission
|
||||
|
||||
|
||||
__all__ = [
|
||||
class PurchaseOrderPermission(InterfacePermission):
|
||||
code = 'purchase_order'
|
||||
|
||||
|
||||
class PurchaseReturnOrderPermission(InterfacePermission):
|
||||
code = 'purchase_return_order'
|
||||
|
||||
|
||||
__all__ = [
|
||||
'PurchaseOrderPermission', 'PurchaseReturnOrderPermission',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,184 @@
|
|||
from extensions.serializers import *
|
||||
from extensions.exceptions import *
|
||||
from apps.purchase.models import *
|
||||
from apps.finance.models import *
|
||||
from apps.data.models import *
|
||||
from apps.goods.models import *
|
||||
from apps.system.models import *
|
||||
|
||||
|
||||
class PurchaseOrderSerializer(BaseSerializer):
|
||||
"""采购单据"""
|
||||
|
||||
class PurchaseGoodsSerializer(BaseSerializer):
|
||||
"""采购商品"""
|
||||
|
||||
goods_number = CharField(source='goods.number', read_only=True, label='商品编号')
|
||||
goods_name = CharField(source='goods.name', read_only=True, label='商品名称')
|
||||
goods_barcode = CharField(source='goods.barcode', read_only=True, label='商品条码')
|
||||
unit_name = CharField(source='goods.unit.name', read_only=True, label='单位名称')
|
||||
|
||||
class Meta:
|
||||
model = PurchaseGoods
|
||||
read_only_fields = ['id', 'goods_number', 'goods_name', 'goods_barcode', 'total_amount',
|
||||
'return_quantity', 'unit_name']
|
||||
fields = ['goods', 'purchase_quantity', 'purchase_price', *read_only_fields]
|
||||
|
||||
def validate_goods(self, instance):
|
||||
instance = self.validate_foreign_key(Goods, instance)
|
||||
if not instance.is_active:
|
||||
raise ValidationError(f'商品[{instance.name}]未激活')
|
||||
return instance
|
||||
|
||||
def validate_purchase_quantity(self, value):
|
||||
if value <= 0:
|
||||
raise ValidationError('采购数量小于或等于零')
|
||||
return value
|
||||
|
||||
def validate_purchase_price(self, value):
|
||||
if value <= 0:
|
||||
raise ValidationError('采购单价小于或等于零')
|
||||
return value
|
||||
|
||||
class PaymentAccountSerializer(BaseSerializer):
|
||||
"""付款账户"""
|
||||
|
||||
account_number = CharField(source='account.number', read_only=True, label='账户编号')
|
||||
account_name = CharField(source='account.name', read_only=True, label='账户名称')
|
||||
|
||||
class Meta:
|
||||
model = PaymentAccount
|
||||
read_only_fields = ['id', 'account_number', 'account_name']
|
||||
fields = ['account', 'payment_amount', *read_only_fields]
|
||||
|
||||
def validate_account(self, instance):
|
||||
instance = self.validate_foreign_key(Account, instance)
|
||||
if not instance.is_active:
|
||||
raise ValidationError(f'结算账户[{instance.name}]未激活')
|
||||
return instance
|
||||
|
||||
def validate_payment_amount(self, value):
|
||||
if value <= 0:
|
||||
raise ValidationError('付款金额小于或等于零')
|
||||
return value
|
||||
|
||||
warehouse_number = CharField(source='warehouse.number', read_only=True, label='仓库编号')
|
||||
warehouse_name = CharField(source='warehouse.name', read_only=True, label='仓库名称')
|
||||
supplier_number = CharField(source='supplier.number', read_only=True, label='供应商编号')
|
||||
supplier_name = CharField(source='supplier.name', read_only=True, label='供应商名称')
|
||||
handler_name = CharField(source='handler.name', read_only=True, label='经手人名称')
|
||||
creator_name = CharField(source='creator.name', read_only=True, label='创建人名称')
|
||||
purchase_goods_items = PurchaseGoodsSerializer(source='purchase_goods_set', many=True, label='采购商品')
|
||||
payment_account_items = PaymentAccountSerializer(source='payment_order.payment_accounts',
|
||||
required=False, many=True, label='付款账户')
|
||||
|
||||
class Meta:
|
||||
model = PurchaseOrder
|
||||
read_only_fields = ['id', 'warehouse_number', 'warehouse_name', 'supplier_number', 'supplier_name',
|
||||
'handler_name', 'total_amount', 'total_quantity', 'payment_amount', 'arrears_amount',
|
||||
'is_void', 'enable_auto_stock_in', 'creator_name', 'create_time']
|
||||
fields = ['number', 'warehouse', 'supplier', 'handler', 'handle_time', 'other_amount', 'remark',
|
||||
'purchase_goods_items', 'payment_account_items', *read_only_fields]
|
||||
|
||||
def validate_number(self, value):
|
||||
self.validate_unique({'number': value}, message=f'编号[{value}]已存在')
|
||||
return value
|
||||
|
||||
def validate_warehouse(self, instance):
|
||||
instance = self.validate_foreign_key(Warehouse, instance)
|
||||
if not instance.is_active:
|
||||
raise ValidationError(f'仓库[{instance.name}]未激活')
|
||||
|
||||
if not instance.is_locked:
|
||||
raise ValidationError(f'仓库[{instance.name}]已锁定')
|
||||
return instance
|
||||
|
||||
def validate_supplier(self, instance):
|
||||
instance = self.validate_foreign_key(Supplier, instance)
|
||||
if not instance.is_active:
|
||||
raise ValidationError(f'供应商[{instance.name}]未激活')
|
||||
return instance
|
||||
|
||||
def validate_handler(self, instance):
|
||||
instance = self.validate_foreign_key(User, instance)
|
||||
if not instance.is_active:
|
||||
raise ValidationError(f'经手人[{instance.name}]未激活')
|
||||
return instance
|
||||
|
||||
def validate_other_amount(self, value):
|
||||
if value <= 0:
|
||||
raise ValidationError('其他费用小于或等于零')
|
||||
return value
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
purchase_goods_items = validated_data.pop('purchase_goods_items')
|
||||
payment_account_items = validated_data.pop('payment_account_items', None)
|
||||
print(purchase_goods_items)
|
||||
print(payment_account_items)
|
||||
|
||||
validated_data['enable_auto_stock_in'] = self.team.enable_auto_stock_in
|
||||
validated_data['creator'] = self.user
|
||||
purchase_order = super().create(validated_data)
|
||||
|
||||
total_purchase_quantity = 0
|
||||
total_purchase_amount = 0
|
||||
|
||||
# 创建采购商品
|
||||
purchase_goods_set = []
|
||||
for purchase_goods_item in purchase_goods_items:
|
||||
purchase_quantity = purchase_goods_item['purchase_quantity']
|
||||
purchase_price = purchase_goods_item['purchase_price']
|
||||
total_amount = NP.times(purchase_quantity, purchase_price)
|
||||
purchase_goods_set.append(PurchaseGoods(
|
||||
purchase_order=purchase_order, goods=purchase_goods_item['goods'],
|
||||
purchase_quantity=purchase_quantity, purchase_price=purchase_price,
|
||||
total_amount=total_amount, team=self.team
|
||||
))
|
||||
|
||||
total_purchase_quantity = NP.plus(total_purchase_quantity, purchase_quantity)
|
||||
total_purchase_amount = NP.plus(total_purchase_amount, total_amount)
|
||||
else:
|
||||
PurchaseGoods.objects.bulk_create(purchase_goods_set)
|
||||
total_purchase_amount = NP.plus(total_purchase_amount, purchase_order.other_amount)
|
||||
purchase_order.total_quantity = total_purchase_quantity
|
||||
purchase_order.total_amount = total_purchase_amount
|
||||
|
||||
# 创建付款单据
|
||||
if payment_account_items:
|
||||
payment_order_number = PaymentOrder.get_number(team=self.team)
|
||||
payment_order_remark = f'采购单据: {purchase_order.number}'
|
||||
payment_order = PaymentOrder.objects.create(
|
||||
number=payment_order_number, supplier=purchase_order.supplier,
|
||||
handler=purchase_order.handler, handle_time=purchase_order.handle_time,
|
||||
remark=payment_order_remark, creator=self.user, team=self.team
|
||||
)
|
||||
|
||||
total_payment_amount = 0
|
||||
|
||||
# 创建付款账户
|
||||
payment_accounts = []
|
||||
for payment_account_item in payment_account_items:
|
||||
payment_amount = payment_account_item['payment_amount']
|
||||
payment_accounts.append(PaymentAccount(
|
||||
payment_order=payment_order, account=payment_account_item['account'],
|
||||
payment_amount=payment_amount, team=self.team
|
||||
))
|
||||
|
||||
total_payment_amount = NP.plus(total_payment_amount, payment_amount)
|
||||
else:
|
||||
PaymentAccount.objects.bulk_create(payment_accounts)
|
||||
payment_order.total_amount = total_payment_amount
|
||||
payment_order.save(update_fields=['total_amount'])
|
||||
purchase_order.payment_amount = total_payment_amount
|
||||
purchase_order.arrears_amount = NP.minus(total_purchase_amount, total_payment_amount)
|
||||
purchase_order.payment_order = payment_order
|
||||
|
||||
purchase_order = purchase_order.save(update_fields=['total_quantity', 'total_amount', 'payment_amount',
|
||||
'arrears_amount', 'payment_order'])
|
||||
return purchase_order
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
||||
'PurchaseOrderSerializer',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,28 @@
|
|||
from extensions.permissions import *
|
||||
from extensions.exceptions import *
|
||||
from extensions.viewsets import *
|
||||
from apps.purchase.serializers import *
|
||||
from apps.purchase.permissions import *
|
||||
from apps.purchase.filters import *
|
||||
from apps.purchase.schemas import *
|
||||
from apps.purchase.models import *
|
||||
|
||||
|
||||
class PurchaseOrderViewSet(BaseViewSet, ListModelMixin, RetrieveModelMixin, CreateModelMixin):
|
||||
"""采购单据"""
|
||||
|
||||
serializer_class = PurchaseOrderSerializer
|
||||
permission_classes = [IsAuthenticated, PurchaseOrderPermission]
|
||||
filterset_class = PurchaseOrderFilter
|
||||
search_fields = ['number', 'supplier__number', 'supplier__name', 'remark']
|
||||
ordering_fields = ['id', 'number', 'total_quantity', 'total_amount', 'create_time']
|
||||
ordering = ['-number', 'id']
|
||||
select_related_fields = ['warehouse', 'supplier', 'handler', 'creator',
|
||||
'purchase_goods_set__goods__unit']
|
||||
prefetch_related_fields = ['purchase_goods_set', 'payment_order__payment_accounts']
|
||||
queryset = PurchaseOrder.objects.all()
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
||||
'PurchaseOrderViewSet',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ class Team(Model):
|
|||
number = CharField(max_length=32, unique=True, verbose_name='编号')
|
||||
expiry_time = DateTimeField(verbose_name='到期时间')
|
||||
create_time = DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
enable_auto_stock_in = BooleanField(default=False, verbose_name='启用自动入库')
|
||||
enable_auto_stock_out = BooleanField(default=False, verbose_name='启用自动出库')
|
||||
|
||||
|
||||
class PermissionType(Model):
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from django.db.models import Model, IntegerChoices, TextChoices
|
|||
from django.db.models import Sum, Count, Value, F, Q, Prefetch
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db import transaction, connection
|
||||
import pendulum
|
||||
import re
|
||||
|
||||
|
||||
class AmountField(DecimalField):
|
||||
|
|
@ -23,4 +25,5 @@ __all__ = [
|
|||
'BooleanField', 'IntegerField', 'FloatField', 'DecimalField', 'AmountField',
|
||||
'CharField', 'DateField', 'DateTimeField', 'JSONField', 'FileField', 'ImageField',
|
||||
'Sum', 'Count', 'Value', 'F', 'Q', 'Prefetch', 'Coalesce', 'transaction', 'connection',
|
||||
'pendulum', 're',
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue