283 lines
13 KiB
Python
283 lines
13 KiB
Python
from decimal import Decimal
|
|
|
|
from django.db import models
|
|
from django.db.models import Sum
|
|
from apps.utils.models import CommonBModel, BaseModel, CommonBDModel, CommonADModel
|
|
from apps.mtm.models import Material
|
|
from apps.wf.models import Ticket
|
|
|
|
|
|
# Create your models here.
|
|
class Supplier(CommonBModel):
|
|
"""
|
|
TN:供应商
|
|
"""
|
|
name = models.CharField('供应商名称', max_length=50)
|
|
number= models.CharField('供应商编号', max_length=20, null=True, blank=True)
|
|
contact = models.CharField('联系人', max_length=20, default='', blank=True)
|
|
contact_phone = models.CharField('联系电话', max_length=11, default='', blank=True)
|
|
address = models.CharField('地址', max_length=200, default='', blank=True)
|
|
can_outsource = models.BooleanField('是否可外协', default=False)
|
|
|
|
class SupplierAudit(CommonADModel):
|
|
name = models.CharField('供应商名称', max_length=100)
|
|
material_name = models.CharField('物料名称', max_length=100)
|
|
material_cate = models.CharField('物料类别', max_length=100)
|
|
survery_form = models.ForeignKey('system.file', verbose_name='调查表', on_delete=models.SET_NULL, null=True, blank=True, related_name='supplier_audit_survey_form')
|
|
business_license = models.ForeignKey('system.file', verbose_name='营业执照', on_delete=models.SET_NULL, null=True, blank=True, related_name='supplier_audit_business_license')
|
|
quality_certificate = models.ForeignKey('system.file', verbose_name='质量证书', on_delete=models.SET_NULL, null=True, blank=True, related_name='supplier_audit_quality_certificate')
|
|
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.SET_NULL, null=True, blank=True)
|
|
|
|
|
|
class PuPlan(CommonBModel):
|
|
"""
|
|
TN:采购计划
|
|
"""
|
|
PUPLAN_CREATE = 10
|
|
PUPLAN_SUBMITED = 20
|
|
PUPLAN_ORDERING = 30
|
|
PUPLAN_ORDERED = 40
|
|
PUPLAN_DONE = 50
|
|
PUPLAN_STATES = (
|
|
(PUPLAN_CREATE, '创建中'),
|
|
(PUPLAN_SUBMITED, '已提交'),
|
|
(PUPLAN_ORDERING, '下单中'),
|
|
(PUPLAN_ORDERED, '下单完成'),
|
|
(PUPLAN_DONE, '已完成'),
|
|
)
|
|
state = models.PositiveSmallIntegerField(
|
|
'状态', choices=PUPLAN_STATES, default=10, help_text=str(PUPLAN_STATES))
|
|
number = models.CharField('编号', max_length=20)
|
|
name = models.CharField('名称', max_length=50, null=True, blank=True)
|
|
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
|
submit_user = models.ForeignKey(
|
|
'system.user', verbose_name='提交人', related_name='submit_user_puplan', on_delete=models.CASCADE, null=True, blank=True)
|
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
|
|
|
|
|
class PuOrder(CommonBModel):
|
|
"""
|
|
TN:采购订单
|
|
"""
|
|
PUORDER_CREATE = 10
|
|
PUORDER_SUBMITED = 20
|
|
PUORDER_SHIP = 30
|
|
PUORDER_DONE = 40
|
|
PUORDER_STATES = (
|
|
(PUORDER_CREATE, '创建中'),
|
|
(PUORDER_SUBMITED, '已提交'),
|
|
(PUORDER_SHIP, '到货中'),
|
|
(PUORDER_DONE, '已完成'),
|
|
)
|
|
state = models.PositiveSmallIntegerField(
|
|
'状态', choices=PUORDER_STATES, default=10, help_text=str(PUORDER_STATES))
|
|
number = models.CharField('订单编号', max_length=20, null=True, blank=True)
|
|
supplier = models.ForeignKey(
|
|
Supplier, verbose_name='供应商', on_delete=models.CASCADE)
|
|
contract = models.ForeignKey(
|
|
'pum.PuContract', verbose_name='采购合同', on_delete=models.SET_NULL,
|
|
null=True, blank=True, related_name='orders')
|
|
delivery_date = models.DateField('截止到货日期', null=True, blank=True)
|
|
submit_time = models.DateTimeField('提交时间', null=True, blank=True)
|
|
submit_user = models.ForeignKey(
|
|
'system.user', verbose_name='提交人', related_name='submit_user_puorder', on_delete=models.CASCADE, null=True, blank=True)
|
|
materials = models.ManyToManyField(
|
|
Material, verbose_name='多个物料', blank=True, through='pum.puorderitem', related_name='pu_order_materials')
|
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
|
|
|
|
|
class PuOrderItem(BaseModel):
|
|
"""TN:物料信息表"""
|
|
material = models.ForeignKey(
|
|
Material, verbose_name='物料', on_delete=models.CASCADE, related_name='pu_orderitem_material')
|
|
count = models.PositiveIntegerField('所需数量', default=0)
|
|
delivered_count = models.PositiveIntegerField('已到货数量', default=0)
|
|
unit_price = models.DecimalField('单价', max_digits=14, decimal_places=2, null=True, blank=True)
|
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
|
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
|
|
on_delete=models.CASCADE, null=True, blank=True, related_name='item_puorder')
|
|
|
|
|
|
class PuPlanItem(CommonBDModel):
|
|
"""
|
|
TN:采购计划明细(因为各部门填写所以需要belong_dept字段)
|
|
"""
|
|
material = models.ForeignKey(
|
|
Material, verbose_name='所需物料', on_delete=models.CASCADE)
|
|
need_count = models.PositiveIntegerField('所属数量')
|
|
need_date = models.DateField('需求日期')
|
|
unit_price = models.DecimalField('单价', max_digits=14, decimal_places=2, null=True, blank=True)
|
|
total_price = models.DecimalField('总价', max_digits=14, decimal_places=2, null=True, blank=True)
|
|
note = models.TextField('备注', default='')
|
|
pu_plan = models.ForeignKey(PuPlan, verbose_name='采购计划', on_delete=models.CASCADE,
|
|
null=True, blank=True, related_name='item_puplan')
|
|
pu_order = models.ForeignKey(PuOrder, verbose_name='关联采购订单',
|
|
on_delete=models.SET_NULL, null=True, blank=True, related_name='puplan_item_puorder')
|
|
|
|
|
|
class QuotationApply(CommonADModel):
|
|
"""
|
|
TN:报价申请表
|
|
"""
|
|
customer_name = models.CharField(max_length=50,verbose_name="客户名称")
|
|
product_name = models.CharField(max_length=50,verbose_name="产品名称")
|
|
contact_person = models.CharField(max_length=50,verbose_name="联系人")
|
|
contact_phone = models.CharField(max_length=20,verbose_name="联系电话", null=True, blank=True)
|
|
receive_address = models.CharField(max_length=100,verbose_name="收件地址", null=True, blank=True)
|
|
product_spec_quantity = models.TextField(verbose_name="产品规格/数量", null=True, blank=True)
|
|
quotation_basis = models.TextField(verbose_name="报价依据", null=True, blank=True)
|
|
suggested_price_calc = models.TextField(verbose_name="建议价格及计算方式", null=True, blank=True)
|
|
quotation_range = models.CharField(max_length=100,verbose_name="报价区间", null=True, blank=True)
|
|
quoter = models.CharField(max_length=50,verbose_name="报价人", null=True, blank=True)
|
|
apply_date = models.DateField(verbose_name="申请日期",auto_now_add=True)
|
|
ticket = models.OneToOneField('wf.ticket', verbose_name='关联工单',
|
|
on_delete=models.CASCADE, related_name='quo_ticket', null=True, blank=True)
|
|
|
|
|
|
class PuContract(CommonBDModel):
|
|
"""
|
|
TN:采购合同
|
|
"""
|
|
STATUS_DRAFT = 10
|
|
STATUS_ACTIVE = 20
|
|
STATUS_DONE = 30
|
|
STATUS_TERMINATED = 40
|
|
STATUS_CHOICES = (
|
|
(STATUS_DRAFT, '草稿'),
|
|
(STATUS_ACTIVE, '执行中'),
|
|
(STATUS_DONE, '已完成'),
|
|
(STATUS_TERMINATED, '已终止'),
|
|
)
|
|
SETTLEMENT_UNPAID = 10
|
|
SETTLEMENT_PARTIAL = 20
|
|
SETTLEMENT_FULL = 30
|
|
SETTLEMENT_CHOICES = (
|
|
(SETTLEMENT_UNPAID, '未付款'),
|
|
(SETTLEMENT_PARTIAL, '部分付款'),
|
|
(SETTLEMENT_FULL, '全部付款'),
|
|
)
|
|
name = models.CharField('合同名称', max_length=100)
|
|
number = models.CharField('合同编号', max_length=100, unique=True)
|
|
supplier = models.ForeignKey(Supplier, verbose_name='供应商', on_delete=models.CASCADE, related_name='contracts')
|
|
contract_amount = models.DecimalField('合同金额', max_digits=14, decimal_places=2, default=0)
|
|
sign_date = models.DateField('签订日期')
|
|
effective_date = models.DateField('生效日期', null=True, blank=True)
|
|
end_date = models.DateField('截止日期', null=True, blank=True)
|
|
status = models.PositiveSmallIntegerField(
|
|
'合同状态', choices=STATUS_CHOICES, default=STATUS_DRAFT, help_text=str(STATUS_CHOICES))
|
|
settlement_status = models.PositiveSmallIntegerField(
|
|
'结算状态', choices=SETTLEMENT_CHOICES, default=SETTLEMENT_UNPAID, help_text=str(SETTLEMENT_CHOICES))
|
|
paid_amount = models.DecimalField('累计已付款', max_digits=14, decimal_places=2, default=0)
|
|
unpaid_amount = models.DecimalField('累计未付款', max_digits=14, decimal_places=2, default=0)
|
|
pay_progress = models.DecimalField('付款进度', max_digits=5, decimal_places=2, default=0)
|
|
description = models.CharField('描述', max_length=200, blank=True, null=True)
|
|
|
|
class Meta:
|
|
verbose_name = '采购合同'
|
|
verbose_name_plural = verbose_name
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def save(self, *args, **kwargs):
|
|
refresh_settlement = kwargs.pop('refresh_settlement', True)
|
|
super().save(*args, **kwargs)
|
|
if refresh_settlement:
|
|
self.refresh_settlement()
|
|
|
|
def refresh_settlement(self):
|
|
paid_amount = PuContractRecord.objects.filter(contract=self).aggregate(
|
|
total=Sum('amount')
|
|
)['total'] or Decimal('0.00')
|
|
contract_amount = Decimal(str(self.contract_amount or 0)).quantize(Decimal('0.01'))
|
|
unpaid_amount = contract_amount - paid_amount
|
|
if unpaid_amount < Decimal('0.00'):
|
|
unpaid_amount = Decimal('0.00')
|
|
if contract_amount <= Decimal('0.00'):
|
|
pay_progress = Decimal('0.00')
|
|
else:
|
|
pay_progress = (paid_amount * Decimal('100.00') / contract_amount).quantize(Decimal('0.01'))
|
|
if pay_progress > Decimal('100.00'):
|
|
pay_progress = Decimal('100.00')
|
|
if paid_amount <= Decimal('0.00'):
|
|
settlement_status = self.SETTLEMENT_UNPAID
|
|
elif paid_amount >= contract_amount and contract_amount > Decimal('0.00'):
|
|
settlement_status = self.SETTLEMENT_FULL
|
|
else:
|
|
settlement_status = self.SETTLEMENT_PARTIAL
|
|
status = self.status
|
|
if status != self.STATUS_TERMINATED:
|
|
if paid_amount <= Decimal('0.00'):
|
|
status = self.STATUS_DRAFT
|
|
elif paid_amount >= contract_amount and contract_amount > Decimal('0.00'):
|
|
status = self.STATUS_DONE
|
|
else:
|
|
status = self.STATUS_ACTIVE
|
|
type(self).objects.filter(pk=self.pk).update(
|
|
paid_amount=paid_amount,
|
|
unpaid_amount=unpaid_amount,
|
|
pay_progress=pay_progress,
|
|
settlement_status=settlement_status,
|
|
status=status,
|
|
)
|
|
self.paid_amount = paid_amount
|
|
self.unpaid_amount = unpaid_amount
|
|
self.pay_progress = pay_progress
|
|
self.settlement_status = settlement_status
|
|
self.status = status
|
|
|
|
|
|
class PuContractRecord(CommonBDModel):
|
|
"""
|
|
TN:采购合同付款流水
|
|
"""
|
|
STAGE_FIRST = 10
|
|
STAGE_MIDDLE = 20
|
|
STAGE_FINAL = 30
|
|
STAGE_OTHER = 40
|
|
STAGE_CHOICES = (
|
|
(STAGE_FIRST, '首款'),
|
|
(STAGE_MIDDLE, '中间款'),
|
|
(STAGE_FINAL, '尾款'),
|
|
(STAGE_OTHER, '其他'),
|
|
)
|
|
PAY_BANK = 10
|
|
PAY_CASH = 20
|
|
PAY_ACCEPTANCE = 30
|
|
PAY_WECHAT = 40
|
|
PAY_ALIPAY = 50
|
|
PAY_OTHER = 60
|
|
PAY_METHOD_CHOICES = (
|
|
(PAY_BANK, '银行转账'),
|
|
(PAY_CASH, '现金'),
|
|
(PAY_ACCEPTANCE, '承兑'),
|
|
(PAY_WECHAT, '微信'),
|
|
(PAY_ALIPAY, '支付宝'),
|
|
(PAY_OTHER, '其他'),
|
|
)
|
|
contract = models.ForeignKey(
|
|
PuContract, verbose_name='采购合同', on_delete=models.CASCADE, related_name='records')
|
|
record_date = models.DateField('付款日期')
|
|
amount = models.DecimalField('付款金额', max_digits=14, decimal_places=2)
|
|
stage_type = models.PositiveSmallIntegerField(
|
|
'阶段类型', choices=STAGE_CHOICES, default=STAGE_OTHER, help_text=str(STAGE_CHOICES))
|
|
pay_method = models.PositiveSmallIntegerField(
|
|
'付款方式', choices=PAY_METHOD_CHOICES, default=PAY_BANK, help_text=str(PAY_METHOD_CHOICES))
|
|
voucher_no = models.CharField('凭证号', max_length=100, null=True, blank=True)
|
|
remark = models.CharField('备注', max_length=200, null=True, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = '采购合同付款流水'
|
|
verbose_name_plural = verbose_name
|
|
ordering = ['-record_date', '-create_time']
|
|
|
|
def save(self, *args, **kwargs):
|
|
super().save(*args, **kwargs)
|
|
self.contract.refresh_settlement()
|
|
|
|
def delete(self, using=None, *args, **kwargs):
|
|
contract = self.contract
|
|
result = super().delete(using=using, *args, **kwargs)
|
|
contract.refresh_settlement()
|
|
return result
|