# -*- 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')]), }