forked from erp-dev/erp
feat: printing module
This commit is contained in:
@@ -1,9 +1,199 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import action
|
||||
from django.forms import BaseInlineFormSet
|
||||
from django.utils.html import format_html
|
||||
from . import models
|
||||
|
||||
|
||||
@admin.register(models.PlateOrder)
|
||||
class PlateOrderAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'id',
|
||||
'design_code',
|
||||
'customer_name',
|
||||
'style_name',
|
||||
'plate_type',
|
||||
'development_status_display',
|
||||
'urgency_display',
|
||||
'progress_display',
|
||||
'salesperson_name',
|
||||
'required_completion_date',
|
||||
'created_at',
|
||||
)
|
||||
list_filter = (
|
||||
'urgency_level',
|
||||
'plate_type',
|
||||
'is_ordered',
|
||||
'is_mark_frame',
|
||||
'created_at',
|
||||
'required_completion_date',
|
||||
)
|
||||
search_fields = (
|
||||
'design_code',
|
||||
'style_name',
|
||||
'customer__name',
|
||||
'salesperson__name',
|
||||
'merchandiser__name',
|
||||
)
|
||||
readonly_fields = ('progress_display', 'status_display')
|
||||
actions = ['advance_to_next_state_action', 'step_back_one_state_action', 'reset_progress_action']
|
||||
autocomplete_fields = ['customer', 'salesperson', 'merchandiser']
|
||||
|
||||
fieldsets = [
|
||||
('基本信息', {
|
||||
'fields': ['design_code', 'plate_type', 'plate_date', 'urgency_level']
|
||||
}),
|
||||
('客户信息', {
|
||||
'fields': ['customer', 'area', 'default_address', 'salesperson', 'merchandiser']
|
||||
}),
|
||||
('产品信息', {
|
||||
'fields': ['style_name', 'fabric', 'width', 'production_method']
|
||||
}),
|
||||
('开版信息', {
|
||||
'fields': [
|
||||
'plate_method', 'plate_image', 'plate_notes', 'reprint_reason',
|
||||
'is_mark_frame', 'sample_meter', 'required_sample_meters'
|
||||
]
|
||||
}),
|
||||
('质量评级', {
|
||||
'fields': [
|
||||
'drawing_rating', 'color_matching_rating',
|
||||
'sample_rating', 'difficulty_rating'
|
||||
]
|
||||
}),
|
||||
('时间管理', {
|
||||
'fields': ['required_completion_date', 'completion_date']
|
||||
}),
|
||||
('审批与反馈', {
|
||||
'fields': [
|
||||
'approval_result', 'customer_feedback',
|
||||
'is_ordered'
|
||||
]
|
||||
}),
|
||||
('流程管理', {
|
||||
'fields': ['business_object', 'status_display', 'progress_display'],
|
||||
'classes': ['collapse']
|
||||
}),
|
||||
]
|
||||
|
||||
@admin.display(description='客户')
|
||||
def customer_name(self, obj):
|
||||
return obj.customer.name if obj.customer else '-'
|
||||
|
||||
@admin.display(description='销售员')
|
||||
def salesperson_name(self, obj):
|
||||
return obj.salesperson.name if obj.salesperson else '-'
|
||||
|
||||
@admin.display(description='开发进程')
|
||||
def development_status_display(self, obj):
|
||||
status = obj.status
|
||||
color_map = {
|
||||
'未打印': 'gray',
|
||||
'待画图': 'orange',
|
||||
'画图完成': 'blue',
|
||||
'调色样': 'purple',
|
||||
'调色完成': 'green',
|
||||
'套纸样': 'cyan',
|
||||
'取消版': 'red',
|
||||
'客户审批': 'yellow',
|
||||
'开版完成': 'darkgreen',
|
||||
'已下单': 'black',
|
||||
}
|
||||
color = color_map.get(status, 'black')
|
||||
return format_html(
|
||||
'<span style="color: {}; font-weight: bold;">{}</span>',
|
||||
color, status
|
||||
)
|
||||
|
||||
@admin.display(description='紧急程度')
|
||||
def urgency_display(self, obj):
|
||||
if obj.urgency_level == '加急':
|
||||
return format_html(
|
||||
'<span style="color: red; font-weight: bold;">⚠️ 加急</span>'
|
||||
)
|
||||
return obj.urgency_level
|
||||
|
||||
@admin.display(description='进度')
|
||||
def progress_display(self, obj):
|
||||
progress = obj.progress_percentage
|
||||
if progress >= 100:
|
||||
color = 'green'
|
||||
elif progress >= 50:
|
||||
color = 'orange'
|
||||
else:
|
||||
color = 'gray'
|
||||
return format_html(
|
||||
'<span style="color: {}; font-weight: bold;">{:.1f}%</span>',
|
||||
color, progress
|
||||
)
|
||||
|
||||
@admin.display(description='当前状态')
|
||||
def status_display(self, obj):
|
||||
return obj.status
|
||||
|
||||
@action(description='推进到下一个状态')
|
||||
def advance_to_next_state_action(self, request, queryset):
|
||||
from stateflow import services
|
||||
success_count = 0
|
||||
messages = []
|
||||
|
||||
for obj in queryset:
|
||||
if not obj.business_object:
|
||||
messages.append(f"{obj.plate_code}: 没有关联的流程实例,无法推进")
|
||||
continue
|
||||
|
||||
success, message = services.advance_to_next_state(obj.business_object, request.user)
|
||||
if success:
|
||||
success_count += 1
|
||||
messages.append(f"{obj.plate_code}: {message}")
|
||||
|
||||
self.message_user(request, f'成功推进 {success_count}/{queryset.count()} 个开版订单。详情: {"; ".join(messages[:5])}')
|
||||
|
||||
@action(description='回退一步')
|
||||
def step_back_one_state_action(self, request, queryset):
|
||||
from stateflow import services
|
||||
success_count = 0
|
||||
messages = []
|
||||
|
||||
for obj in queryset:
|
||||
if not obj.business_object:
|
||||
messages.append(f"{obj.plate_code}: 没有关联的流程实例,无法回退")
|
||||
continue
|
||||
|
||||
success, message = services.step_back_one_state(obj.business_object, request.user)
|
||||
if success:
|
||||
success_count += 1
|
||||
messages.append(f"{obj.plate_code}: {message}")
|
||||
|
||||
self.message_user(request, f'成功回退 {success_count}/{queryset.count()} 个开版订单。详情: {"; ".join(messages[:5])}')
|
||||
|
||||
@action(description='重置进度')
|
||||
def reset_progress_action(self, request, queryset):
|
||||
from stateflow import services
|
||||
reset_count = 0
|
||||
|
||||
for obj in queryset:
|
||||
if not obj.business_object:
|
||||
continue
|
||||
|
||||
services.reset_business_object_progress(obj.business_object)
|
||||
reset_count += 1
|
||||
|
||||
self.message_user(request, f'已重置 {reset_count} 个开版订单的进度。')
|
||||
|
||||
|
||||
class PrintingJobInlineFormSet(BaseInlineFormSet):
|
||||
"""自定义 formset 处理 PrintingJob 的创建"""
|
||||
|
||||
def save_new(self, form, commit=True):
|
||||
"""保存新的 PrintingJob(暂时不创建 BusinessObject,等 order 保存完成后再创建)"""
|
||||
obj = super().save_new(form, commit=commit)
|
||||
return obj
|
||||
|
||||
|
||||
class PrintingJobInline(admin.StackedInline):
|
||||
model = models.PrintingJob
|
||||
formset = PrintingJobInlineFormSet
|
||||
extra = 1
|
||||
|
||||
|
||||
@@ -12,11 +202,93 @@ class PrintingOrderAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'oid',
|
||||
'customer_name',
|
||||
'progress_percent',
|
||||
'created_at',
|
||||
'outgoing_date',
|
||||
)
|
||||
inlines = [PrintingJobInline]
|
||||
|
||||
def save_formset(self, request, form, formset, change):
|
||||
"""保存 formset 后,为新创建的 PrintingJob 创建 BusinessObject"""
|
||||
# 先调用默认的保存
|
||||
formset.save()
|
||||
|
||||
# 此时 PrintingOrder 已经保存完成(包括 process 字段)
|
||||
# 为所有新创建且没有 business_object 的 job 创建 BusinessObject
|
||||
if isinstance(formset.model, type) and issubclass(formset.model, models.PrintingJob):
|
||||
order = form.instance
|
||||
for job in order.printing_jobs.all():
|
||||
if not job.business_object and order.process:
|
||||
from stateflow.models import BusinessObject
|
||||
business_object = BusinessObject.objects.create(
|
||||
name=f"PrintingJob-{job.id}",
|
||||
process=order.process,
|
||||
description=f"印染任务 {job.id} 的流程实例",
|
||||
)
|
||||
job.business_object = business_object
|
||||
job.save(update_fields=['business_object'])
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
"""保存 PrintingOrder 时使用 service 层"""
|
||||
from api_v1.views.printing.services import PrintingOrderService
|
||||
|
||||
if not change: # 新创建
|
||||
# 使用 service 创建(会自动设置默认 process)
|
||||
data = {
|
||||
'customer': obj.customer,
|
||||
'fabric': obj.fabric,
|
||||
'width': obj.width,
|
||||
'is_urgent': obj.is_urgent,
|
||||
'area': obj.area,
|
||||
'address': obj.address,
|
||||
'fabric_source': obj.fabric_source,
|
||||
'is_fabric_received': obj.is_fabric_received,
|
||||
'craft': obj.craft,
|
||||
'description': obj.description,
|
||||
'outgoing_date': obj.outgoing_date,
|
||||
'curve': obj.curve,
|
||||
'new_curve': obj.new_curve,
|
||||
'position': obj.position,
|
||||
'printing_warn': obj.printing_warn,
|
||||
'rolling_warn': obj.rolling_warn,
|
||||
'production_warn': obj.production_warn,
|
||||
'is_invalid': obj.is_invalid,
|
||||
'process': obj.process,
|
||||
}
|
||||
created_obj = PrintingOrderService.create_printing_order(data, request.user)
|
||||
# 将创建的对象的所有字段复制到当前对象
|
||||
for field in created_obj._meta.fields:
|
||||
setattr(obj, field.name, getattr(created_obj, field.name))
|
||||
else: # 更新
|
||||
# 使用 service 更新
|
||||
data = {
|
||||
'customer': obj.customer,
|
||||
'fabric': obj.fabric,
|
||||
'width': obj.width,
|
||||
'is_urgent': obj.is_urgent,
|
||||
'area': obj.area,
|
||||
'address': obj.address,
|
||||
'fabric_source': obj.fabric_source,
|
||||
'is_fabric_received': obj.is_fabric_received,
|
||||
'craft': obj.craft,
|
||||
'description': obj.description,
|
||||
'outgoing_date': obj.outgoing_date,
|
||||
'curve': obj.curve,
|
||||
'new_curve': obj.new_curve,
|
||||
'position': obj.position,
|
||||
'printing_warn': obj.printing_warn,
|
||||
'rolling_warn': obj.rolling_warn,
|
||||
'production_warn': obj.production_warn,
|
||||
'is_invalid': obj.is_invalid,
|
||||
'process': obj.process,
|
||||
}
|
||||
success, message, updated_obj = PrintingOrderService.update_printing_order(
|
||||
obj, data, request.user
|
||||
)
|
||||
if not success:
|
||||
from django.core.exceptions import ValidationError
|
||||
raise ValidationError(message)
|
||||
|
||||
@admin.display(description='客户名称')
|
||||
def customer_name(self, obj: models.PrintingOrder):
|
||||
return obj.customer.name
|
||||
@@ -24,3 +296,140 @@ class PrintingOrderAdmin(admin.ModelAdmin):
|
||||
@admin.display(description='订单编号(虚拟)')
|
||||
def oid(self, obj: models.PrintingOrder):
|
||||
return obj.human_id
|
||||
|
||||
@admin.display(description='进度')
|
||||
def progress_percent(self, obj: models.PrintingOrder):
|
||||
return f"{obj.progress}%"
|
||||
|
||||
|
||||
@admin.register(models.PrintingJob)
|
||||
class PrintingJobAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'id',
|
||||
'printing_order',
|
||||
'customer_name',
|
||||
'product',
|
||||
'quantity',
|
||||
'unit',
|
||||
'size',
|
||||
'pieces',
|
||||
'business_object',
|
||||
'current_progress',
|
||||
'execution_status',
|
||||
'created_at',
|
||||
)
|
||||
search_fields = ('printing_order__human_id', 'product__name')
|
||||
list_filter = ('created_at', 'printing_order__customer__name')
|
||||
actions = ['advance_to_next_state_action', 'step_back_one_state_action', 'reset_progress_action']
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
"""保存 PrintingJob 时使用 service 层"""
|
||||
from api_v1.views.printing.services import PrintingJobService
|
||||
|
||||
if not change: # 新创建
|
||||
# 使用 service 创建(这样会自动创建 BusinessObject)
|
||||
data = {
|
||||
'printing_order': obj.printing_order,
|
||||
'product': obj.product,
|
||||
'quantity': obj.quantity,
|
||||
'unit': obj.unit,
|
||||
'size': obj.size,
|
||||
'pieces': obj.pieces,
|
||||
'description': obj.description,
|
||||
}
|
||||
created_obj = PrintingJobService.create_printing_job(data, request.user)
|
||||
# 将创建的对象的 ID 赋值给当前对象(这样 admin 可以正确跳转)
|
||||
obj.pk = created_obj.pk
|
||||
obj.id = created_obj.id
|
||||
else: # 更新
|
||||
# 使用 service 更新
|
||||
data = {
|
||||
'printing_order': obj.printing_order,
|
||||
'product': obj.product,
|
||||
'quantity': obj.quantity,
|
||||
'unit': obj.unit,
|
||||
'size': obj.size,
|
||||
'pieces': obj.pieces,
|
||||
'description': obj.description,
|
||||
}
|
||||
success, message, updated_obj = PrintingJobService.update_printing_job(
|
||||
obj, data, request.user
|
||||
)
|
||||
if not success:
|
||||
from django.core.exceptions import ValidationError
|
||||
raise ValidationError(message)
|
||||
|
||||
@admin.display(description='客户名称')
|
||||
def customer_name(self, obj: models.PrintingJob):
|
||||
return obj.printing_order.customer.name
|
||||
|
||||
@admin.display(description='当前进度')
|
||||
def current_progress(self, obj: models.PrintingJob):
|
||||
"""显示该 job 自己的进度(已完成节点数 / 总节点数)"""
|
||||
if not obj.business_object or not obj.printing_order.process:
|
||||
return "0%"
|
||||
|
||||
total_nodes = obj.business_object.process.process_nodes.count()
|
||||
if total_nodes == 0:
|
||||
return "0%"
|
||||
|
||||
completed_nodes = obj.business_object.state_logs.filter(is_cancelled=False).count()
|
||||
progress = int((completed_nodes / total_nodes) * 100)
|
||||
return f"{progress}%"
|
||||
|
||||
@admin.display(description='执行状态')
|
||||
def execution_status(self, obj: models.PrintingJob):
|
||||
return obj.status
|
||||
|
||||
@action(description='前进到下一个状态')
|
||||
def advance_to_next_state_action(self, request, queryset):
|
||||
from stateflow import services
|
||||
success_count = 0
|
||||
messages = []
|
||||
|
||||
for obj in queryset:
|
||||
# 检查是否有 business_object
|
||||
if not obj.business_object:
|
||||
messages.append(f"PrintingJob #{obj.id}: 没有关联的业务对象,无法推进")
|
||||
continue
|
||||
|
||||
success, message = services.advance_to_next_state(obj.business_object, request.user)
|
||||
if success:
|
||||
success_count += 1
|
||||
messages.append(f"PrintingJob #{obj.id}: {message}")
|
||||
|
||||
self.message_user(request, f'成功推进 {success_count}/{queryset.count()} 个印染任务。详情: {"; ".join(messages[:5])}')
|
||||
|
||||
@action(description='回退一步')
|
||||
def step_back_one_state_action(self, request, queryset):
|
||||
from stateflow import services
|
||||
success_count = 0
|
||||
messages = []
|
||||
|
||||
for obj in queryset:
|
||||
# 检查是否有 business_object
|
||||
if not obj.business_object:
|
||||
messages.append(f"PrintingJob #{obj.id}: 没有关联的业务对象,无法回退")
|
||||
continue
|
||||
|
||||
success, message = services.step_back_one_state(obj.business_object, request.user)
|
||||
if success:
|
||||
success_count += 1
|
||||
messages.append(f"PrintingJob #{obj.id}: {message}")
|
||||
|
||||
self.message_user(request, f'成功回退 {success_count}/{queryset.count()} 个印染任务。详情: {"; ".join(messages[:5])}')
|
||||
|
||||
@action(description='重置进度')
|
||||
def reset_progress_action(self, request, queryset):
|
||||
from stateflow import services
|
||||
reset_count = 0
|
||||
|
||||
for obj in queryset:
|
||||
# 检查是否有 business_object
|
||||
if not obj.business_object:
|
||||
continue
|
||||
|
||||
services.reset_business_object_progress(obj.business_object)
|
||||
reset_count += 1
|
||||
|
||||
self.message_user(request, f'已重置 {reset_count} 个印染任务的进度。')
|
||||
|
||||
Reference in New Issue
Block a user