This commit is contained in:
huziyang@jikimo.com
2025-07-18 16:18:09 +08:00
parent fe8df494f9
commit 22f36d095c
11 changed files with 717 additions and 2 deletions

View File

@@ -0,0 +1,251 @@
# -*- coding: utf-8 -*-
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
class SfTechnologyDesignTask(models.Model):
_name = 'sf.technology.design.task'
_description = '工艺设计任务'
_order = 'create_date desc'
_inherit = ['mail.thread', 'mail.activity.mixin']
# 基本信息
name = fields.Char('任务编号', required=True, copy=False, readonly=True,
default=lambda self: _('New'))
state = fields.Selection([
('pending', '待工艺设计'),
('in_progress', '进行中'),
('completed', '已完成'),
('cancelled', '已取消'),
], string='状态', default='pending', tracking=True)
# 关联需求计划
demand_plan_id = fields.Many2one('sf.production.demand.plan', string='需求计划', required=True, ondelete='cascade')
# 销售订单信息
sale_order_id = fields.Many2one('sale.order', string='销售订单', compute='_compute_sale_order_info', store=True)
sale_order_line_id = fields.Many2one('sale.order.line', string='销售订单明细', compute='_compute_sale_order_info', store=True)
customer_name = fields.Char('客户名称', compute='_compute_sale_order_info', store=True)
# 产品信息
product_id = fields.Many2one('product.product', string='产品名称', compute='_compute_product_info', store=True)
part_name = fields.Char('零件名称', compute='_compute_product_info', store=True)
part_number = fields.Char('零件图号', compute='_compute_product_info', store=True)
model_id = fields.Char('模型ID', compute='_compute_product_info', store=True)
# 产品类型和文件
product_type = fields.Selection([
('standard', '标准件'),
('custom', '定制件'),
('prototype', '样件'),
], string='零件类型', default='custom', store=True)
model_file = fields.Binary('模型文件', compute='_compute_model_info', store=True)
model_filename = fields.Char('模型文件名', compute='_compute_model_info', store=True)
# 材料信息
materials_id = fields.Many2one('sf.production.materials', string='材料及型号', compute='_compute_material_info', store=True)
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类', compute='_compute_material_info', store=True)
blank_precision = fields.Selection([('精坯', '精坯'), ('粗坯', '粗坯')], string='坯料类型', compute='_compute_material_info', store=True)
embryo_long = fields.Char('坯料尺寸', compute='_compute_embryo_long', store=True)
# 加工信息
machining_precision = fields.Selection([
('0.10', '±0.10mm'),
('0.05', '±0.05mm'),
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')
], string='加工精度', compute='_compute_machining_info', store=True)
machining_panel = fields.Char('加工面', compute='_compute_machining_info', store=True)
# 订单信息
product_uom_qty = fields.Float('订单数量', compute='_compute_order_info', store=True)
is_incoming_material = fields.Boolean('客供料', compute='_compute_order_info', store=True)
# 工艺参数
clamping_times = fields.Integer('装夹次数', default=1)
single_clamping_duration = fields.Float('单次装夹时长(分钟)', default=30.0)
cnc_processing_duration = fields.Float('CNC加工时长(分钟)', default=0.0)
# 质量要求
customer_quality_requirements = fields.Text('客户质量要求')
processing_drawing_2d = fields.Binary('2D加工图纸')
processing_drawing_filename = fields.Char('2D图纸文件名')
# 时间信息
create_date = fields.Datetime('创建时间', default=fields.Datetime.now)
start_date = fields.Datetime('开始时间')
complete_date = fields.Datetime('完成时间')
deadline = fields.Datetime('截止时间')
# 负责人
assigned_to = fields.Many2one('res.users', string='负责人', tracking=True)
created_by = fields.Many2one('res.users', string='创建人', default=lambda self: self.env.user, readonly=True)
# 备注
notes = fields.Text('备注')
@api.depends('demand_plan_id')
def _compute_sale_order_info(self):
for record in self:
if record.demand_plan_id:
record.sale_order_id = record.demand_plan_id.sale_order_id
record.sale_order_line_id = record.demand_plan_id.sale_order_line_id
record.customer_name = record.demand_plan_id.customer_name
else:
record.sale_order_id = False
record.sale_order_line_id = False
record.customer_name = ""
@api.depends('demand_plan_id')
def _compute_product_info(self):
for record in self:
if record.demand_plan_id and record.demand_plan_id.product_id:
record.product_id = record.demand_plan_id.product_id
if record.product_id.product_tmpl_id:
record.part_name = record.product_id.product_tmpl_id.part_name
record.part_number = record.product_id.product_tmpl_id.part_number
record.model_id = record.product_id.product_tmpl_id.model_id
else:
record.part_name = ""
record.part_number = ""
record.model_id = ""
else:
record.product_id = False
record.part_name = ""
record.part_number = ""
record.model_id = ""
@api.depends('product_id')
def _compute_model_info(self):
for record in self:
if record.product_id and record.product_id.product_tmpl_id:
template = record.product_id.product_tmpl_id
record.model_file = template.model_file
record.model_filename = template.model_name
else:
record.model_file = False
record.model_filename = ""
@api.depends('product_id')
def _compute_material_info(self):
for record in self:
if record.product_id and record.product_id.product_tmpl_id:
template = record.product_id.product_tmpl_id
record.materials_id = template.materials_id
record.blank_type = template.blank_type
record.blank_precision = template.blank_precision
else:
record.materials_id = False
record.blank_type = False
record.blank_precision = False
@api.depends('product_id')
def _compute_machining_info(self):
for record in self:
if record.product_id and record.product_id.product_tmpl_id:
template = record.product_id.product_tmpl_id
record.machining_precision = template.model_machining_precision
record.machining_panel = template.model_processing_panel
else:
record.machining_precision = False
record.machining_panel = ""
@api.depends('demand_plan_id')
def _compute_order_info(self):
for record in self:
if record.demand_plan_id:
record.product_uom_qty = record.demand_plan_id.product_uom_qty
record.is_incoming_material = record.demand_plan_id.is_incoming_material
else:
record.product_uom_qty = 0.0
record.is_incoming_material = False
@api.depends('product_id')
def _compute_embryo_long(self):
for record in self:
if record.product_id and record.product_id.product_tmpl_id:
template = record.product_id.product_tmpl_id
record.embryo_long = f"{template.model_long}×{template.model_width}×{template.model_height}"
else:
record.embryo_long = ""
@api.model
def create(self, vals):
if vals.get('name', _('New')) == _('New'):
vals['name'] = self.env['ir.sequence'].next_by_code('sf.technology.design.task') or _('New')
return super(SfTechnologyDesignTask, self).create(vals)
def action_start_design(self):
"""开始工艺设计"""
self.ensure_one()
if self.state != 'pending':
raise ValidationError(_('只有待工艺设计的任务才能开始设计'))
self.write({
'state': 'in_progress',
'start_date': fields.Datetime.now(),
})
self.message_post(body=_('工艺设计任务已开始'))
def action_complete_design(self):
"""完成工艺设计"""
self.ensure_one()
if self.state != 'in_progress':
raise ValidationError(_('只有进行中的任务才能完成'))
self.write({
'state': 'completed',
'complete_date': fields.Datetime.now(),
})
self.message_post(body=_('工艺设计任务已完成'))
def action_cancel_task(self):
"""取消任务"""
self.ensure_one()
if self.state in ['completed']:
raise ValidationError(_('已完成的任务不能取消'))
self.write({
'state': 'cancelled',
})
self.message_post(body=_('工艺设计任务已取消'))
def action_reset_to_pending(self):
"""重置为待工艺设计"""
self.ensure_one()
if self.state not in ['in_progress', 'cancelled']:
raise ValidationError(_('只有进行中或已取消的任务才能重置'))
self.write({
'state': 'pending',
'start_date': False,
'complete_date': False,
})
self.message_post(body=_('工艺设计任务已重置为待设计状态'))
@api.model
def create_from_demand_plan(self, demand_plan_ids):
"""从需求计划创建工艺设计任务"""
tasks = self.env['sf.technology.design.task']
for demand_plan in demand_plan_ids:
# 检查是否已存在工艺设计任务
existing_task = self.search([
('demand_plan_id', '=', demand_plan.id),
('state', 'not in', ['cancelled'])
], limit=1)
if not existing_task:
task_vals = {
'demand_plan_id': demand_plan.id,
'deadline': fields.Datetime.now() + fields.timedelta(days=3), # 默认3天期限
}
task = self.create(task_vals)
tasks |= task
return tasks
def get_task_count_by_state(self):
"""获取各状态的任务数量"""
return {
'pending': self.search_count([('state', '=', 'pending')]),
'in_progress': self.search_count([('state', '=', 'in_progress')]),
'completed': self.search_count([('state', '=', 'completed')]),
'cancelled': self.search_count([('state', '=', 'cancelled')]),
}