from django.db import models from django.conf import settings from flower.common import ModelBase from basic_info import models as basic_models from stateflow import models as stateflow_models class PlateOrder(ModelBase): """开版管理订单""" # 自动编号相关 design_code = models.CharField(max_length=50, blank=True, null=True, verbose_name='设计编号') # 版相关信息 plate_type = models.CharField(max_length=20, blank=True, null=True, verbose_name='起版情况') # 首版/复版等 plate_date = models.DateTimeField(null=True, blank=True, verbose_name='下版时间') plate_method = models.CharField(max_length=50, blank=True, null=True, verbose_name='开版方式') plate_image = models.FileField(upload_to='plate_images/', null=True, blank=True, verbose_name='开版图') plate_notes = models.TextField(blank=True, null=True, verbose_name='打版注意事项') reprint_reason = models.TextField(blank=True, null=True, verbose_name='复版原因') # 状态和进程 # development_status = models.CharField(max_length=20, blank=True, null=True, verbose_name='开发进程') # 未打印/待画图等 urgency_level = models.CharField(max_length=20, default='正常', verbose_name='紧急程度') # 正常/加急 # 人员关联 salesperson = models.ForeignKey( basic_models.Employee, on_delete=models.PROTECT, null=True, blank=True, related_name='plate_orders_as_salesperson', verbose_name='销售员' ) merchandiser = models.ForeignKey( basic_models.Employee, on_delete=models.PROTECT, null=True, blank=True, related_name='plate_orders_as_merchandiser', verbose_name='跟单员' ) designer = models.ForeignKey( basic_models.Employee, on_delete=models.PROTECT, null=True, blank=True, related_name='plate_orders_as_designer', verbose_name='设计师' ) # 客户和地址信息 customer = models.ForeignKey( basic_models.Customer, on_delete=models.PROTECT, related_name='plate_orders', verbose_name='客户' ) area = models.CharField(max_length=255, blank=True, null=True, verbose_name='区域') default_address = models.CharField(max_length=500, blank=True, null=True, verbose_name='默认地址') # 质量评级 is_mark_frame = models.BooleanField(default=False, verbose_name='是否套唛架') drawing_rating = models.CharField(max_length=20, blank=True, null=True, verbose_name='画图评级') color_matching_rating = models.CharField(max_length=20, blank=True, null=True, verbose_name='调色评级') sample_rating = models.CharField(max_length=20, blank=True, null=True, verbose_name='套样评级') difficulty_rating = models.CharField(max_length=20, blank=True, null=True, verbose_name='难度评级') # 时间相关 required_completion_date = models.DateField(null=True, blank=True, verbose_name='要求完成时间') completion_date = models.DateTimeField(null=True, blank=True, verbose_name='完成时间') # 产品信息 fabric = models.CharField(max_length=100, blank=True, null=True, verbose_name='布料') fabric_source = models.CharField(max_length=100, blank=True, null=True, verbose_name='布料来源') width = models.CharField(max_length=50, blank=True, null=True, verbose_name='幅宽') style_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='款号名称') # 生产方式 production_method = models.CharField(max_length=50, blank=True, null=True, verbose_name='做货方式') # 样品相关 sample_meter = models.CharField(max_length=100, blank=True, null=True, verbose_name='米样') required_sample_meters = models.DecimalField( max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='客户要求米样米数' ) # 审批和订单状态 approval_result = models.CharField(max_length=50, blank=True, null=True, verbose_name='审批结果') is_ordered = models.BooleanField(default=False, verbose_name='是否已下单') is_invalid = models.BooleanField(default=False, verbose_name='是否作废') customer_feedback = models.TextField(blank=True, null=True, verbose_name='客户修改意见') # 流程管理 process = models.IntegerField(default=settings.PLATE_ORDER_DEFAULT_PROCESS_ID, verbose_name='关联流程') business_object = models.OneToOneField( stateflow_models.BusinessObject, on_delete=models.SET_NULL, null=True, blank=True, related_name='plate_order', verbose_name='流程实例', ) def __str__(self): return f'PlateOrder-{self.id}' def save(self, *args, **kwargs): """保存时自动创建 BusinessObject""" # 先保存以获取 ID(如果是新建) is_new = self.pk is None super().save(*args, **kwargs) # 如果有 process 且没有 business_object,则自动创建 if self.process and not self.business_object: try: from stateflow.models import Process process_obj = Process.objects.get(id=self.process) # 创建 BusinessObject business_object = stateflow_models.BusinessObject.objects.create( process=process_obj ) self.business_object = business_object # 再次保存以更新 business_object 字段 super().save(update_fields=['business_object']) except Process.DoesNotExist: # 如果流程不存在,忽略错误 pass class Meta: verbose_name = '开版订单' verbose_name_plural = '开版订单' ordering = ['-created_at'] permissions = [ ('can_invalidate_plateorder', '可以作废开版订单'), ('can_activate_plateorder', '可以恢复开版订单'), ] @property def status(self) -> str: """ 返回当前状态名称 规则: - current_state 为 None:返回"已完成" - 否则:返回 current_state 的名称 注意:废除了"未开始"的概念,直接显示节点名称 """ from stateflow.services import get_display_status return get_display_status(self.business_object) @property def status_id(self): """返回当前状态ID(下一个待执行的状态ID)""" if not self.business_object: return None current_state = self.business_object.get_current_state() return current_state.id if current_state else None @property def is_completed(self) -> bool: """判断是否完成""" if not self.business_object: return False # 检查流程是否完成 from stateflow.services import get_overall_status return get_overall_status(self.business_object) == 'completed' @property def has_started(self) -> bool: """判断是否已开始""" if not self.business_object: return False # 检查是否有状态流转记录 from stateflow.models import StateFlowRecord has_records = StateFlowRecord.objects.filter( business_object=self.business_object ).exists() return has_records @property def progress_percentage(self) -> float: """计算进度百分比""" if not self.business_object: return 0.0 return self.business_object.get_progress_percentage() class PrintingOrder(ModelBase): """PrintingOrder model representing a printing order.""" customer = models.ForeignKey( basic_models.Customer, on_delete=models.PROTECT, related_name='printing_orders', verbose_name='客户', ) fabric = models.CharField(max_length=100, verbose_name='面料') width = models.CharField(max_length=100, verbose_name='幅宽') is_urgent = models.BooleanField(default=False, verbose_name='紧急') area = models.CharField(max_length=100, null=True, blank=True, verbose_name='地区') address = models.CharField(max_length=200, null=True, blank=True, verbose_name='地址') fabric_source = models.CharField(max_length=100, null=True, blank=True, verbose_name='布料来源') is_fabric_received = models.BooleanField(default=False, verbose_name='布料是否已收') craft = models.CharField(max_length=100, null=True, blank=True, verbose_name='工艺') description = models.TextField(blank=True, null=True, verbose_name='订单描述') outgoing_date = models.DateField(null=True, blank=True, verbose_name='出货日期') curve = models.CharField(max_length=100, blank=True, null=True, verbose_name='曲线') new_curve = models.CharField(max_length=100, blank=True, null=True, verbose_name='新加曲线') position = models.TextField(blank=True, null=True, verbose_name='位置') printing_warn = models.TextField(blank=True, null=True, verbose_name='打印注意事项') rolling_warn = models.TextField(blank=True, null=True, verbose_name='滚筒注意事项') production_warn = models.TextField(blank=True, null=True, verbose_name='生产注意事项') is_invalid = models.BooleanField(default=False, verbose_name='是否作废') process = models.ForeignKey( stateflow_models.Process, on_delete=models.PROTECT, null=True, blank=True, related_name='printing_orders', verbose_name='印染流程', ) def __str__(self): return self.human_id class Meta: verbose_name = '印染订单' verbose_name_plural = '印染订单' permissions = [ ('can_invalidate_printingorder', '可以作废印染订单'), ('can_activate_printingorder', '可以恢复印染订单'), ] @property def human_id(self) -> str: if self.id is None or self.created_at is None: return "" return f"{self.created_at.year}{self.created_at.month:02d}{self.created_at.day:02d}{self.id:06d}" def can_change_process(self) -> bool: """检查是否可以修改流程(所有job都未开始)""" return not any(job.has_started for job in self.printing_jobs.all()) @property def progress(self) -> int: """计算订单完成进度百分比(已完成的任务数 / 总任务数)""" jobs = self.printing_jobs.all() if not jobs.exists(): return 0 completed_count = sum(1 for job in jobs if job.is_completed) return int((completed_count / jobs.count()) * 100) class PrintingJob(ModelBase): """PrintingJob model representing a printing job associated with an order.""" printing_order = models.ForeignKey( PrintingOrder, on_delete=models.CASCADE, related_name='printing_jobs', verbose_name='主订单', ) product = models.ForeignKey( basic_models.Product, on_delete=models.PROTECT, related_name='printing_jobs', verbose_name='产品', ) quantity = models.PositiveIntegerField(verbose_name='数量') unit = models.CharField(max_length=50, verbose_name='单位') size = models.CharField(max_length=100, null=True, blank=True, verbose_name='一段尺寸') pieces = models.PositiveIntegerField(null=True, blank=True, verbose_name='件数') description = models.TextField(blank=True, null=True, verbose_name='备注') business_object = models.OneToOneField( stateflow_models.BusinessObject, on_delete=models.SET_NULL, null=True, blank=True, related_name='printing_job', verbose_name='流程实例', ) def __str__(self): return self.printing_order.human_id @property def status(self) -> str: """ 返回当前状态名称 规则: - current_state 为 None:返回"已完成" - 否则:返回 current_state 的名称 注意:废除了"未开始"的概念,直接显示节点名称 """ from stateflow.services import get_display_status return get_display_status(self.business_object) @property def status_id(self): """返回当前状态ID(下一个待执行的状态ID)""" if not self.business_object: return None current_state = self.business_object.get_current_state() return current_state.id if current_state else None @property def is_completed(self) -> bool: """判断是否完成(所有流程节点都已完成)""" if not self.business_object or not self.printing_order.process: return False # 获取流程的所有节点 process_nodes = self.printing_order.process.process_nodes.all() total_nodes = process_nodes.count() if total_nodes == 0: return False # 检查已完成的节点数 completed_count = self.business_object.state_logs.filter(is_cancelled=False).count() # 所有节点都完成了才算完成 return completed_count >= total_nodes @property def has_started(self) -> bool: """判断是否已开始(有BusinessObject且有状态记录,或不在初始状态)""" if not self.business_object: return False # 检查是否有状态流转记录 from stateflow.models import StateFlowRecord has_records = StateFlowRecord.objects.filter( business_object=self.business_object ).exists() return has_records class Meta: verbose_name = '印染款式明细' verbose_name_plural = '印染款式明细'