diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml index fbd917b2..8e5af75a 100644 --- a/quality_control/views/quality_views.xml +++ b/quality_control/views/quality_views.xml @@ -203,7 +203,7 @@ Quality Alerts quality.alert - kanban,tree,form,pivot,graph,calendar + tree,kanban,form,pivot,graph,calendar

Create a new quality alert diff --git a/sf_base/models/common.py b/sf_base/models/common.py index 95572631..257eb2d4 100644 --- a/sf_base/models/common.py +++ b/sf_base/models/common.py @@ -100,6 +100,7 @@ class MrsProductionProcess(models.Model): travel_day = fields.Float('路途天数/d') sequence = fields.Integer('排序') + # class MrsProcessingTechnology(models.Model): # _name = 'sf.processing.technology' # _description = '加工工艺' @@ -157,7 +158,7 @@ class MrsProductionProcessParameter(models.Model): for parameter in self: if parameter.process_id: name = parameter.process_id.name + '-' + parameter.name - result.append((parameter.id, name)) + result.append((parameter.id, name)) return result # 获取表面工艺的获取方式 diff --git a/sf_base/static/src/scss/format_img.scss b/sf_base/static/src/scss/format_img.scss index 982d3c50..fc6f02dc 100644 --- a/sf_base/static/src/scss/format_img.scss +++ b/sf_base/static/src/scss/format_img.scss @@ -14,6 +14,7 @@ .img-fluid { max-width: unset !important; + width: 40px; } .o_inner_group .img-fluid { diff --git a/sf_dlm_management/views/product_template_management_view.xml b/sf_dlm_management/views/product_template_management_view.xml index f335c1b5..cae895e3 100644 --- a/sf_dlm_management/views/product_template_management_view.xml +++ b/sf_dlm_management/views/product_template_management_view.xml @@ -2,6 +2,7 @@ + tree,kanban,form,activity {"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'} diff --git a/sf_maintenance/models/sf_maintenance.py b/sf_maintenance/models/sf_maintenance.py index 3d68bb9a..de6be8da 100644 --- a/sf_maintenance/models/sf_maintenance.py +++ b/sf_maintenance/models/sf_maintenance.py @@ -689,6 +689,8 @@ class SfMaintenanceEquipment(models.Model): if next_date < date_now: next_date = date_now else: + if not equipment.initial_action_date: + raise ValidationError('重置保养日期不能为空!!!') next_date = equipment.initial_action_date + timedelta(days=equipment.period) equipment.next_action_date = next_date else: @@ -735,6 +737,8 @@ class SfMaintenanceEquipment(models.Model): if next_date < date_now: next_date = date_now else: + if not equipment.initial_overhaul_date: + raise ValidationError('重置维修日期不能为空') next_date = equipment.initial_overhaul_date + timedelta(days=equipment.overhaul_period) equipment.overhaul_date = next_date else: diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 5106dcb1..3320f943 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -21,6 +21,7 @@ 'wizard/workpiece_delivery_views.xml', 'wizard/rework_wizard_views.xml', 'wizard/production_wizard_views.xml', + 'wizard/production_technology_wizard_views.xml', 'views/mrp_views_menus.xml', 'views/agv_scheduling_views.xml', 'views/stock_lot_views.xml', diff --git a/sf_manufacturing/models/__init__.py b/sf_manufacturing/models/__init__.py index 7d6aa8ae..b0c295f5 100644 --- a/sf_manufacturing/models/__init__.py +++ b/sf_manufacturing/models/__init__.py @@ -11,3 +11,5 @@ from . import production_line_base from . import agv_setting from . import agv_scheduling from . import res_config_setting +from . import sf_technology_design +from . import sf_production_common diff --git a/sf_manufacturing/models/model_type.py b/sf_manufacturing/models/model_type.py index 10157f63..27ebfb03 100644 --- a/sf_manufacturing/models/model_type.py +++ b/sf_manufacturing/models/model_type.py @@ -24,24 +24,7 @@ class ProductModelTypeRoutingSort(models.Model): route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['装夹预调', 'CNC加工', '解除装夹'])]) is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat') - - # routing_type = fields.Selection([ - # ('获取CNC加工程序', '获取CNC加工程序'), - # ('装夹', '装夹'), - # ('前置三元定位检测', '前置三元定位检测'), - # ('CNC加工', 'CNC加工'), - # ('后置三元质量检测', '后置三元质量检测'), - # ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺') - # ], string="工序类型", compute='_compute_route_workcenter_id') - # - # @api.depends('route_workcenter_id') - # def _compute_route_workcenter_id(self): - # for record in self: - # if record: - # record.routing_type = record.route_workcenter_id.routing_type - routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type') - workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids') product_model_type_id = fields.Many2one('sf.model.type') @@ -57,24 +40,7 @@ class EmbryoModelTypeRoutingSort(models.Model): sequence = fields.Integer('Sequence') route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['切割'])]) is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat') - - # routing_type = fields.Selection([ - # ('获取CNC加工程序', '获取CNC加工程序'), - # ('装夹', '装夹'), - # ('前置三元定位检测', '前置三元定位检测'), - # ('CNC加工', 'CNC加工'), - # ('后置三元质量检测', '后置三元质量检测'), - # ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺') - # ], string="工序类型", compute='_compute_route_workcenter_id') - # - # @api.depends('route_workcenter_id') - # def _compute_route_workcenter_id(self): - # for record in self: - # if record: - # record.routing_type = record.route_workcenter_id.routing_type - routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type') - workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids') embryo_model_type_id = fields.Many2one('sf.model.type') @@ -90,24 +56,7 @@ class SurfaceTechnicsModelTypeRoutingSort(models.Model): sequence = fields.Integer('Sequence') route_workcenter_id = fields.Many2one('mrp.routing.workcenter', domain=[('routing_type', 'in', ['表面工艺'])]) is_repeat = fields.Boolean('重复', related='route_workcenter_id.is_repeat') - - # routing_type = fields.Selection([ - # ('获取CNC加工程序', '获取CNC加工程序'), - # ('装夹', '装夹'), - # ('前置三元定位检测', '前置三元定位检测'), - # ('CNC加工', 'CNC加工'), - # ('后置三元质量检测', '后置三元质量检测'), - # ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺') - # ], string="工序类型", compute='_compute_route_workcenter_id') - # - # @api.depends('route_workcenter_id') - # def _compute_route_workcenter_id(self): - # for record in self: - # if record: - # record.routing_type = record.route_workcenter_id.routing_type - routing_type = fields.Selection(string="工序类型", related='route_workcenter_id.routing_type') - workcenter_ids = fields.Many2many('mrp.workcenter', required=False, related='route_workcenter_id.workcenter_ids') surface_technics_model_type_id = fields.Many2one('sf.model.type') diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index c1936e6d..fa81837f 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -98,6 +98,7 @@ class MrpProduction(models.Model): # ]) state = fields.Selection([ ('draft', '草稿'), + ('technology_to_confirmed', '待工艺确认'), ('confirmed', '待排程'), ('pending_cam', '待加工'), ('progress', '加工中'), @@ -160,6 +161,7 @@ class MrpProduction(models.Model): is_remanufacture = fields.Boolean('是否重新制造', default=False) remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids') remanufacture_production_id = fields.Many2one('mrp.production', string='') + technology_design_ids = fields.One2many('sf.technology.design', 'production_id', string='工艺设计') @api.depends('remanufacture_production_id') def _compute_remanufacture_production_ids(self): @@ -216,8 +218,9 @@ class MrpProduction(models.Model): precision_rounding=move.product_uom.rounding or move.product_id.uom_id.rounding) for move in production.move_raw_ids if move.product_id): production.state = 'progress' - - # # 新添加的状态逻辑 + elif not production.technology_design_ids: + production.state = 'technology_to_confirmed' + # 新添加的状态逻辑 if ( production.state == 'to_close' or production.state == 'progress') and production.schedule_state == '未排': production.state = 'confirmed' @@ -254,6 +257,59 @@ class MrpProduction(models.Model): if production.tool_state == '2': production.state = 'rework' + # 工艺确认 + def technology_confirm(self): + process_parameters = [] + special_design = self.technology_design_ids.filtered( + lambda a: a.routing_tag == 'special' and a.process_parameters_id is not False) + for special in special_design: + if special.process_parameters_id: + product_production_process = self.env['product.template'].search( + [('server_product_process_parameters_id', '=', special.process_parameters_id.id)]) + if not product_production_process: + if special.process_parameters_id not in process_parameters: + process_parameters.append(special.process_parameters_id.display_name) + if process_parameters: + raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters))) + # 判断同一个加工面的标准工序的顺序是否依次排序 + error_panel = [] + technology_design = self.technology_design_ids.filtered(lambda a: a.routing_tag == 'standard').sorted( + key=lambda m: m.sequence) + for index, design in enumerate(technology_design): + routing_type = design.route_id.routing_type + if index < len(technology_design) - 1: + next_index = index + 1 + next_design = technology_design[next_index] + next_design_routing_type = next_design.route_id.routing_type + logging.info('当前工序和加工面: %s-%s' % (design.route_id.name, design.panel)) + logging.info('下一个工序和加工面: %s-%s' % (next_design.route_id.name, next_design.panel)) + if design.panel is not False: + if design.panel != next_design.panel: + if index == 0: + raise UserError('【加工面】为%s的标准工序里含有其他加工面的工序,请调整后重试' % design.panel) + if routing_type not in ['解除装夹']: + raise UserError('【加工面】为%s的标准工序顺序有误,请调整后重试' % design.panel) + if design.panel == next_design.panel: + if (routing_type == '装夹预调' and next_design_routing_type == '解除装夹') or ( + routing_type == 'CNC加工' and next_design_routing_type == '装夹预调'): + if design.panel not in error_panel: + error_panel.append(design.panel) + else: + if not error_panel and not process_parameters: + return { + 'name': _('工艺确认'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'sf.production.technology.wizard', + 'target': 'new', + 'context': { + 'default_production_id': self.id, + 'default_origin': self.origin, + }} + if error_panel: + raise UserError(_("【加工面】为%s的标准工序顺序有误,请调整后重试", ", ".join(error_panel))) + return True + def action_check(self): """ 审核启用 @@ -492,73 +548,33 @@ class MrpProduction(models.Model): 'state': 'pending', }] if production.product_id.categ_id.type == '成品': - # # 根据加工面板的面数及对应的工序模板生成工单 - i = 0 - processing_panel_len = len(production.product_id.model_processing_panel.split(',')) - for k in (production.product_id.model_processing_panel.split(',')): - product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search( - [('product_model_type_id', '=', production.product_id.product_model_type_id.id)], - order='sequence asc' - ) - i += 1 - for route in product_routing_workcenter: - if route.is_repeat is True: - workorders_values.append( - self.env['mrp.workorder'].json_workorder_str(k, production, route, item)) - # if i == processing_panel_len and route.routing_type == '解除装夹': - # workorders_values.append( - # self.env['mrp.workorder'].json_workorder_str(k, production, route)) - # 表面工艺工序 - # 获取表面工艺id - # 工序id - surface_technics_arr = [] - route_workcenter_arr = [] - for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids: - if item.route_workcenter_id.surface_technics_id.id: - for process_param in production.product_id.model_process_parameters_ids: - logging.info('process_param:%s%s' % (process_param.id, process_param.name)) - if item.route_workcenter_id.surface_technics_id == process_param.process_id: - logging.info( - 'surface_technics_id:%s%s' % (item.route_workcenter_id.surface_technics_id.id, - item.route_workcenter_id.surface_technics_id.name)) - surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id) - route_workcenter_arr.append(item.route_workcenter_id.id) - if surface_technics_arr: - production_process = self.env['sf.production.process'].search( - [('id', 'in', surface_technics_arr)], - order='sequence asc' - ) - for p in production_process: - logging.info('production_process:%s' % p.name) - # if production_process: - process_parameter = production.product_id.model_process_parameters_ids.filtered( - lambda pm: pm.process_id.id == p.id) - if process_parameter: - # 产品为表面工艺服务的供应商 - product_production_process = self.env['product.template'].search( - [('server_product_process_parameters_id', '=', process_parameter.id)]) - if product_production_process: - route_production_process = self.env[ - 'mrp.routing.workcenter'].search( - [('surface_technics_id', '=', p.id), - ('id', 'in', route_workcenter_arr)]) - if route_production_process: - workorders_values.append( - self.env[ - 'mrp.workorder']._json_workorder_surface_process_str( - production, route_production_process, - process_parameter, - product_production_process.seller_ids[0].partner_id.id)) + # # 根据工序设计生成工单 + for route in item.technology_design_ids: + if route.route_id.routing_type not in ['表面工艺']: + workorders_values.append( + self.env['mrp.workorder'].json_workorder_str(production, route)) + else: + product_production_process = self.env['product.template'].search( + [('server_product_process_parameters_id', '=', route.process_parameters_id.id)]) + # if product_production_process: + # route_production_process = self.env[ + # 'mrp.routing.workcenter'].search( + # [('surface_technics_id', '=', p.id), + # ('id', 'in', route_workcenter_arr)]) + # if route_production_process: + workorders_values.append( + self.env[ + 'mrp.workorder']._json_workorder_surface_process_str( + production, route, product_production_process.seller_ids[0].partner_id.id)) elif production.product_id.categ_id.type == '坯料': embryo_routing_workcenter = self.env['sf.embryo.model.type.routing.sort'].search( [('embryo_model_type_id', '=', production.product_id.embryo_model_type_id.id)], order='sequence asc' ) - for route in embryo_routing_workcenter: + for route_embryo in embryo_routing_workcenter: workorders_values.append( - self.env['mrp.workorder'].json_workorder_str('', production, route)) + self.env['mrp.workorder'].json_workorder_str('', production, route_embryo)) production.workorder_ids = workorders_values - # for production_item in productions: process_parameter_workorder = self.env['mrp.workorder'].search( [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), ('is_subcontract', '=', True)]) @@ -840,6 +856,7 @@ class MrpProduction(models.Model): backorders = backorders - productions_to_backorder productions_not_to_backorder._post_inventory(cancel_backorder=True) + # 查出最后一张工单完成入库操作 # if self.workorder_ids.filtered(lambda w: w.routing_type in ['表面工艺']): # move_finish = self.env['stock.move'].search([('created_production_id', '=', self.id)]) # if move_finish: @@ -1218,10 +1235,6 @@ class sf_detection_result(models.Model): 'type': 'ir.actions.act_window', 'res_id': self.id, 'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')], - # 'view_mode': 'form', - # 'context': { - # 'default_id': self.id - # }, 'target': 'new' } diff --git a/sf_manufacturing/models/mrp_routing_workcenter.py b/sf_manufacturing/models/mrp_routing_workcenter.py index a584379f..2916bdce 100644 --- a/sf_manufacturing/models/mrp_routing_workcenter.py +++ b/sf_manufacturing/models/mrp_routing_workcenter.py @@ -7,21 +7,23 @@ class ResMrpRoutingWorkcenter(models.Model): _inherit = 'mrp.routing.workcenter' routing_type = fields.Selection([ - # ('获取CNC加工程序', '获取CNC加工程序'), ('装夹预调', '装夹预调'), - # ('前置三元定位检测', '前置三元定位检测'), ('CNC加工', 'CNC加工'), - # ('后置三元质量检测', '后置三元质量检测'), ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺') ], string="工序类型") + routing_tag = fields.Selection([ + ('standard', '标准'), + ('special', '特殊') + ], string="标签") is_repeat = fields.Boolean('重复', default=False) workcenter_id = fields.Many2one('mrp.workcenter', required=False) workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True) bom_id = fields.Many2one('mrp.bom', required=False) surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺") reserved_duration = fields.Float('预留时长', default=30, tracking=True) + def get_no(self): international_standards = self.search( [('code', '!=', ''), ('active', 'in', [True, False])], @@ -78,3 +80,13 @@ class ResMrpRoutingWorkcenter(models.Model): else: workcenter_id = workcenter_ids[0] return workcenter_id + + @api.model + def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): + if self._context.get('production_id'): + technology_design = self.env['sf.technology.design'].search( + [('production_id', '=', self._context.get('production_id'))]) + route_ids = [t.route_id.id for t in technology_design] + domain = [('id', 'not in', route_ids)] + return self._search(domain, limit=limit, access_rights_uid=name_get_uid) + return super()._name_search(name, args, operator, limit, name_get_uid) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 3125cb4b..9bfc82e1 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -670,35 +670,14 @@ class ResMrpWorkOrder(models.Model): }} # 拼接工单对象属性值 - def json_workorder_str(self, k, production, route, item): + def json_workorder_str(self, production, route): # 计算预计时长duration_expected routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹'] - if route.routing_type in routing_types: + if route.route_id.routing_type in routing_types: routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search( - [('name', '=', route.routing_type)]) + [('name', '=', route.route_id.routing_type)]) duration_expected = routing_workcenter.time_cycle reserved_duration = routing_workcenter.reserved_duration - # if route.routing_type == '切割': - # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # [('name', '=', '切割')]).time_cycle - # # elif route.routing_type == '获取CNC加工程序': - # # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # # [('name', '=', '获取CNC加工程序')]).time_cycle - # elif route.routing_type == '装夹预调': - # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # [('name', '=', '装夹预调')]).time_cycle - # # elif route.routing_type == '前置三元定位检测': - # # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # # [('name', '=', '前置三元定位检测')]).time_cycle - # elif route.routing_type == 'CNC加工': - # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # [('name', '=', 'CNC加工')]).time_cycle - # # elif route.routing_type == '后置三元质量检测': - # # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # # [('name', '=', '后置三元质量检测')]).time_cycle - # elif route.routing_type == '解除装夹': - # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( - # [('name', '=', '解除装夹')]).time_cycle else: duration_expected = 60 reserved_duration = 30 @@ -706,26 +685,19 @@ class ResMrpWorkOrder(models.Model): 'product_uom_id': production.product_uom_id.id, 'qty_producing': 0, 'operation_id': False, - 'name': route.route_workcenter_id.name, - 'processing_panel': k, - 'quality_point_ids': route.route_workcenter_id.quality_point_ids, - 'routing_type': route.routing_type, - # 'work_state': '待发起', - 'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids, - route.routing_type, + 'name': route.route_id.name, + 'processing_panel': route.panel, + 'quality_point_ids': route.route_id.quality_point_ids, + 'routing_type': route.route_id.routing_type, + 'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.route_id.workcenter_ids.ids, + route.route_id.routing_type, production.product_id), # 设定初始化值,避免出现变成bool问题 'date_planned_start': datetime.now(), 'date_planned_finished': datetime.now() + timedelta(days=1), 'duration_expected': duration_expected, 'duration': 0, - 'tag_type': '重新加工' if item is False else False, - 'cnc_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cnc.processing']._json_cnc_processing( - k, item), - 'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k, - item), - # 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list( - # production) + # 'tag_type': '重新加工' if item is False else False, 'reserved_duration': reserved_duration, }] return workorders_values_str @@ -756,22 +728,22 @@ class ResMrpWorkOrder(models.Model): ] # 拼接工单对象属性值(表面工艺) - def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id): + def _json_workorder_surface_process_str(self, production, route, supplier_id): workorders_values_str = [0, '', { 'product_uom_id': production.product_uom_id.id, 'qty_producing': 0, 'operation_id': False, - 'name': '%s-%s' % (route.name, process_parameter.name), + 'name': route.process_parameters_id.display_name, 'processing_panel': '', 'routing_type': '表面工艺', - 'surface_technics_parameters_id': process_parameter.id, + 'surface_technics_parameters_id': route.process_parameters_id.id, 'work_state': '', 'supplier_id': supplier_id, - 'is_subcontract': True if process_parameter.gain_way == '外协' else False, + 'is_subcontract': True if route.process_parameters_id.gain_way == '外协' else False, 'workcenter_id': self.env[ - 'mrp.workcenter'].get_process_outsourcing_workcenter() if process_parameter.gain_way == '外协' else - self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids, - route.routing_type, + 'mrp.workcenter'].get_process_outsourcing_workcenter() if route.process_parameters_id.gain_way == '外协' else + self.env['mrp.routing.workcenter'].get_workcenter(route.route_id.workcenter_ids.ids, + route.route_id.routing_type, production.product_id), 'date_planned_start': datetime.now(), 'date_planned_finished': datetime.now() + timedelta(days=1), @@ -1069,37 +1041,6 @@ class ResMrpWorkOrder(models.Model): workorder.state = 'waiting' continue - # elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress', - # 'rework']: - # per_work = self.env['mrp.workorder'].search( - # [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id), - # ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)]) - # if per_work: - # workorder.state = 'waiting' - # if workorder.routing_type == 'CNC加工' and workorder.state == 'progress': - # workorder.state = 'to be detected' - - # for workorder in self: - # if workorder.is_rework is True and workorder.state == 'done': - # cnc_work = self.env['mrp.workorder'].search([('routing_type','=','CNC加工'),('production_id','=',workorder.production_id.id)]) - # if cnc_work: - # cnc_work.state = 'waiting' - # if workorder.state == 'pending': - # if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): - # workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting' - # continue - # if workorder.state not in ('waiting', 'ready'): - # continue - # if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): - # workorder.state = 'pending' - # continue - # if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'): - # continue - # if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting': - # workorder.state = 'ready' - # elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready': - # workorder.state = 'waiting' - # 重写工单开始按钮方法 def button_start(self): if self.routing_type == 'CNC加工': diff --git a/sf_manufacturing/models/sf_production_common.py b/sf_manufacturing/models/sf_production_common.py new file mode 100644 index 00000000..dc9ff2a3 --- /dev/null +++ b/sf_manufacturing/models/sf_production_common.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +import logging +from odoo import fields, models, api +from odoo.exceptions import UserError + + +class SfProductionProcessParameter(models.Model): + _inherit = 'sf.production.process.parameter' + + @api.model + def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): + if self._context.get('route_id'): + routing = self.env['mrp.routing.workcenter'].search([('id', '=', self._context.get('route_id'))]) + domain = [('process_id', '=', routing.surface_technics_id.id)] + return self._search(domain, limit=limit, access_rights_uid=name_get_uid) + return super()._name_search(name, args, operator, limit, name_get_uid) + diff --git a/sf_manufacturing/models/sf_technology_design.py b/sf_manufacturing/models/sf_technology_design.py new file mode 100644 index 00000000..6aa49f5a --- /dev/null +++ b/sf_manufacturing/models/sf_technology_design.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models + + +class sf_technology_design(models.Model): + _name = 'sf.technology.design' + _description = "工艺设计" + + sequence = fields.Integer('序号') + route_id = fields.Many2one('mrp.routing.workcenter', '工序') + process_parameters_id = fields.Many2one('sf.production.process.parameter', string='表面工艺参数') + panel = fields.Char('加工面') + routing_tag = fields.Selection(related='route_id.routing_tag', string='标签', store=True) + time_cycle_manual = fields.Float(related='route_id.time_cycle_manual', string='预计时长') + production_id = fields.Many2one('mrp.production') + is_auto = fields.Boolean('是否自动生成', default=False) + active = fields.Boolean('有效', default=True) + + def json_technology_design_str(self, k, route, i): + workorders_values_str = [0, '', { + 'route_id': route.id, + 'panel': k, + 'process_parameters_id': False if route.routing_type.id != '表面工艺' else self.env[ + 'sf.production.process.parameter'].search( + [('process_id', '=', route.surface_technics_id.id)]).id, + 'sequence': i, + 'is_auto': True}] + return workorders_values_str + + def unlink_technology_design(self): + self.active = False diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 55667d41..a0787477 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -284,6 +284,7 @@ class StockRule(models.Model): 'product_id': production.product_id.id, 'state': 'draft', }) + technology_design_values = [] all_production = productions grouped_product_ids = {k: list(g) for k, g in groupby(all_production, key=lambda x: x.product_id.id)} # 初始化一个字典来存储每个product_id对应的生产订单名称列表 @@ -293,33 +294,31 @@ class StockRule(models.Model): # 为同一个product_id创建一个生产订单名称列表 product_id_to_production_names[product_id] = [production.name for production in all_production] for production_item in productions: - production_programming = self.env['mrp.production'].search( [('product_id.id', '=', production_item.product_id.id), ('origin', '=', production_item.origin)], limit=1, order='id asc') if production_item.product_id.id in product_id_to_production_names: - if not production_programming.programming_no: - if production_item.product_id.model_process_parameters_ids: - is_purchase = False - sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids, - key=lambda w: w.id) + if production_item.product_id.model_process_parameters_ids: + is_purchase = False + sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids, + key=lambda w: w.id) - consecutive_process_parameters = [] - m = 0 - for i in range(len(sorted_process_parameters) - 1): - if m == 0: - is_purchase = False - if self.env['product.template']._get_process_parameters_product( - sorted_process_parameters[i]).partner_id == self.env[ - 'product.template']._get_process_parameters_product(sorted_process_parameters[ - i + 1]).partner_id and \ - sorted_process_parameters[i].gain_way == '外协': - if sorted_process_parameters[i] not in consecutive_process_parameters: - consecutive_process_parameters.append(sorted_process_parameters[i]) - consecutive_process_parameters.append(sorted_process_parameters[i + 1]) - m += 1 - continue + consecutive_process_parameters = [] + m = 0 + for i in range(len(sorted_process_parameters) - 1): + if m == 0: + is_purchase = False + if self.env['product.template']._get_process_parameters_product( + sorted_process_parameters[i]).partner_id == self.env[ + 'product.template']._get_process_parameters_product(sorted_process_parameters[ + i + 1]).partner_id and \ + sorted_process_parameters[i].gain_way == '外协': + if sorted_process_parameters[i] not in consecutive_process_parameters: + consecutive_process_parameters.append(sorted_process_parameters[i]) + consecutive_process_parameters.append(sorted_process_parameters[i + 1]) + m += 1 + continue else: if m == len(consecutive_process_parameters) - 1 and m != 0: self.env['purchase.order'].get_purchase_order(consecutive_process_parameters, @@ -329,7 +328,7 @@ class StockRule(models.Model): is_purchase = True consecutive_process_parameters = [] m = 0 - # 当前面的连续外协采购单生成再生成当前外协采购单 + # 当前面的连续外协采购单生成再生成当前外协采购单 if is_purchase is False: self.env['purchase.order'].get_purchase_order(consecutive_process_parameters, production_item, @@ -355,16 +354,70 @@ class StockRule(models.Model): self.env['purchase.order'].get_purchase_order(sorted_process_parameters[i], production_item, product_id_to_production_names) - # # 同一个产品多个制造订单对应一个编程单和模型库 - # # 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递 - if not production_item.programming_no: - if not production_programming.programming_no: - production_item.fetchCNC( - ', '.join(product_id_to_production_names[production_item.product_id.id])) - else: - production_item.write({'programming_no': production_programming.programming_no, - 'programming_state': '编程中'}) - return True + if not technology_design_values: + if production.product_id.categ_id.type == '成品': + production.product_id.model_processing_panel = 'ZM,FM' + # 根据加工面板的面数及成品工序模板生成工序设计 + i = 0 + for k in (production.product_id.model_processing_panel.split(',')): + product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search( + [('product_model_type_id', '=', production.product_id.product_model_type_id.id)], + order='sequence asc' + ) + for route in product_routing_workcenter: + i += 1 + technology_design_values.append( + self.env['sf.technology.design'].json_technology_design_str(k, route, i)) + surface_technics_arr = [] + route_workcenter_arr = [] + for process_param in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids.filtered( + lambda st: st.id in production.product_id.model_process_parameters_ids.ids): + # if item.route_workcenter_id.surface_technics_id.id: + # for process_param in production.product_id.model_process_parameters_ids: + logging.info('process_param:%s%s' % (process_param.id, process_param.name)) + if item.route_workcenter_id.surface_technics_id == process_param.process_id: + logging.info( + 'surface_technics_id:%s%s' % ( + item.route_workcenter_id.surface_technics_id.id, + item.route_workcenter_id.surface_technics_id.name)) + surface_technics_arr.append( + item.route_workcenter_id.surface_technics_id.id) + route_workcenter_arr.append(item.route_workcenter_id.id) + if surface_technics_arr: + production_process = self.env['sf.production.process'].search( + [('id', 'in', surface_technics_arr)], + order='sequence asc' + ) + for p in production_process: + logging.info('production_process:%s' % p.name) + process_parameter = production.product_id.model_process_parameters_ids.filtered( + lambda pm: pm.process_id.id == p.id) + product_production_process = self.env['product.template'].search( + [('server_product_process_parameters_id', '=', + process_parameter.id)]) + if process_parameter: + i += 1 + route_production_process = self.env[ + 'mrp.routing.workcenter'].search( + [('surface_technics_id', '=', p.id), + ('id', 'in', route_workcenter_arr)]) + technology_design_values.append( + self.env['sf.technology.design'].json_technology_design_str(k, + route_production_process, + product_production_process, + i)) + productions.technology_design_ids = technology_design_values + + # # 同一个产品多个制造订单对应一个编程单和模型库 + # # 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递 + # if not production_item.programming_no: + # if not production_programming.programming_no: + # production_item.fetchCNC( + # ', '.join(product_id_to_production_names[production_item.product_id.id])) + # else: + # production_item.write({'programming_no': production_programming.programming_no, + # 'programming_state': '编程中'}) + return True class ProductionLot(models.Model): @@ -554,7 +607,9 @@ class StockPicking(models.Model): retrospect_ref = fields.Char('追溯参考', compute='_compute_move_ids', store=True) - @api.depends('move_ids') + picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code') + + @api.depends('move_ids', 'move_ids.product_id') def _compute_move_ids(self): for item in self: if item.move_ids: @@ -574,13 +629,14 @@ class StockPicking(models.Model): default_codes = '' if boms: for bom in boms: - code = bom.product_tmpl_id.default_code.split('-')[-1] - default_code = bom.product_tmpl_id.default_code.split(f'-{code}')[0] - if default_code not in default_codes: - if default_codes == '': - default_codes = default_code - else: - default_codes = default_codes + ',' + default_code + if bom.product_tmpl_id.default_code: + code = bom.product_tmpl_id.default_code.split('-')[-1] + default_code = bom.product_tmpl_id.default_code.split(f'-{code}')[0] + if default_code not in default_codes: + if default_codes == '': + default_codes = default_code + else: + default_codes = default_codes + ',' + default_code item.retrospect_ref = default_codes elif item.picking_type_id.sequence_code in ['INT', 'PC']: pass diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index a8511245..21386995 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -165,6 +165,12 @@ access_sf_agv_scheduling_group_sf_order_user,sf_agv_scheduling_group_sf_order_us access_sf_agv_scheduling_group_sf_mrp_manager,sf_agv_scheduling_group_sf_mrp_manager,model_sf_agv_scheduling,sf_base.group_sf_mrp_manager,1,1,1,0 access_sf_agv_scheduling_group_sf_equipment_user,sf_agv_scheduling_group_sf_equipment_user,model_sf_agv_scheduling,sf_base.group_sf_equipment_user,1,1,1,0 +access_sf_technology_design_group_plan_dispatch,sf_technology_design_group_plan_dispatch,model_sf_technology_design,sf_base.group_plan_dispatch,1,1,1,0 +access_sf_technology_design_group_sf_mrp_manager,sf_technology_design_group_sf_mrp_manager,model_sf_technology_design,sf_base.group_sf_mrp_manager,1,1,1,0 +access_sf_technology_design_group_production_engineer,sf_technology_design_group_production_engineer,model_sf_technology_design,sf_base.group_production_engineer,1,1,1,0 +access_sf_production_technology_wizard_group_plan_dispatch,sf_production_technology_wizard_group_plan_dispatch,model_sf_production_technology_wizard,sf_base.group_plan_dispatch,1,1,1,0 +access_sf_production_technology_wizard_group_sf_mrp_manager,sf_production_technology_wizard_group_sf_mrp_manager,model_sf_production_technology_wizard,sf_base.group_sf_mrp_manager,1,1,1,0 +access_sf_production_technology_wizard_group_production_engineer,sf_production_technology_wizard_group_production_engineer,model_sf_production_technology_wizard,sf_base.group_production_engineer,1,1,1,0 diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index a271b622..b0bbb924 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -70,7 +70,7 @@ - confirmed,pending_cam,progress,rework,scrap,done + technology_to_confirmed,confirmed,pending_cam,progress,rework,scrap,done @@ -115,12 +115,18 @@ string="验证" type="object" class="oe_highlight" confirm="There are no components to consume. Are you still sure you want to continue?" data-hotkey="g" groups="sf_base.group_sf_mrp_user"/> + + + + + diff --git a/sf_manufacturing/views/mrp_routing_workcenter_view.xml b/sf_manufacturing/views/mrp_routing_workcenter_view.xml index eada92d9..ddb9558c 100644 --- a/sf_manufacturing/views/mrp_routing_workcenter_view.xml +++ b/sf_manufacturing/views/mrp_routing_workcenter_view.xml @@ -16,6 +16,7 @@ + diff --git a/sf_manufacturing/views/stock_picking_view.xml b/sf_manufacturing/views/stock_picking_view.xml index 356059c2..2fae1fac 100644 --- a/sf_manufacturing/views/stock_picking_view.xml +++ b/sf_manufacturing/views/stock_picking_view.xml @@ -18,10 +18,13 @@ - - - - + + + + + diff --git a/sf_manufacturing/wizard/__init__.py b/sf_manufacturing/wizard/__init__.py index ae06323f..b10ec066 100644 --- a/sf_manufacturing/wizard/__init__.py +++ b/sf_manufacturing/wizard/__init__.py @@ -1,3 +1,4 @@ from . import workpiece_delivery_wizard from . import rework_wizard from . import production_wizard +from . import production_technology_wizard diff --git a/sf_manufacturing/wizard/production_technology_wizard.py b/sf_manufacturing/wizard/production_technology_wizard.py new file mode 100644 index 00000000..648ea24b --- /dev/null +++ b/sf_manufacturing/wizard/production_technology_wizard.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Part of YiZuo. See LICENSE file for full copyright and licensing details. +import logging +from odoo.exceptions import UserError, ValidationError +from collections import defaultdict, namedtuple +from odoo.addons.stock.models.stock_rule import ProcurementException +from datetime import datetime +from odoo import models, api, fields, _ + + +class ProductionTechnologyWizard(models.TransientModel): + _name = 'sf.production.technology.wizard' + _description = '制造订单工艺确认向导' + + production_id = fields.Many2one('mrp.production', string='制造订单号') + origin = fields.Char(string='源单据') + is_technology_confirm = fields.Boolean(default=False) + + def confirm(self): + if self.is_technology_confirm is True: + domain = [('origin', '=', self.origin)] + else: + domain = [('id', '=', self.production_id.id)] + productions = self.env['mrp.production'].search(domain) + productions._create_workorder(self.production_id) diff --git a/sf_manufacturing/wizard/production_technology_wizard_views.xml b/sf_manufacturing/wizard/production_technology_wizard_views.xml new file mode 100644 index 00000000..134fb8a9 --- /dev/null +++ b/sf_manufacturing/wizard/production_technology_wizard_views.xml @@ -0,0 +1,34 @@ + + + + sf.production.technology.wizard.form.view + sf.production.technology.wizard + +

+ + + +
+ + 对当前制造订单,同一销售订单相同产品所生成的制造订单统一进行工艺调整与确认 +
+ +
+
+
+
+ + + 工艺确认 + sf.production.technology.wizard + form + + + + new + + + \ No newline at end of file diff --git a/sf_plan/views/view.xml b/sf_plan/views/view.xml index f30cbb49..21e320d1 100644 --- a/sf_plan/views/view.xml +++ b/sf_plan/views/view.xml @@ -89,7 +89,7 @@ - + diff --git a/sf_quality/views/quality_cnc_test_view.xml b/sf_quality/views/quality_cnc_test_view.xml index 31c21ed3..29cab866 100644 --- a/sf_quality/views/quality_cnc_test_view.xml +++ b/sf_quality/views/quality_cnc_test_view.xml @@ -175,8 +175,8 @@ 驾驶舱 ir.actions.act_window quality.cnc.test - kanban,tree,form - + tree,kanban,form + [] { 'search_default_filter_waiting':1} diff --git a/sf_quality/views/view.xml b/sf_quality/views/view.xml index e0b2064b..6b7e4fa0 100644 --- a/sf_quality/views/view.xml +++ b/sf_quality/views/view.xml @@ -26,7 +26,7 @@ 质量缺陷单 quality.alert - kanban,tree,form,pivot,graph,calendar + tree,kanban,form,pivot,graph,calendar

Create a new quality alert diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py index 01d5953b..91e6cd46 100644 --- a/sf_sale/models/sale_order.py +++ b/sf_sale/models/sale_order.py @@ -208,7 +208,8 @@ class RePurchaseOrder(models.Model): compute='_compute_user_id', store=True) - purchase_type = fields.Selection([('standard', '标准采购'), ('consignment', '委外加工')], string='采购类型', default='standard') + purchase_type = fields.Selection([('standard', '标准采购'), ('consignment', '委外加工')], string='采购类型', + default='standard') @api.depends('partner_id') def _compute_user_id(self): @@ -273,6 +274,7 @@ class RePurchaseOrder(models.Model): 'partner_id': server_template.seller_ids.partner_id.id, 'origin': ','.join(production_process), 'state': 'draft', + 'purchase_type': 'consignment', 'order_line': server_product_process}) # self.env.cr.commit() diff --git a/sf_sale/views/purchase_order_view.xml b/sf_sale/views/purchase_order_view.xml index 0a66287f..23c71a1f 100644 --- a/sf_sale/views/purchase_order_view.xml +++ b/sf_sale/views/purchase_order_view.xml @@ -218,6 +218,7 @@ {"search_default_categ_id":1,"search_default_filter_to_purchase":1, "purchase_product_template": 1} + tree,kanban,form,activity \ No newline at end of file diff --git a/sf_sale/views/quick_easy_order_view.xml b/sf_sale/views/quick_easy_order_view.xml index aebb4e97..3b386062 100644 --- a/sf_sale/views/quick_easy_order_view.xml +++ b/sf_sale/views/quick_easy_order_view.xml @@ -74,7 +74,7 @@ - + diff --git a/sf_sale/views/sale_order_view.xml b/sf_sale/views/sale_order_view.xml index 16ae9c4c..fe7c840d 100644 --- a/sf_sale/views/sale_order_view.xml +++ b/sf_sale/views/sale_order_view.xml @@ -95,7 +95,7 @@ {'readonly': [('state', 'in', ['cancel','sale'])]} - + @@ -165,9 +165,11 @@ 拒绝接单 - + - + @@ -218,7 +220,7 @@ 下单时间 - @@ -283,6 +285,8 @@ + tree,kanban,form,activity + {"search_default_categ_id":1, "search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1} diff --git a/sf_stock/views/stock_product_template.xml b/sf_stock/views/stock_product_template.xml index 2d2a2fbe..8938f924 100644 --- a/sf_stock/views/stock_product_template.xml +++ b/sf_stock/views/stock_product_template.xml @@ -2,6 +2,7 @@ + tree,kanban,form,activity {"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}