From ec9f82f09c89a372827622fef3432f561b189c31 Mon Sep 17 00:00:00 2001 From: gqh Date: Wed, 16 Nov 2022 09:41:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E4=B8=AD=E5=BF=83=E6=A6=82?= =?UTF-8?q?=E8=BF=B0=E7=9C=8B=E6=9D=BF=E8=A7=86=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/sf_production.py | 12 +- sf_route_workcenter/__manifest__.py | 5 +- sf_route_workcenter/models/__init__.py | 2 +- sf_route_workcenter/models/mrp_maintenance.py | 184 ++++++++++++++++++ sf_route_workcenter/models/workcenter.py | 5 +- .../views/maintenance_views.xml | 95 +++++++++ sf_route_workcenter/views/mrp_views.xml | 60 ++++++ .../views/mrp_workcenter_views.xml | 37 ++++ sf_route_workcenter/views/sf_workorder.xml | 88 ++++++--- 9 files changed, 452 insertions(+), 36 deletions(-) create mode 100644 sf_route_workcenter/models/mrp_maintenance.py create mode 100644 sf_route_workcenter/views/maintenance_views.xml create mode 100644 sf_route_workcenter/views/mrp_views.xml create mode 100644 sf_route_workcenter/views/mrp_workcenter_views.xml diff --git a/sf_manufacturing_orders/models/sf_production.py b/sf_manufacturing_orders/models/sf_production.py index 39984650..a41e34f3 100644 --- a/sf_manufacturing_orders/models/sf_production.py +++ b/sf_manufacturing_orders/models/sf_production.py @@ -220,18 +220,26 @@ class StockRule(models.Model): for company_id, productions_values in productions_values_by_company.items(): # create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example) + '''创建制造订单''' productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( productions_values) self.env['stock.move'].sudo().create(productions._get_moves_raw_values()) self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) + ''' + 创建工单 + ''' productions._create_workorder() - productions.lot_producing_id._get_next_serial(productions.company_id,productions.product_id) - productions.action_generate_serial() + productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \ ( p.move_dest_ids.procure_method != 'make_to_order' and not p.move_raw_ids and not p.workorder_ids)).action_confirm() for production in productions: + ''' + 创建制造订单时生成序列号 + ''' + production.lot_producing_id._get_next_serial(production.company_id, production.product_id) + production.action_generate_serial() origin_production = production.move_dest_ids and production.move_dest_ids[ 0].raw_material_production_id or False orderpoint = production.orderpoint_id diff --git a/sf_route_workcenter/__manifest__.py b/sf_route_workcenter/__manifest__.py index c7764d13..5d8e16ca 100644 --- a/sf_route_workcenter/__manifest__.py +++ b/sf_route_workcenter/__manifest__.py @@ -10,10 +10,13 @@ """, 'category': 'YZ', 'website': 'https://www.sf.cs.jikimo.com', - 'depends': ['mrp', 'sf_base', 'hr_holidays'], + 'depends': ['mrp', 'sf_base', 'hr_holidays', 'maintenance'], 'data': [ 'views/sf_tray_view.xml', 'views/sf_workorder.xml', + 'views/maintenance_views.xml', + 'views/mrp_views.xml', + 'views/mrp_workcenter_views.xml', 'report/sf_tray_report.xml' ], 'demo': [ diff --git a/sf_route_workcenter/models/__init__.py b/sf_route_workcenter/models/__init__.py index c1bb7975..f5061dc2 100644 --- a/sf_route_workcenter/models/__init__.py +++ b/sf_route_workcenter/models/__init__.py @@ -1,3 +1,3 @@ # -*-coding:utf-8-*- from . import workcenter - +from . import mrp_maintenance diff --git a/sf_route_workcenter/models/mrp_maintenance.py b/sf_route_workcenter/models/mrp_maintenance.py new file mode 100644 index 00000000..d0ea9b42 --- /dev/null +++ b/sf_route_workcenter/models/mrp_maintenance.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from dateutil.relativedelta import relativedelta +from datetime import timedelta, datetime +from collections import defaultdict + +from odoo import api, fields, models, _ +from odoo.addons.resource.models.resource import Intervals + + + + + +class MrpWorkcenter(models.Model): + _inherit = "mrp.workcenter" + + equipment_ids = fields.One2many( + 'maintenance.equipment', 'workcenter_id', string="Maintenance Equipment", + check_company=True) + + def action_work_order(self): + if not self.env.context.get('desktop_list_view', False): + action = self.env["ir.actions.actions"]._for_xml_id("sf_route_workcenter.mrp_workorder_action_tablet") + return action + else: + return super(MrpWorkcenter, self).action_work_order() + + def _get_unavailability_intervals(self, start_datetime, end_datetime): + res = super(MrpWorkcenter, self)._get_unavailability_intervals(start_datetime, end_datetime) + if not self: + return res + sql = """ + SELECT workcenter_id, ARRAY_AGG((schedule_date || '|' || schedule_date + INTERVAL '1h' * duration)) as date_intervals + FROM maintenance_request + LEFT JOIN maintenance_equipment + ON maintenance_request.equipment_id = maintenance_equipment.id + WHERE + schedule_date IS NOT NULL + AND duration IS NOT NULL + AND equipment_id IS NOT NULL + AND maintenance_equipment.workcenter_id IS NOT NULL + AND maintenance_equipment.workcenter_id IN %s + AND (schedule_date, schedule_date + INTERVAL '1h' * duration) OVERLAPS (%s, %s) + GROUP BY maintenance_equipment.workcenter_id; + """ + self.env.cr.execute(sql, [tuple(self.ids), fields.Datetime.to_string(start_datetime.astimezone()), fields.Datetime.to_string(end_datetime.astimezone())]) + res_maintenance = defaultdict(list) + for wc_row in self.env.cr.dictfetchall(): + res_maintenance[wc_row.get('workcenter_id')] = [ + [fields.Datetime.to_datetime(i) for i in intervals.split('|')] + for intervals in wc_row.get('date_intervals') + ] + + for wc_id in self.ids: + intervals_previous_list = [(s.timestamp(), e.timestamp(), self.env['maintenance.request']) for s, e in res[wc_id]] + intervals_maintenances_list = [(m[0].timestamp(), m[1].timestamp(), self.env['maintenance.request']) for m in res_maintenance[wc_id]] + final_intervals_wc = Intervals(intervals_previous_list + intervals_maintenances_list) + res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc] + return res + + +class MaintenanceEquipment(models.Model): + _inherit = "maintenance.equipment" + _check_company_auto = True + + expected_mtbf = fields.Integer(string='Expected MTBF', help='Expected Mean Time Between Failure') + mtbf = fields.Integer(compute='_compute_maintenance_request', string='MTBF', help='Mean Time Between Failure, computed based on done corrective maintenances.') + mttr = fields.Integer(compute='_compute_maintenance_request', string='MTTR', help='Mean Time To Repair') + estimated_next_failure = fields.Date(compute='_compute_maintenance_request', string='Estimated time before next failure (in days)', help='Computed as Latest Failure Date + MTBF') + latest_failure_date = fields.Date(compute='_compute_maintenance_request', string='Latest Failure Date') + workcenter_id = fields.Many2one( + 'mrp.workcenter', string='Work Center', check_company=True) + + @api.depends('effective_date', 'maintenance_ids.stage_id', 'maintenance_ids.close_date', 'maintenance_ids.request_date') + def _compute_maintenance_request(self): + for equipment in self: + maintenance_requests = equipment.maintenance_ids.filtered(lambda x: x.maintenance_type == 'corrective' and x.stage_id.done) + mttr_days = 0 + for maintenance in maintenance_requests: + if maintenance.stage_id.done and maintenance.close_date: + mttr_days += (maintenance.close_date - maintenance.request_date).days + equipment.mttr = len(maintenance_requests) and (mttr_days / len(maintenance_requests)) or 0 + maintenance = maintenance_requests.sorted(lambda x: x.request_date) + if len(maintenance) >= 1: + equipment.mtbf = (maintenance[-1].request_date - equipment.effective_date).days / len(maintenance) + equipment.latest_failure_date = maintenance and maintenance[-1].request_date or False + if equipment.mtbf: + equipment.estimated_next_failure = equipment.latest_failure_date + relativedelta(days=equipment.mtbf) + else: + equipment.estimated_next_failure = False + + def button_mrp_workcenter(self): + self.ensure_one() + return { + 'name': _('work centers'), + 'view_mode': 'form', + 'res_model': 'mrp.workcenter', + 'view_id': self.env.ref('mrp.mrp_workcenter_view').id, + 'type': 'ir.actions.act_window', + 'res_id': self.workcenter_id.id, + 'context': { + 'default_company_id': self.company_id.id + } + } + + +class MaintenanceRequest(models.Model): + _inherit = "maintenance.request" + _check_company_auto = True + + production_id = fields.Many2one( + 'mrp.production', string='Manufacturing Order', check_company=True) + workorder_id = fields.Many2one( + 'mrp.workorder', string='Work Order', check_company=True) + production_company_id = fields.Many2one(string='Production Company', related='production_id.company_id') + company_id = fields.Many2one(domain="[('id', '=?', production_company_id)]") + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests") + request_ids = fields.One2many('maintenance.request', 'production_id') + + @api.depends('request_ids') + def _compute_maintenance_count(self): + for production in self: + production.maintenance_count = len(production.request_ids) + + def button_maintenance_req(self): + self.ensure_one() + return { + 'name': _('New Maintenance Request'), + 'view_mode': 'form', + 'res_model': 'maintenance.request', + 'type': 'ir.actions.act_window', + 'context': { + 'default_company_id': self.company_id.id, + 'default_production_id': self.id, + }, + 'domain': [('production_id', '=', self.id)], + } + + def open_maintenance_request_mo(self): + self.ensure_one() + action = { + 'name': _('Maintenance Requests'), + 'view_mode': 'kanban,tree,form,pivot,graph,calendar', + 'res_model': 'maintenance.request', + 'type': 'ir.actions.act_window', + 'context': { + 'default_company_id': self.company_id.id, + 'default_production_id': self.id, + }, + 'domain': [('production_id', '=', self.id)], + } + if self.maintenance_count == 1: + production = self.env['maintenance.request'].search([('production_id', '=', self.id)]) + action['view_mode'] = 'form' + action['res_id'] = production.id + return action + + +class MrpProductionWorkcenterLine(models.Model): + _inherit = "mrp.workorder" + + def button_maintenance_req(self): + self.ensure_one() + return { + 'name': _('New Maintenance Request'), + 'view_mode': 'form', + 'views': [(self.env.ref('mrp_maintenance.maintenance_request_view_form_inherit_mrp').id, 'form')], + 'res_model': 'maintenance.request', + 'type': 'ir.actions.act_window', + 'context': { + 'default_company_id': self.company_id.id, + 'default_workorder_id': self.id, + 'default_production_id': self.production_id.id, + 'discard_on_footer_button': True, + }, + 'target': 'new', + 'domain': [('workorder_id', '=', self.id)] + } diff --git a/sf_route_workcenter/models/workcenter.py b/sf_route_workcenter/models/workcenter.py index f703da78..25a8b5b2 100644 --- a/sf_route_workcenter/models/workcenter.py +++ b/sf_route_workcenter/models/workcenter.py @@ -320,7 +320,10 @@ class MrpProduction(models.Model): i += 1 for route in routingworkcenter: - if route.is_repeat == True: + if route.routing_type == 'CNC加工': + workorders_values.append( + self.env['mrp.workorder'].json_workorder_str(k, production, route)) + if route.routing_type == '后置三元质量检测': workorders_values.append( self.env['mrp.workorder'].json_workorder_str(k, production, route)) diff --git a/sf_route_workcenter/views/maintenance_views.xml b/sf_route_workcenter/views/maintenance_views.xml new file mode 100644 index 00000000..28eb251b --- /dev/null +++ b/sf_route_workcenter/views/maintenance_views.xml @@ -0,0 +1,95 @@ + + + + maintenance.equipment.view.form.inherit.mrp + maintenance.equipment + + + + + + + + + + + + + + + + + maintenance.request.view.form.inherit.mrp + maintenance.request + + + + + + + + + + +
+
+
+
+
+ + ['|', (not workorder_id and 1 or 0, '=', 1), '|', ('workcenter_id', '=', False), ('workcenter_id.order_ids', 'in', workorder_id)] + +
+
+ + + maintenence.request.view.search.inherit.mrp + maintenance.request + + + + + + + + + + + + +
diff --git a/sf_route_workcenter/views/mrp_views.xml b/sf_route_workcenter/views/mrp_views.xml new file mode 100644 index 00000000..6e3f70c6 --- /dev/null +++ b/sf_route_workcenter/views/mrp_views.xml @@ -0,0 +1,60 @@ + + + + + mrp.workcenter.form.inherit.maintenance + mrp.workcenter + + + + + + + + + + + + + + + + + + + + + mrp.workcenter.view.kanban.inherit.maintenance + mrp.workcenter + + + + + + + + + + + mrp.production.view.form.inherit.maintenance + mrp.production + + + +
+ +
+
+
+ + +
diff --git a/sf_route_workcenter/views/mrp_workcenter_views.xml b/sf_route_workcenter/views/mrp_workcenter_views.xml new file mode 100644 index 00000000..07349ec2 --- /dev/null +++ b/sf_route_workcenter/views/mrp_workcenter_views.xml @@ -0,0 +1,37 @@ + + + mrp.workcenter.view.kanban.inherit.mrp.workorder + mrp.workcenter + + + + +