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