1
0
forked from erp-dev/erp

feat: printing module

This commit is contained in:
2025-11-13 18:22:42 +08:00
parent 8097d6941c
commit f071e5fff9
35 changed files with 5537 additions and 440 deletions

View File

@@ -1,6 +1,171 @@
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='跟单员'
)
# 客户和地址信息
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='布料')
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='客户修改意见')
# 流程管理
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 self.human_id or f'PlateOrder-{self.id}'
class Meta:
verbose_name = '开版订单'
verbose_name_plural = '开版订单'
ordering = ['-created_at']
permissions = [
('can_invalidate_plateorder', '可以作废开版订单'),
('can_activate_plateorder', '可以恢复开版订单'),
]
@property
def status(self) -> str:
"""返回当前状态名称"""
if not self.business_object:
return '未开始'
current_state = self.business_object.get_current_state()
# current_state 为 None 有两种情况:未开始或已完成
if current_state is None:
# 检查是否有完成记录来区分
has_completed_records = self.business_object.state_logs.filter(is_cancelled=False).exists()
if has_completed_records:
return '已完成'
else:
return '未开始'
return current_state.name
@property
def status_id(self):
"""返回当前状态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):
@@ -28,6 +193,14 @@ class PrintingOrder(ModelBase):
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
@@ -42,8 +215,24 @@ class PrintingOrder(ModelBase):
@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."""
@@ -61,13 +250,81 @@ class PrintingJob(ModelBase):
)
quantity = models.PositiveIntegerField(verbose_name='数量')
unit = models.CharField(max_length=50, verbose_name='单位')
size = models.CharField(max_length=100, verbose_name='一段尺寸')
pieces = models.PositiveIntegerField(verbose_name='件数')
description = models.TextField(blank=True, 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:
"""返回当前状态名称"""
if not self.business_object:
return '未开始'
current_state = self.business_object.get_current_state()
# current_state 为 None 有两种情况:未开始或已完成
if current_state is None:
# 检查是否有完成记录来区分
has_completed_records = self.business_object.state_logs.filter(is_cancelled=False).exists()
if has_completed_records:
return '已完成'
else:
return '未开始'
return current_state.name
@property
def status_id(self):
"""返回当前状态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 = '印染款式明细'