diff --git a/jikimo_workorder_exception/__init__.py b/jikimo_workorder_exception/__init__.py new file mode 100644 index 00000000..c3d410ea --- /dev/null +++ b/jikimo_workorder_exception/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import controllers diff --git a/jikimo_workorder_exception/__manifest__.py b/jikimo_workorder_exception/__manifest__.py new file mode 100644 index 00000000..4c63ed6d --- /dev/null +++ b/jikimo_workorder_exception/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': '机企猫 工单异常记录', + 'version': '1.0', + 'summary': '记录工单的异常日志', + 'sequence': 1, + 'category': 'sf', + 'website': 'https://www.sf.jikimo.com', + 'depends': ['sf_manufacturing', 'sf_mrs_connect'], + 'data': [ + 'views/mrp_workorder_views.xml', + 'security/ir.model.access.csv', + ], + 'demo': [ + ], + 'license': 'LGPL-3', + 'installable': True, + 'application': False, + 'auto_install': False, +} diff --git a/jikimo_workorder_exception/controllers/__init__.py b/jikimo_workorder_exception/controllers/__init__.py new file mode 100644 index 00000000..deec4a8b --- /dev/null +++ b/jikimo_workorder_exception/controllers/__init__.py @@ -0,0 +1 @@ +from . import main \ No newline at end of file diff --git a/jikimo_workorder_exception/controllers/main.py b/jikimo_workorder_exception/controllers/main.py new file mode 100644 index 00000000..6a17f958 --- /dev/null +++ b/jikimo_workorder_exception/controllers/main.py @@ -0,0 +1,89 @@ +from odoo import http, fields +from odoo.http import request +import json +import logging +from odoo.addons.sf_mrs_connect.controllers.controllers import Sf_Mrs_Connect +from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect + +_logger = logging.getLogger(__name__) + +class WorkorderExceptionConroller(http.Controller): + + @http.route('/AutoDeviceApi/BillError', type='json', auth='public', methods=['GET', 'POST'], csrf=False, + cors="*") + def workder_exception(self, **kw): + """ + 记录工单异常 + :param kw: + :return: + """ + _logger.info('workder_exception:%s' % kw) + try: + res = {'Succeed': True, 'ErrorCode': 0, 'Error': ''} + datas = request.httprequest.data + ret = json.loads(datas)['Datas'] + if not ret.get('RfidCode') or not ret.get('ErrorType'): + res = {'Succeed': False, 'ErrorCode': 400, 'Error': '参数错误'} + return json.JSONEncoder().encode(res) + + # 通过RfidCode获取就绪的CNC工单 + workorder = request.env['mrp.workorder'].sudo().search([ + ('rfid_code', '=', ret['RfidCode']), + ('routing_type', '=', 'CNC加工'), + ]) + if not workorder: + res = {'Succeed': False, 'ErrorCode': 401, 'Error': '无效的工单'} + return json.JSONEncoder().encode(res) + + # 创建工单异常记录,关联工单 + request.env['jikimo.workorder.exception'].sudo().create({ + 'workorder_id': workorder.id, + 'exception_code': ret.get('ErrorType'), + 'exception_content': ret.get('Error', '') + }) + + except Exception as e: + res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} + _logger.info('workder_exception error:%s' % e) + return json.JSONEncoder().encode(res) + + +class SfMrsConnectController(Sf_Mrs_Connect): + + @http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + cors="*") + def get_cnc_processing_create(self, **kw): + """ + 更新工单异常记录【'YC001', 'YC004'】 + """ + res = super(SfMrsConnectController, self).get_cnc_processing_create(**kw) + # 如果有未完成的YC0001、YC0004异常记录,则标记为完成 + res = json.loads(res) + _logger.info('已进入工单异常:%s' % res) + if res.get('production_ids'): + try: + productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))]) + if productions.workorder_ids: + productions.workorder_ids.handle_exception(['YC0001', 'YC0004']) + except Exception as e: + _logger.info('更新工单异常记录失败:%s' % e) + return json.JSONEncoder().encode(res) + +class ManufactruingController(Manufacturing_Connect): + + @http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False, + cors="*") + def button_Work_START(self, **kw): + """ + 更新工单异常记录【'YC0002', 'YC0003'】 + """ + res = super(ManufactruingController, self).button_Work_START(**kw) + res = json.loads(res) + _logger.info('已进入工单异常:%s' % res) + if res.get('workorder_id'): + try: + workorder = request.env['mrp.workorder'].sudo().browse(int(res.get('workorder_id'))) + workorder.handle_exception(['YC0002', 'YC0003']) + except Exception as e: + _logger.info('更新工单异常记录失败:%s' % e) + return json.JSONEncoder().encode(res) \ No newline at end of file diff --git a/jikimo_workorder_exception/models/__init__.py b/jikimo_workorder_exception/models/__init__.py new file mode 100644 index 00000000..9ab4d803 --- /dev/null +++ b/jikimo_workorder_exception/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import jikimo_workorder_exception +from . import mrp_workorder diff --git a/jikimo_workorder_exception/models/jikimo_workorder_exception.py b/jikimo_workorder_exception/models/jikimo_workorder_exception.py new file mode 100644 index 00000000..19dd824b --- /dev/null +++ b/jikimo_workorder_exception/models/jikimo_workorder_exception.py @@ -0,0 +1,14 @@ +from odoo import models, fields + + +class JikimoWorkorderException(models.Model): + _name = 'jikimo.workorder.exception' + _description = '工单异常记录' + _order = 'id desc' + + workorder_id = fields.Many2one('mrp.workorder', string='工单') + exception_code = fields.Char('异常编码') + exception_content = fields.Char('反馈的异常/问题信息') + completion_time = fields.Datetime('处理完成时间') + state = fields.Selection([('pending', '进行中'), ('done', '已处理')], string='状态', default='pending') + \ No newline at end of file diff --git a/jikimo_workorder_exception/models/mrp_workorder.py b/jikimo_workorder_exception/models/mrp_workorder.py new file mode 100644 index 00000000..64f0dd8f --- /dev/null +++ b/jikimo_workorder_exception/models/mrp_workorder.py @@ -0,0 +1,40 @@ +from odoo import models, fields +import logging + +_logger = logging.getLogger(__name__) + +class MrpWorkorder(models.Model): + _inherit = 'mrp.workorder' + + exception_ids = fields.One2many('jikimo.workorder.exception', 'workorder_id', string='工单异常记录') + + def write(self, values): + if values.get('test_results') and self.exception_ids: + pending_exception = self.exception_ids.filtered( + lambda exc: exc.state == 'pending' and exc.exception_code == 'YC0005' + ) + if pending_exception: + pending_exception.write({ + 'completion_time': fields.Datetime.now(), + 'state': 'done' + }) + return super(MrpWorkorder, self).write(values) + + def handle_exception(self, exception_codes): + """ + 处理异常 + :param exception_codes: 需要处理的异常编码列表 + """ + if not isinstance(exception_codes, list): + exception_codes = [exception_codes] + if self.exception_ids: + _logger.info('workorder.exception_ids:%s' % self.exception_ids) + pending_exception = self.exception_ids.filtered( + lambda exc: exc.state == 'pending' and exc.exception_code in exception_codes + ) + _logger.info('pending_exception:%s' % pending_exception) + if pending_exception: + pending_exception.write({ + 'completion_time': fields.Datetime.now(), + 'state': 'done' + }) diff --git a/jikimo_workorder_exception/security/ir.model.access.csv b/jikimo_workorder_exception/security/ir.model.access.csv new file mode 100644 index 00000000..bbc066e4 --- /dev/null +++ b/jikimo_workorder_exception/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_jikimo_workorder_exception","access.jikimo.workorder.exception","model_jikimo_workorder_exception","mrp.group_mrp_user",1,1,1,0 diff --git a/jikimo_workorder_exception/tests/__init__.py b/jikimo_workorder_exception/tests/__init__.py new file mode 100644 index 00000000..99feb117 --- /dev/null +++ b/jikimo_workorder_exception/tests/__init__.py @@ -0,0 +1,2 @@ +from . import common +from . import test_jikimo_workorder_exception diff --git a/jikimo_workorder_exception/tests/common.py b/jikimo_workorder_exception/tests/common.py new file mode 100644 index 00000000..bbc729db --- /dev/null +++ b/jikimo_workorder_exception/tests/common.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import fields, Command +from odoo.tests.common import TransactionCase, HttpCase, tagged, Form + +import json +import time +import base64 +from lxml import etree + +@tagged('post_install', '-at_install') +class TestJikimoWorkorderExceptionCommon(TransactionCase): + + def setUp(self): + super(TestJikimoWorkorderExceptionCommon, self).setUp() + # 获取名字为“1#自动生产线”的制造中心 + workcenter = self.env['mrp.workcenter'].search([('name', '=', '1#自动生产线')], limit=1) + # 创建一个产品 + product_product = self.env['product.product'].create({ + 'name': '测试产品', + 'type': 'product', + }) + uom_unit = self.env.ref('uom.product_uom_unit') + # 创建一个bom + self.bom = self.env['mrp.bom'].create({ + 'product_id': product_product.id, + 'product_tmpl_id': product_product.product_tmpl_id.id, + 'product_uom_id': uom_unit.id, + 'product_qty': 1.0, + 'type': 'normal', + }) + # 创建一个制造订单 + self.production = self.env['mrp.production'].create({ + 'name': 'Test Production', + 'product_id': product_product.id, + 'bom_id': self.bom.id, + 'company_id': self.env.ref('base.main_company').id, + }) + # 创建一个测试工单 + self.workorder = self.env['mrp.workorder'].create({ + 'name': 'Test order', + 'workcenter_id': workcenter.id, + 'product_uom_id': self.bom.product_uom_id.id, + 'production_id': self.production.id, + 'duration_expected': 1.0, + 'rfid_code': 'test-123456', + 'routing_type': 'CNC加工' + }) \ No newline at end of file diff --git a/jikimo_workorder_exception/tests/test_jikimo_workorder_exception.py b/jikimo_workorder_exception/tests/test_jikimo_workorder_exception.py new file mode 100644 index 00000000..69220947 --- /dev/null +++ b/jikimo_workorder_exception/tests/test_jikimo_workorder_exception.py @@ -0,0 +1,53 @@ +import json +from datetime import datetime +from odoo.addons.jikimo_workorder_exception.tests.common import TestJikimoWorkorderExceptionCommon + +class TestJikimoWorkorderException(TestJikimoWorkorderExceptionCommon): + + def test_create_exception_record(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0001', + 'exception_content': '无CNC编程' + }) + + self.assertTrue(exception_record) + self.assertEqual(exception_record.exception_content, '无CNC编程') + self.assertEqual(exception_record.workorder_id.id, self.workorder.id) + self.assertEqual(exception_record.exception_code, 'YC0001') + + def test_handle_exception(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0001', + 'exception_content': '无CNC编程' + }) + self.workorder.handle_exception('YC0001') + self.assertEqual(exception_record.state, 'done') + # 判断完成时间是否为当前分钟 + self.assertEqual(exception_record.completion_time.minute, datetime.now().minute) + + def test_handle_exception_with_invalid_code(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0001', + 'exception_content': '无CNC编程' + }) + self.workorder.handle_exception(['YC0002', 'YC0004']) + self.assertEqual(exception_record.state, 'pending') + self.assertEqual(exception_record.completion_time, False) + + + def test_handle_exception_with_test_results(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0005', + 'exception_content': '工单加工失败' + }) + self.workorder.write({ + 'test_results': '返工', + 'reason': 'cutter', + 'detailed_reason': '刀坏了', + }) + self.assertEqual(exception_record.state, 'done') + self.assertEqual(exception_record.completion_time.minute, datetime.now().minute) \ No newline at end of file diff --git a/jikimo_workorder_exception/views/mrp_workorder_views.xml b/jikimo_workorder_exception/views/mrp_workorder_views.xml new file mode 100644 index 00000000..e589adb4 --- /dev/null +++ b/jikimo_workorder_exception/views/mrp_workorder_views.xml @@ -0,0 +1,23 @@ + + + + + mrp.workorder.form + mrp.workorder + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jikimo_workorder_exception_notify/__init__.py b/jikimo_workorder_exception_notify/__init__.py new file mode 100644 index 00000000..77bbdbd3 --- /dev/null +++ b/jikimo_workorder_exception_notify/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import models + diff --git a/jikimo_workorder_exception_notify/__manifest__.py b/jikimo_workorder_exception_notify/__manifest__.py new file mode 100644 index 00000000..0b8c1013 --- /dev/null +++ b/jikimo_workorder_exception_notify/__manifest__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': '机企猫 工单异常消息通知', + 'version': '1.0', + 'summary': '当产生工单异常时,发送消息通知', + 'sequence': 1, + 'category': 'sf', + 'website': 'https://www.sf.jikimo.com', + 'depends': ['jikimo_workorder_exception', 'jikimo_message_notify'], + 'data': [ + 'data/bussiness_node.xml', + 'data/template_data.xml', + # 'security/ir.model.access.csv', + ], + 'demo': [ + ], + 'license': 'LGPL-3', + 'installable': True, + 'application': False, + 'auto_install': False, +} diff --git a/jikimo_workorder_exception_notify/data/bussiness_node.xml b/jikimo_workorder_exception_notify/data/bussiness_node.xml new file mode 100644 index 00000000..b772158b --- /dev/null +++ b/jikimo_workorder_exception_notify/data/bussiness_node.xml @@ -0,0 +1,17 @@ + + + + + 无功能刀具 + jikimo.workorder.exception + + + 无定位数据 + jikimo.workorder.exception + + + 加工失败 + jikimo.workorder.exception + + + \ No newline at end of file diff --git a/jikimo_workorder_exception_notify/data/template_data.xml b/jikimo_workorder_exception_notify/data/template_data.xml new file mode 100644 index 00000000..554ef2c2 --- /dev/null +++ b/jikimo_workorder_exception_notify/data/template_data.xml @@ -0,0 +1,38 @@ + + + + + 生产线无功能刀具提醒 + + jikimo.workorder.exception + + markdown + urgent + ### 生产线无功能刀具提醒 +单号:工单[{{workorder_id.production_id.name}}]({{url}}) +原因:生产线无加工程序用的{{function_tool_name}}名称功能刀具 + + + 工单无定位数据提醒 + + jikimo.workorder.exception + + markdown + urgent + ### 生产线无功能刀具提醒 +单号:工单[{{workorder_id.production_id.name}}]({{url}}) +原因:无装夹定位测量数据 + + + 工单加工失败提醒 + + jikimo.workorder.exception + + markdown + urgent + ### 工单加工失败提醒 +单号:工单[{{workorder_id.production_id.name}}]({{url}}) +原因:加工失败,工件下产线处理 + + + \ No newline at end of file diff --git a/jikimo_workorder_exception_notify/models/__init__.py b/jikimo_workorder_exception_notify/models/__init__.py new file mode 100644 index 00000000..76a90c74 --- /dev/null +++ b/jikimo_workorder_exception_notify/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import jikimo_message_template +from . import jikimo_workorder_exception diff --git a/jikimo_workorder_exception_notify/models/jikimo_message_template.py b/jikimo_workorder_exception_notify/models/jikimo_message_template.py new file mode 100644 index 00000000..530cbb99 --- /dev/null +++ b/jikimo_workorder_exception_notify/models/jikimo_message_template.py @@ -0,0 +1,10 @@ +from odoo import models + + +class JikimoMessageTemplate(models.Model): + _inherit = "jikimo.message.template" + + def _get_message_model(self): + res = super(JikimoMessageTemplate, self)._get_message_model() + res.append('jikimo.workorder.exception') + return res \ No newline at end of file diff --git a/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py b/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py new file mode 100644 index 00000000..bbe69ab5 --- /dev/null +++ b/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py @@ -0,0 +1,61 @@ +from odoo import models, api +from odoo.addons.sf_base.commons.common import Common +import requests, logging + +_logger = logging.getLogger(__name__) + + +class JikimoWorkorderException(models.Model): + _name = 'jikimo.workorder.exception' + _inherit = ['jikimo.workorder.exception', 'jikimo.message.dispatch'] + + @api.model_create_multi + def create(self, vals_list): + res = super(JikimoWorkorderException, self).create(vals_list) + # 根据异常编码发送消息提醒 + try: + for rec in res: + if rec.exception_code == 'YC0001': + # 无CNC程序,调用cloud接口 + data = {'name': rec.workorder_id.production_id.programming_no, 'exception_code': 'YC0001'} + configsettings = self.env['res.config.settings'].sudo().get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/message/workorder_exception' + config_url = configsettings['sf_url'] + url + data['token'] = configsettings['token'] + ret = requests.post(config_url, json=data, headers=config_header) + ret = ret.json() + _logger.info('无CNC程序异常消息推送接口:%s' % ret) + elif rec.exception_code == 'YC0002': + # 无功能刀具 + rec.add_queue('无功能刀具') + elif rec.exception_code == 'YC0003': + # 无定位数据 + rec.add_queue('无定位数据') + elif rec.exception_code == 'YC0004': + # 无FTP文件,调用cloud接口 + data = {'name': rec.workorder_id.programming_no, 'exception_code': 'YC0004'} + configsettings = self.env['res.config.settings'].sudo().get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/message/workorder_exception' + config_url = configsettings['sf_url'] + url + data['token'] = configsettings['token'] + ret = requests.post(config_url, json=data, headers=config_header) + ret = ret.json() + _logger.info('无FTP文件异常消息推送接口:%s' % ret) + elif rec.exception_code == 'YC0005': + # 加工失败 + rec.add_queue('加工失败') + except Exception as e: + _logger.error('异常编码发送消息提醒失败:%s' % e) + return res + + def _get_message(self, message_queue_ids): + contents = super(JikimoWorkorderException, self)._get_message(message_queue_ids) + url = self.env['ir.config_parameter'].get_param('web.base.url') + action_id = self.env.ref('mrp.mrp_production_action').id + for index, content in enumerate(contents): + exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id) + url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id) + contents[index] = content.replace('{{url}}', url) + return contents diff --git a/jikimo_workorder_exception_notify/tests/__init__.py b/jikimo_workorder_exception_notify/tests/__init__.py new file mode 100644 index 00000000..318a4c8a --- /dev/null +++ b/jikimo_workorder_exception_notify/tests/__init__.py @@ -0,0 +1,2 @@ +from . import common +from . import test_jikimo_workorder_exception_notify diff --git a/jikimo_workorder_exception_notify/tests/common.py b/jikimo_workorder_exception_notify/tests/common.py new file mode 100644 index 00000000..5fefe6bf --- /dev/null +++ b/jikimo_workorder_exception_notify/tests/common.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import fields, Command +from odoo.tests.common import TransactionCase, HttpCase, tagged, Form + +import json +import time +import base64 +from lxml import etree + +@tagged('post_install', '-at_install') +class TestJikimoWorkorderExceptionNotifyCommonNotify(TransactionCase): + + def setUp(self): + super(TestJikimoWorkorderExceptionNotifyCommonNotify, self).setUp() + # 获取最后一个工单 + self.workorder = self.env['mrp.workorder'].search([], order='id desc', limit=1) + \ No newline at end of file diff --git a/jikimo_workorder_exception_notify/tests/test_jikimo_workorder_exception_notify.py b/jikimo_workorder_exception_notify/tests/test_jikimo_workorder_exception_notify.py new file mode 100644 index 00000000..85d304be --- /dev/null +++ b/jikimo_workorder_exception_notify/tests/test_jikimo_workorder_exception_notify.py @@ -0,0 +1,62 @@ +import json +from datetime import datetime +from odoo.addons.jikimo_workorder_exception_notify.tests.common import TestJikimoWorkorderExceptionNotifyCommonNotify + +class TestJikimoWorkorderExceptionNotify(TestJikimoWorkorderExceptionNotifyCommonNotify): + + def test_create_message_template(self): + self.assertTrue(self.env['jikimo.message.template'].search([ + ('name', '=', '生产线无功能刀具提醒'), + ('model', '=', 'jikimo.workorder.exception') + ])) + self.assertTrue(self.env['jikimo.message.template'].search([ + ('name', '=', '工单无定位数据提醒'), + ('model', '=', 'jikimo.workorder.exception') + ])) + self.assertTrue(self.env['jikimo.message.template'].search([ + ('name', '=', '加工失败'), + ('model', '=', 'jikimo.workorder.exception') + ])) + + + def test_create_message_queue(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0002', + 'exception_content': '无功能刀具' + }) + + bussiness_node = self.env['jikimo.message.bussiness.node'].search([ + ('name', '=', '无功能刀具'), + ('model', '=', 'jikimo.workorder.exception') + ]) + + message_template = self.env['jikimo.message.template'].search([ + ('bussiness_node_id', '=', bussiness_node.id), + ('model', '=', 'jikimo.workorder.exception') + ]) + + message_record = self.env['jikimo.message.queue'].search([ + ('res_id', '=', exception_record.id), + ('model', '=', 'jikimo.workorder.exception'), + ('message_status', '=', 'pending'), + ('message_template_id', '=', message_template.id) + ]) + self.assertTrue(message_record) + + + def test_get_message(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0002', + 'exception_content': '无功能刀具' + }) + message_queue_ids = self.env['jikimo.message.queue'].search([ + ('res_id', '=', exception_record.id), + ('model', '=', 'jikimo.workorder.exception'), + ('message_status', '=', 'pending') + ]) + message = self.env['jikimo.workorder.exception']._get_message(message_queue_ids) + self.assertTrue(message) + + diff --git a/quality_control/views/quality_views.xml b/quality_control/views/quality_views.xml index f7a1a3dc..fbd917b2 100644 --- a/quality_control/views/quality_views.xml +++ b/quality_control/views/quality_views.xml @@ -1033,7 +1033,7 @@ name="Overview" action="quality_alert_team_action" parent="menu_quality_root" - sequence="5"/> + sequence="5" active="False"/> 0 and cls not in cls._sub_classes: + cls.__bases__ = (cls._sub_classes[-1],) + if cls not in cls._sub_classes: + cls._sub_classes.append(cls) diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index 48144622..2a5e4d3e 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -189,6 +189,7 @@ class Manufacturing_Connect(http.Controller): request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write( {'actual_start_time': workorder.date_start, 'state': 'processing'}) + res.update({'workorder_id': workorder.id}) except Exception as e: res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} @@ -595,14 +596,6 @@ class Manufacturing_Connect(http.Controller): if panel_workorder: panel_workorder.write({'production_line_state': '已下产线'}) workorder.write({'state': 'to be detected'}) - # workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( - # [ - # ('rfid_code', '=', rfid_code), ('type', '=', '下产线'), - # ('production_id', '=', order.production_id.id), - # ('workorder_id', '=', order.id), - # ('workorder_state', '=', 'done')]) - # if workpiece_delivery: - # delivery_Arr.append(workpiece_delivery.id) else: res = {'Succeed': False, 'ErrorCode': 204, 'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']} @@ -695,4 +688,4 @@ class Manufacturing_Connect(http.Controller): except Exception as e: res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)} logging.info('AGVDownProduct error:%s' % e) - return json.JSONEncoder().encode(res) \ No newline at end of file + return json.JSONEncoder().encode(res) diff --git a/sf_manufacturing/models/mrp_routing_workcenter.py b/sf_manufacturing/models/mrp_routing_workcenter.py index 0c380ebd..a584379f 100644 --- a/sf_manufacturing/models/mrp_routing_workcenter.py +++ b/sf_manufacturing/models/mrp_routing_workcenter.py @@ -40,7 +40,7 @@ class ResMrpRoutingWorkcenter(models.Model): def get_company_id(self): self.company_id = self.env.user.company_id.id - company_id = fields.Many2one('res.company', compute="get_company_id", related=False) + company_id = fields.Many2one('res.company', compute="get_company_id", related=False, store=True) # 排产的时候, 根据坯料的长宽高比对一下机床的最大加工尺寸.不符合就不要分配给这个加工中心(机床). # 工单对应的工作中心,根据工序中的工作中心去匹配, diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index eb613c75..e896ca40 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -59,7 +59,7 @@ class ResMrpWorkOrder(models.Model): compute='_compute_state', store=True, default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True) - # state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True) + delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效') @api.depends('production_id.manual_quotation') def _compute_manual_quotation(self): diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index 4664db03..670dea7c 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -351,8 +351,11 @@ sequence + delivery_warning == 'warning' + delivery_warning == 'overdue' + fullscreen--> current [('state', '!=', 'cancel'),('schedule_state', '=', '已排')] - {'search_default_product': 1, 'search_default_workcenter_id': active_id} + {'search_default_product': 1, 'search_default_workcenter_id': + active_id,'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1} +

没有工单要做! @@ -244,7 +246,7 @@ - @@ -283,6 +285,8 @@ + + @@ -616,6 +620,12 @@ + + + + + + diff --git a/sf_message/__manifest__.py b/sf_message/__manifest__.py index 416b2641..2e8dfd30 100644 --- a/sf_message/__manifest__.py +++ b/sf_message/__manifest__.py @@ -11,11 +11,12 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'mrp'], + 'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality', 'mrp'], 'data': [ 'data/bussiness_node.xml', - # 'data/cron_data.xml', + 'data/cron_data.xml', 'data/template_data.xml', + 'security/ir.model.access.csv', ], 'test': [ diff --git a/sf_message/controllers/__init__.py b/sf_message/controllers/__init__.py index deec4a8b..12a7e529 100644 --- a/sf_message/controllers/__init__.py +++ b/sf_message/controllers/__init__.py @@ -1 +1 @@ -from . import main \ No newline at end of file +from . import main diff --git a/sf_message/controllers/main.py b/sf_message/controllers/main.py index ab97816b..11e776dc 100644 --- a/sf_message/controllers/main.py +++ b/sf_message/controllers/main.py @@ -15,12 +15,13 @@ class MessageSfMrsConnect(Sf_Mrs_Connect): def get_cnc_processing_create(self, **kw): res = super(MessageSfMrsConnect, self).get_cnc_processing_create(**kw) res = json.loads(res) + _logger.info('已进入消息推送:%s' % res) if res.get('production_ids'): try: _logger.info('已编程的制造订单:%s' % res.get('production_ids')) productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))]) # 过滤programming_state为已编程,tool_state为2的制造订单 - tool_state_valid_productions = productions.filtered(lambda x: x.programming_state == '已编程' and x.tool_state == '2') + tool_state_valid_productions = productions.filtered(lambda x: x.tool_state == '2') if tool_state_valid_productions: data = { 'name': tool_state_valid_productions[0].programming_no @@ -38,3 +39,22 @@ class MessageSfMrsConnect(Sf_Mrs_Connect): _logger.info('无效用刀异常消息推送接口:%s' % e) return json.JSONEncoder().encode(res) + @http.route('/api/maintenance_logs/notify', type='json', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") + def maintenance_logs_notify(self, **kw): + res = {'code': 200, 'message': '设备故障日志信息推送成功'} + datas = request.httprequest.data + ret = json.loads(datas) + log_id = ret.get('log_id') + if not log_id: + res = {'code': 400, 'message': '设备故障日志id不能为空'} + return json.JSONEncoder().encode(res) + try: + if not isinstance(log_id, list): + log_id = [log_id] + maintenance_logs = request.env['sf.maintenance.logs'].sudo().search([('id', 'in', [int(id) for id in log_id])]) + if maintenance_logs: + maintenance_logs.add_queue('设备故障') + except Exception as e: + res = {'code': 400, 'message': '设备故障信息推送失败', 'error': str(e)} + return json.JSONEncoder().encode(res) + diff --git a/sf_message/data/bussiness_node.xml b/sf_message/data/bussiness_node.xml index 66b24aa7..a7123d44 100644 --- a/sf_message/data/bussiness_node.xml +++ b/sf_message/data/bussiness_node.xml @@ -12,15 +12,16 @@ sale.order - - - - + + 销售订单逾期预警 + sale.order + + + + 销售订单已逾期 + sale.order + - - - - 调拨入库 @@ -66,33 +67,53 @@ - - - - - - - - - - - - + + 装夹预调工单逾期预警 + mrp.workorder + + + 装夹预调工单已逾期 + mrp.workorder + - - - - + + CNC加工工单逾期预警 + mrp.workorder + + + CNC工单已逾期 + mrp.workorder + - - - - + + 解除装夹工单逾期预警 + mrp.workorder + - - - - + + 解除装夹工单已逾期 + mrp.workorder + + + + 表面工艺工单逾期预警 + mrp.workorder + + + + 表面工艺工单已逾期 + mrp.workorder + + + + 待质量判定 + quality.cnc.test + + + + 设备故障 + sf.maintenance.logs + \ No newline at end of file diff --git a/sf_message/data/cron_data.xml b/sf_message/data/cron_data.xml index f69dce7b..f095c9bd 100644 --- a/sf_message/data/cron_data.xml +++ b/sf_message/data/cron_data.xml @@ -1,24 +1,11 @@ - 销售订单逾期预警 + 检查销售订单是否已逾期预警和逾期 code - model._overdue_warning_func() - 1 - minutes - -1 - - - - - - - 销售订单已逾期 - - code - model._overdue_func() - 1 + model._overdue_or_warning_func() + 10 minutes -1 @@ -27,11 +14,11 @@ - 装夹预调工单逾期预警 + 检查工单是否已逾期预警和逾期 code - model._overdue_warning_func() - 1 + model._overdue_or_warning_func() + 10 minutes -1 @@ -39,122 +26,17 @@ - - 工单已逾期 + + 检查工单是否完成并恢复正常时效 code - model._overdue_func() - 1 + model._recover_time_warning_func() + 10 minutes -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sf_message/data/template_data.xml b/sf_message/data/template_data.xml index fd5ba389..057dc009 100644 --- a/sf_message/data/template_data.xml +++ b/sf_message/data/template_data.xml @@ -2,7 +2,7 @@ - + 待接单 sale.order @@ -16,7 +16,7 @@ - 确认接单 + 待排程提醒 sale.order @@ -27,6 +27,31 @@ 事项:{{mrp_production_count}}个制造订单待计划排程 + + + 销售订单逾期预警 + + sale.order + + markdown + normal + ### 销售订单逾期预警 +事项:共有[{{warning_num}}]({{url}})个销售订单有逾期风险 + + + + + 销售订单已逾期 + + sale.order + + markdown + urgent + ### 销售订单已逾期提醒 +事项:共有[{{overdue_num}}]({{url}})个销售订单已逾期 + + + 坯料采购提醒 @@ -63,6 +88,102 @@ 事项:共{{number}}个工单已下发,请查收知悉 + + 装夹预调工单逾期预警 + + mrp.workorder + + markdown + timing + normal + ### 工单逾期预警 +事项:共有[{{warning_num}}]({{url}})工单有逾期风险 + + + + 装夹预调工单已逾期 + + mrp.workorder + + markdown + timing + normal + ### 工单已逾期提醒 +事项:共有[{{overdue_num}}]({{url}})工单已逾期 + + + + CNC加工工单逾期预警 + + mrp.workorder + + markdown + timing + normal + ### 工单逾期预警 +事项:共有[{{warning_num}}]({{url}})工单有逾期风险 + + + + CNC加工工单已逾期 + + mrp.workorder + + markdown + timing + normal + ### 工单已逾期提醒 +事项:共有[{{overdue_num}}]({{url}})工单已逾期 + + + + 解除装夹工单逾期预警 + + mrp.workorder + + markdown + timing + normal + ### 工单逾期预警 +事项:共有[{{warning_num}}]({{url}})工单有逾期风险 + + + + 解除装夹工单已逾期 + + mrp.workorder + + markdown + timing + normal + ### 工单已逾期提醒 +事项:共有[{{overdue_num}}]({{url}})工单已逾期 + + + + 表面工艺工单逾期预警 + + mrp.workorder + + markdown + timing + normal + ### 工单逾期预警 +事项:共有[{{warning_num}}]({{url}})工单有逾期风险 + + + + 表面工艺工单已逾期 + + mrp.workorder + + markdown + timing + normal + ### 工单已逾期提醒 +事项:共有[{{overdue_num}}]({{url}})工单已逾期 + + @@ -132,5 +253,27 @@ 单号:发料出库单[{{name}}]({{request_url}}) 事项:销售订单{{sale_order_name}}已全部产出并入库,请及时发货 + + + 待质量判定 + + quality.cnc.test + + markdown + normal + ### 待质量判定提醒 +事项:共有[{{judge_num}}]({{url}})个工单需判定质量结果 + + + 设备故障 + + sf.maintenance.logs + + markdown + urgent + ### 设备故障及异常提醒: +机台号:[{{maintenance_equipment_id.name}}]({{url}}) +事项:{{create_date}}故障报警 + \ No newline at end of file diff --git a/sf_message/models/__init__.py b/sf_message/models/__init__.py index 2ff5461b..0b9c9ad6 100644 --- a/sf_message/models/__init__.py +++ b/sf_message/models/__init__.py @@ -8,3 +8,5 @@ from . import sf_message_purchase from . import sf_message_workorder from . import sf_message_functional_tool_dismantle from . import sf_message_mrp_production +from . import sf_message_quality_cnc_test +from . import sf_message_maintenance_logs diff --git a/sf_message/models/sf_message_maintenance_logs.py b/sf_message/models/sf_message_maintenance_logs.py new file mode 100644 index 00000000..288043ff --- /dev/null +++ b/sf_message/models/sf_message_maintenance_logs.py @@ -0,0 +1,22 @@ +from odoo import models, fields, api + +class SFMessageMaintenanceLogs(models.Model): + _name = 'sf.maintenance.logs' + _inherit = ['sf.maintenance.logs', 'jikimo.message.dispatch'] + + @api._model_create_multi + def create(self, vals_list): + res = super(SFMessageMaintenanceLogs, self).create(vals_list) + for rec in res: + rec.add_queue() + return res + + def _get_message(self, message_queue_ids): + contents = super(SFMessageMaintenanceLogs, self)._get_message(message_queue_ids) + url = self.env['ir.config_parameter'].get_param('web.base.url') + action_id = self.env.ref('sf_maintenance.action_maintenance_logs').id + for index, content in enumerate(contents): + maintenance_logs_id = self.env['sf.maintenance.logs'].browse(message_queue_ids[index].res_id) + url = url + '/web#id=%s&view_type=form&action=%s' % (maintenance_logs_id.id, action_id) + contents[index] = content.replace('{{url}}', url) + return contents \ No newline at end of file diff --git a/sf_message/models/sf_message_quality_cnc_test.py b/sf_message/models/sf_message_quality_cnc_test.py new file mode 100644 index 00000000..0ddc1391 --- /dev/null +++ b/sf_message/models/sf_message_quality_cnc_test.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +import logging +from datetime import datetime, timedelta +from odoo import models, fields, api, _ + + +class SFMessageQualityCncTest(models.Model): + _name = 'quality.cnc.test' + _inherit = ['quality.cnc.test', 'jikimo.message.dispatch'] + + def create(self, vals_list): + res = super(SFMessageQualityCncTest, self).create(vals_list) + if res: + try: + logging.info('add_queue res:%s' % res) + res.add_queue('待质量判定') + except Exception as e: + logging.info('add_queue error:%s' % e) + return res + + # 继承并重写jikimo.message.dispatch的_get_message() + def _get_message(self, message_queue_ids): + contents = [] + url = self.env['ir.config_parameter'].get_param('web.base.url') + i = 0 + for item in message_queue_ids: + if item.message_template_id.bussiness_node_id.name == '待质量判定': + content = item.message_template_id.content + i += 1 + if i >= 1: + action_id = self.env.ref('sf_quality.action_quality_cnc_test').id + url_with_id = f"{url}/web#view_type=list&action={action_id}" + content_template = content.replace('{{judge_num}}', str(i)) + content_template = content_template.replace('{{url}}', url_with_id) + contents.append(content_template) + return contents diff --git a/sf_message/models/sf_message_sale.py b/sf_message/models/sf_message_sale.py index d684cf7d..13a5f796 100644 --- a/sf_message/models/sf_message_sale.py +++ b/sf_message/models/sf_message_sale.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import logging +from datetime import datetime, timedelta from odoo import models, fields, api, _ @@ -12,6 +13,7 @@ class SFMessageSale(models.Model): res = super(SFMessageSale, self).create(vals_list) if res: try: + logging.info('add_queue res:%s' % res) res.add_queue('待接单') except Exception as e: logging.info('add_queue error:%s' % e) @@ -42,16 +44,20 @@ class SFMessageSale(models.Model): # 继承并重写jikimo.message.dispatch的_get_message() def _get_message(self, message_queue_ids): contents = [] + bussiness_node = None url = self.env['ir.config_parameter'].get_param('web.base.url') + current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + current_time = self.env['sf.sync.common'].sudo().get_add_time(current_time_strf) + current_time_datetime = datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') + time_range = timedelta(minutes=2) + i = 0 for item in message_queue_ids: - # 待接单的处理 if item.message_template_id.bussiness_node_id.name == '待接单': content = super(SFMessageSale, self)._get_message(item) action_id = self.env.ref('sale.action_quotations_with_onboarding').id - url = f"{url}/web#id={item.res_id}&view_type=form&action={action_id}" - content = content[0].replace('{{url}}', url) + url_with_id = f"{url}/web#id={item.res_id}&view_type=form&action={action_id}" + content = content[0].replace('{{url}}', url_with_id) contents.append(content) - # 确认接单的处理 elif item.message_template_id.bussiness_node_id.name == '确认接单': content = super(SFMessageSale, self)._get_message(item) sale_order_line = self.env['sale.order.line'].search([('order_id', '=', int(item.res_id))]) @@ -59,16 +65,77 @@ class SFMessageSale(models.Model): sale_order_line[ 0].product_id.name action_id = self.env.ref('sf_plan.sf_production_plan_action1').id - url = f"{url}/web#view_type=list&action={action_id}" - content = content[0].replace('{{product_id}}', product).replace('{{url}}', url) + url_with_id = f"{url}/web#view_type=list&action={action_id}" + content = content[0].replace('{{product_id}}', product).replace('{{url}}', url_with_id) contents.append(content) + elif item.message_template_id.bussiness_node_id.name in ['销售订单逾期预警', '销售订单已逾期']: + bussiness_node = item.message_template_id.bussiness_node_id.name + for reminder_time in item.message_template_id.reminder_time_ids: + content = item.message_template_id.content + target_time = datetime.combine(current_time_datetime.date(), datetime.min.time()).replace( + hour=reminder_time.time_point, + minute=0, + second=0, + microsecond=0 + ) + logging.info(current_time) + logging.info(target_time) + if target_time - time_range <= current_time_datetime <= target_time + time_range: + search_condition = [ + ('delivery_warning', '=', 'warning')] if bussiness_node == '销售订单逾期预警' else [ + ('delivery_warning', '=', 'overdue')] + record = self.sudo().search(search_condition + [('id', '=', int(item.res_id))]) + if record: + i += 1 + if i >= 1: + action_id = self.env.ref('sale.action_orders').id + url_with_id = f"{url}/web#view_type=list&action={action_id}" + content_template = content.replace('{{url}}', url_with_id) + if bussiness_node == '销售订单逾期预警': + content = content_template.replace('{{warning_num}}', str(i)) + elif bussiness_node == '销售订单已逾期': + content = content_template.replace('{{overdue_num}}', str(i)) + contents.append(content) return contents - # # 销售订单逾期预警 - # def _overdue_warning_func(self): - # sale_order_ - # return 1 - # - # # 销售订单已逾期 - # def _overdue_func(self): - # return 1 + # # 销售订单逾期预警和已逾期 + def _overdue_or_warning_func(self): + today = datetime.today().date() + deadline_check = today + timedelta(days=1) + logging.info(f"today: {today}, deadline_check: {deadline_check}") + sale_order = self.sudo().search([('state', 'in', ['sale']), ('deadline_of_delivery', '!=', False)]) + for item in sale_order: + production = self.env['mrp.production'].search([('origin', '=', item.name)]) + production_not_done = production.filtered(lambda p: p.state not in ['done', 'scrap', 'cancel']) + production_done_count = len(production.filtered(lambda p: p.state in ['done', 'scrap', 'cancel'])) + if len(production_not_done) != item.mrp_production_count: + if deadline_check == item.deadline_of_delivery: + item.delivery_warning = 'warning' + elif today == item.deadline_of_delivery: + item.delivery_warning = 'overdue' + elif production_done_count == item.mrp_production_count: + if item.delivery_status in ['pending', 'partial']: + if deadline_check == item.deadline_of_delivery: + item.delivery_warning = 'warning' + elif today == item.deadline_of_delivery: + item.delivery_warning = 'overdue' + else: + continue + overdue_orders = self.sudo().search([('delivery_warning', 'in', ['warning', 'overdue'])]) + for wo in overdue_orders: + message_template = self.env["jikimo.message.template"].search([ + ("model", "=", self._name), + ("bussiness_node_id", "=", self.env.ref('sf_message.bussiness_sale_order_overdue_warning').id) + ]) + sale_order_has = self.env['jikimo.message.queue'].search([ + ('res_id', '=', wo.id), + ('message_status', '=', 'pending'), + ('message_template_id', '=', message_template.id) + ]) + if not sale_order_has: + if wo.delivery_warning == 'warning': + wo.add_queue('销售订单逾期预警') + elif wo.delivery_warning == 'overdue': + wo.add_queue('销售订单已逾期') + + diff --git a/sf_message/models/sf_message_template.py b/sf_message/models/sf_message_template.py index 56346c27..8d1a2b9d 100644 --- a/sf_message/models/sf_message_template.py +++ b/sf_message/models/sf_message_template.py @@ -14,4 +14,6 @@ class SfMessageTemplate(models.Model): res.append('sf.functional.tool.dismantle') res.append('purchase.order') res.append('mrp.workorder') + res.append('sf.maintenance.logs') + res.append('quality.cnc.test') return res diff --git a/sf_message/models/sf_message_workorder.py b/sf_message/models/sf_message_workorder.py index 59e3a0fc..505b6f93 100644 --- a/sf_message/models/sf_message_workorder.py +++ b/sf_message/models/sf_message_workorder.py @@ -1,3 +1,4 @@ +from datetime import datetime, timedelta from odoo import models, fields, api, _ import logging, json import requests @@ -6,6 +7,7 @@ from urllib.parse import urlencode _logger = logging.getLogger(__name__) + class SFMessageWork(models.Model): _name = 'mrp.workorder' _inherit = ['mrp.workorder', 'jikimo.message.dispatch'] @@ -23,6 +25,17 @@ class SFMessageWork(models.Model): def _get_message(self, message_queue_ids): contents = [] product_id = [] + bussiness_node = None + url = self.env['ir.config_parameter'].get_param('web.base.url') + current_time_strf = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + current_time = self.env['sf.sync.common'].sudo().get_add_time(current_time_strf) + current_time_datetime = datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') + time_range = timedelta(minutes=2) + template_names = { + '预警': ['装夹预调工单逾期预警', 'CNC加工工单逾期预警', '解除装夹工单逾期预警', '表面工艺工单逾期预警'], + '已逾期': ['装夹预调工单已逾期', 'CNC加工工单已逾期', '解除装夹工单已逾期', '表面工艺工单已逾期'] + } + i = 0 for message_queue_id in message_queue_ids: if message_queue_id.message_template_id.name == '工单已下发通知': content = message_queue_id.message_template_id.content @@ -37,6 +50,37 @@ class SFMessageWork(models.Model): '{{request_url}}', url) product_id.append(mrp_workorder_line.product_id.id) contents.append(content) + elif message_queue_id.message_template_id.name in template_names['预警'] + template_names['已逾期']: + item = message_queue_id.message_template_id + bussiness_node = item.bussiness_node_id.name + for reminder_time in item.reminder_time_ids: + content = item.content + target_time = datetime.combine(current_time_datetime.date(), datetime.min.time()).replace( + hour=reminder_time.time_point, + minute=0, + second=0, + microsecond=0 + ) + logging.info(current_time) + logging.info(target_time) + logging.info(target_time - time_range) + logging.info(target_time + time_range) + if target_time - time_range <= current_time_datetime <= target_time + time_range: + search_condition = [ + ('delivery_warning', '=', 'warning')] if bussiness_node in template_names['预警'] else [ + ('delivery_warning', '=', 'overdue')] + record = self.sudo().search(search_condition + [('id', '=', int(item.res_id))]) + if record: + i += 1 + if i >= 1: + action_id = self.env.ref('sf_manufacturing.mrp_workorder_action_tablet').id + url_with_id = f"{url}/web#view_type=list&action={action_id}" + content_template = content.replace('{{url}}', url_with_id) + if bussiness_node in template_names['预警']: + content = content_template.replace('{{warning_num}}', str(i)) + elif bussiness_node in template_names['已逾期']: + content = content_template.replace('{{overdue_num}}', str(i)) + contents.append(content) return contents def request_url(self): @@ -52,3 +96,63 @@ class SFMessageWork(models.Model): full_url = url + "/web#" + query_string return full_url + def _overdue_or_warning_func(self): + workorders = self.env['mrp.workorder'].search([("state", "in", ["ready", "progress", "to be detected"])]) + grouped_workorders = {} + for workorder in workorders: + routing_type = workorder.routing_type + if routing_type not in grouped_workorders: + grouped_workorders[routing_type] = [] + grouped_workorders[routing_type].append(workorder) + for routing_type, orders in grouped_workorders.items(): + print(f"Routing Type: {routing_type}, Orders: {len(orders)}") + for item in orders: + if item.date_planned_finished: + current_time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + current_time = self.env['sf.sync.common'].sudo().get_add_time(current_time_str) + current_time_datetime = datetime.strptime(current_time, '%Y-%m-%d %H:%M:%S') + date_planned_finished_str = self.env['sf.sync.common'].sudo().get_add_time( + item.date_planned_finished.strftime("%Y-%m-%d %H:%M:%S")) + date_planned_finished = datetime.strptime(date_planned_finished_str, '%Y-%m-%d %H:%M:%S') + twelve_hours_ago = current_time_datetime - timedelta(hours=12) + if current_time_datetime >= date_planned_finished: + logging.info("------overdue-------") + logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, " + f"Planned Finish: {date_planned_finished}") + item.delivery_warning = 'overdue' + elif twelve_hours_ago <= current_time_datetime <= date_planned_finished: + logging.info("------warning-------") + logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, " + f"Planned Finish: {date_planned_finished}") + item.delivery_warning = 'warning' + business_node_ids = { + '装夹预调': self.env.ref('sf_message.bussiness_mrp_workorder_pre_overdue_warning').id, + 'CNC加工': self.env.ref('sf_message.bussiness_mrp_workorder_cnc_overdue_warning').id, + '解除装夹': self.env.ref('sf_message.bussiness_mrp_workorder_unclamp_overdue_warning').id, + '表面工艺': self.env.ref('sf_message.bussiness_mrp_workorder_surface_overdue_warning').id, + } + message_templates = {key: self.env["jikimo.message.template"].sudo().search([ + ("model", "=", self._name), + ("bussiness_node_id", "=", business_node_ids[key]) + ]) for key in business_node_ids} + for item in orders: + if item.delivery_warning in ['overdue', 'warning']: + bussiness_node_id = business_node_ids.get(item.routing_type) + if bussiness_node_id and message_templates[item.routing_type]: + message_queue_ids = self.env["jikimo.message.queue"].sudo().search([ + ("message_template_id", "=", message_templates[item.routing_type].id), + ("message_status", "=", "pending"), + ("res_id", "=", item.id) + ]) + if not message_queue_ids: + overdue_message = '工单已逾期' if item.delivery_warning == 'overdue' else '工单逾期预警' + queue_method_name = f'add_queue' + # 构建参数列表,其中包含item.routing_type和overdue_message + args = [f'{item.routing_type}{overdue_message}'] + # 获取add_queue方法并调用它,传入参数列表 + getattr(item, queue_method_name)(*args) + + def _recover_time_warning_func(self): + workorder_done = self.env['mrp.workorder'].search([("state", "=", "done")]) + workorder_overdue = workorder_done.filtered(lambda x: x.delivery_warning in ['overdue', 'warning']) + workorder_overdue.write({'delivery_warning': 'normal'}) diff --git a/sf_message/security/ir.model.access.csv b/sf_message/security/ir.model.access.csv index cb42c911..05a7366c 100644 --- a/sf_message/security/ir.model.access.csv +++ b/sf_message/security/ir.model.access.csv @@ -1,22 +1,28 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_jikimo_message_template_group_sale_salemanager,jikimo_message_template,model_jikimo_message_template,sf_base.group_sale_salemanager,1,1,1,0 -access_jikimo_message_template_group_purchase,jikimo_message_template,model_jikimo_message_template,sf_base.group_purchase,1,1,1,0 -access_jikimo_message_template_group_sf_stock_user,jikimo_message_template,model_jikimo_message_template,sf_base.group_sf_stock_user,1,1,1,0 -access_jikimo_message_template_group_sf_order_user,jikimo_message_template,model_jikimo_message_template,sf_base.group_sf_order_user,1,1,1,0 -access_jikimo_message_template_group_sf_tool_user,jikimo_message_template,model_jikimo_message_template,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_template_group_sale_salemanager,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sale_salemanager,1,1,1,0 +access_jikimo_message_template_group_purchase,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_purchase,1,1,1,0 +access_jikimo_message_template_group_sf_stock_user,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sf_stock_user,1,1,1,0 +access_jikimo_message_template_group_sf_order_user,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sf_order_user,1,1,1,0 +access_jikimo_message_template_group_sf_tool_user,jikimo_message_template,jikimo_message_notify.model_jikimo_message_template,sf_base.group_sf_tool_user,1,1,1,0 -access_jikimo_message_bussiness_node_group_sale_salemanager,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sale_salemanager,1,1,1,0 -access_jikimo_message_bussiness_node_group_purchase,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_purchase,1,1,1,0 -access_jikimo_message_bussiness_node_group_sf_stock_user,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sf_stock_user,1,1,1,0 -access_jikimo_message_bussiness_node_group_sf_order_user,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sf_order_user,1,1,1,0 -access_jikimo_message_bussiness_node_group_sf_tool_user,jikimo_message_bussiness_node,model_jikimo_message_bussiness_node,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_bussiness_node_group_sale_salemanager,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sale_salemanager,1,1,1,0 +access_jikimo_message_bussiness_node_group_purchase,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_purchase,1,1,1,0 +access_jikimo_message_bussiness_node_group_sf_stock_user,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sf_stock_user,1,1,1,0 +access_jikimo_message_bussiness_node_group_sf_order_user,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sf_order_user,1,1,1,0 +access_jikimo_message_bussiness_node_group_sf_tool_user,jikimo_message_bussiness_node,jikimo_message_notify.model_jikimo_message_bussiness_node,sf_base.group_sf_tool_user,1,1,1,0 -access_jikimo_message_queue_group_sale_salemanager,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sale_salemanager,1,1,1,0 -access_jikimo_message_queue_group_purchase,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_purchase,1,1,1,0 -access_jikimo_message_queue_group_sf_stock_user,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sf_stock_user,1,1,1,0 -access_jikimo_message_queue_group_sf_order_user,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sf_order_user,1,1,1,0 -access_jikimo_message_queue_group_sf_tool_user,jikimo_message_queue,model_jikimo_message_queue,sf_base.group_sf_tool_user,1,1,1,0 +access_jikimo_message_queue_group_sale_salemanager,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sale_salemanager,1,1,1,0 +access_jikimo_message_queue_group_purchase,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_purchase,1,1,1,0 +access_jikimo_message_queue_group_sf_stock_user,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sf_stock_user,1,1,1,0 +access_jikimo_message_queue_group_sf_order_user,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sf_order_user,1,1,1,0 +access_jikimo_message_queue_group_sf_tool_user,jikimo_message_queue,jikimo_message_notify.model_jikimo_message_queue,sf_base.group_sf_tool_user,1,1,1,0 + +access_jikimo_message_reminder_time_group_sale_salemanager,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sale_salemanager,1,1,1,0 +access_jikimo_message_reminder_time_group_purchase,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_purchase,1,1,1,0 +access_jikimo_message_reminder_time_group_sf_stock_user,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sf_stock_user,1,1,1,0 +access_jikimo_message_reminder_time_group_sf_order_user,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sf_order_user,1,1,1,0 +access_jikimo_message_reminder_time_group_sf_tool_user,jikimo_message_reminder_time,jikimo_message_notify.model_jikimo_message_reminder_time,sf_base.group_sf_tool_user,1,1,1,0 diff --git a/sf_message/views/sf_message_sale_view.xml b/sf_message/views/sf_message_sale_view.xml new file mode 100644 index 00000000..6de01920 --- /dev/null +++ b/sf_message/views/sf_message_sale_view.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/sf_message/views/sf_message_template_view.xml b/sf_message/views/sf_message_template_view.xml deleted file mode 100644 index 21920b64..00000000 --- a/sf_message/views/sf_message_template_view.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - sf.message.template.view.form - message.template - -

- -
-
- - - - - - - - - -
-
-
- - - - sf.message.template.view.tree - message.template - - - - - - - - - - - - - - - sf.message.template.search.view - message.template - - - - - - - - - - - - 消息模板 - message.template - tree,form - - - - - - - \ No newline at end of file diff --git a/sf_mrs_connect/controllers/controllers.py b/sf_mrs_connect/controllers/controllers.py index cb56287a..35a7d3a3 100644 --- a/sf_mrs_connect/controllers/controllers.py +++ b/sf_mrs_connect/controllers/controllers.py @@ -3,11 +3,12 @@ import logging import os import json import base64 -from odoo import http +from odoo import http, fields, models from odoo.http import request +from odoo.addons.sf_base.controllers.controllers import MultiInheritController -class Sf_Mrs_Connect(http.Controller): +class Sf_Mrs_Connect(http.Controller, MultiInheritController): @http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, cors="*") @@ -81,18 +82,22 @@ class Sf_Mrs_Connect(http.Controller): if files_panel: for file in files_panel: file_extension = os.path.splitext(file)[1] - logging.info('file_extension:%s' % file_extension) if file_extension.lower() == '.pdf': panel_file_path = os.path.join(program_path_tmp_panel, file) logging.info('panel_file_path:%s' % panel_file_path) + logging.info('更新工作指令:%s' % cnc_workorder) cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) + logging.info('更新工作指令完成:%s' % cnc_workorder) pre_workorder = productions.workorder_ids.filtered( lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework' 'cancel'] and ap.processing_panel == panel) if pre_workorder: + logging.info('更新加工图纸:%s' % pre_workorder) pre_workorder.write( {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())}) + logging.info('更新加工图纸完成:%s' % pre_workorder) productions.write({'programming_state': '已编程', 'work_state': '已编程'}) + logging.info('已更新制造订单编程状态:%s' % productions.ids) res.update({ 'production_ids': productions.ids }) @@ -110,5 +115,6 @@ class Sf_Mrs_Connect(http.Controller): return json.JSONEncoder().encode(res) except Exception as e: res = {'status': -1, 'message': '系统解析失败'} + request.cr.rollback() logging.info('get_cnc_processing_create error:%s' % e) return json.JSONEncoder().encode(res) diff --git a/sf_quality/__manifest__.py b/sf_quality/__manifest__.py index c41e1a24..b1151b6d 100644 --- a/sf_quality/__manifest__.py +++ b/sf_quality/__manifest__.py @@ -13,10 +13,11 @@ 'author': 'jikimo', 'website': 'https://sf.cs.jikimo.com', # 此处依赖sf_manufacturing是因为我要重写其中的一个字段operation_id的string,故需要sf_manufacturing先安装 - 'depends': ['quality_control'], + 'depends': ['quality_control', 'web_widget_model_viewer', 'sf_manufacturing'], 'data': [ 'security/ir.model.access.csv', - 'views/view.xml' + 'views/view.xml', + 'views/quality_cnc_test_view.xml' ], 'assets': { diff --git a/sf_quality/models/__init__.py b/sf_quality/models/__init__.py index 71468786..248f7fb2 100644 --- a/sf_quality/models/__init__.py +++ b/sf_quality/models/__init__.py @@ -3,3 +3,4 @@ from . import custom_quality from . import quality +from . import quality_cnc_test diff --git a/sf_quality/models/quality_cnc_test.py b/sf_quality/models/quality_cnc_test.py new file mode 100644 index 00000000..cc39bd11 --- /dev/null +++ b/sf_quality/models/quality_cnc_test.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class SfQualityCncTest(models.Model): + _name = 'quality.cnc.test' + _description = 'CNC加工质检' + + name = fields.Char('单号', default=lambda self: self.env['ir.sequence'].next_by_code('quality.cnc.test')) + workorder_id = fields.Many2one('mrp.workorder') + production_id = fields.Many2one(related='workorder_id.production_id', string='制造订单') + product_id = fields.Many2one(related='workorder_id.product_id', string='产品') + model_file = fields.Binary(related='workorder_id.glb_file', string='加工模型') + processing_panel = fields.Char(related='workorder_id.processing_panel', string='加工面') + equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备') + production_line_id = fields.Many2one(related='workorder_id.production_line_id', + string='生产线') + part_number = fields.Char(related='workorder_id.part_number', string='成品零件图号') + detection_report = fields.Binary(related='workorder_id.detection_report', readonly=True, string='检测报告') + state = fields.Selection([ + ('waiting', '待判定'), + ('done', '已完成')], string='状态', default='waiting') + result = fields.Selection([ + ('pass', '合格'), + ('fail', '不合格')], string='判定结果') + number = fields.Integer('数量', default=1) + test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], string="检测结果") + reason = fields.Selection( + [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"), + ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因") + detailed_reason = fields.Text('详细原因') + + def submit_pass(self): + self.write({'result': 'pass', 'test_results': self.test_results, 'state': 'done'}) + self.workorder_id.write({'test_results': self.test_results}) + self.workorder_id.button_finish() + + def submit_fail(self): + if not self.reason and not self.detailed_reason and not self.test_results: + raise UserError(_('请填写【判定结果】里的信息')) + else: + self.write({'result': 'fail', 'test_results': self.test_results, 'state': 'done'}) + self.workorder_id.write( + {'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason}) + self.workorder_id.button_finish() + + +class SfQualityWorkOrder(models.Model): + _inherit = 'mrp.workorder' + + def button_finish(self): + super(SfQualityWorkOrder, self).button_finish() + if self.routing_type == 'CNC加工': + quality_cnc_test = self.env['quality.cnc.test'].search([('workorder_id', '=', self.id)]) + if quality_cnc_test: + quality_cnc_test.write({'result': 'fail' if self.test_results in ['返工', '报废'] else 'pass', + 'test_results': self.test_results, 'state': 'done', + 'reason': self.reason, + 'detailed_reason': self.detailed_reason, + 'detection_report': self.detection_report}) + + def write(self, vals): + res = super(SfQualityWorkOrder, self).write(vals) + for item in self: + if item.state == 'to be detected': + quality_cnc_test = self.env['quality.cnc.test'].search([('workorder_id', '=', item.id)]) + if not quality_cnc_test: + self.env['quality.cnc.test'].sudo().create({'workorder_id': item.id}) + return res diff --git a/sf_quality/security/ir.model.access.csv b/sf_quality/security/ir.model.access.csv index b147a1df..19818982 100644 --- a/sf_quality/security/ir.model.access.csv +++ b/sf_quality/security/ir.model.access.csv @@ -67,5 +67,11 @@ access_quality_alert_stage,quality.alert.stage,quality.model_quality_alert_stage access_stock_move_group_quality,stock_move_group_quality,stock.model_stock_move,sf_base.group_quality,1,1,0,0 access_stock_move_group_quality_director,stock_move_group_quality_director,stock.model_stock_move,sf_base.group_quality_director,1,1,0,0 +access_quality_cnc_test_group_quality,quality_cnc_test_group_quality,model_quality_cnc_test,sf_base.group_quality,1,1,0,0 +access_quality_cnc_test_group_quality_director,quality_cnc_test_group_quality_director,model_quality_cnc_test,sf_base.group_quality_director,1,1,0,0 + +access_quality_cnc_test_group_sf_equipment_user,quality_cnc_test_group_sf_equipment_user,model_quality_cnc_test,sf_base.group_sf_equipment_user,1,1,0,0 + + diff --git a/sf_quality/views/quality_cnc_test_view.xml b/sf_quality/views/quality_cnc_test_view.xml new file mode 100644 index 00000000..a9bceffe --- /dev/null +++ b/sf_quality/views/quality_cnc_test_view.xml @@ -0,0 +1,192 @@ + + + + + 加工质检单编码规则 + quality.cnc.test + QCT + 4 + + + + + quality.cnc.test.view.tree + quality.cnc.test + + + + + + + + + + + + + + + + + + search.quality.cnc.test + quality.cnc.test + + + + + + + + + + + + + + + + 加工质检 + quality.cnc.test + tree,form + { 'search_default_filter_waiting':1} + +

+ 暂无加工质检单 +

+
+
+ + + quality.cnc.test.form. + quality.cnc.test + +
+
+
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + quality.cnc.test.view.kanban + quality.cnc.test + + + + +
+
+
+
+ + + +
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ + + + + + 驾驶舱 + ir.actions.act_window + quality.cnc.test + kanban,form + + + [] + { 'search_default_filter_waiting':1} + +

+ 暂无加工质检单 +

+
+
+ + + +
diff --git a/sf_quality/views/view.xml b/sf_quality/views/view.xml index 1edbca05..1ed999d6 100644 --- a/sf_quality/views/view.xml +++ b/sf_quality/views/view.xml @@ -34,36 +34,36 @@
- - quality.point.form.inherit.sf - quality.point - - - - - - - custom_required - 1 - - - custom_required - 1 - - - + + quality.point.form.inherit.sf + quality.point + + + + + + + custom_required + 1 + + + custom_required + 1 + + + - - sf.quality.point.form.inherit.sf - quality.point - - - - custom_required - - - custom_required - - - + + sf.quality.point.form.inherit.sf + quality.point + + + + custom_required + + + custom_required + + + diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py index d19a3b02..f28a76c4 100644 --- a/sf_sale/models/sale_order.py +++ b/sf_sale/models/sale_order.py @@ -55,6 +55,9 @@ class ReSaleOrder(models.Model): store=True, readonly=False, copy=False, precompute=True, states=READONLY_FIELD_STATES, default=fields.Datetime.now) + delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效') + + # 业务平台分配工厂后在智能工厂先创建销售订单 def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address, deadline_of_delivery, payments_way, pay_way): diff --git a/sf_sale/views/sale_order_view.xml b/sf_sale/views/sale_order_view.xml index a5c7a6fa..61b53b08 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'])]}
- + @@ -106,6 +106,7 @@ + {'no_create': True} @@ -208,6 +209,20 @@ + + sale.order.message.search.view + sale.order + + primary + + + + + + + + + sale.order.tree sale.order @@ -217,18 +232,15 @@ create_date desc False + delivery_warning == 'warning' + delivery_warning == 'overdue' 订单号 - - - - - - - - + + + @@ -253,10 +265,18 @@ + {"search_default_categ_id":1, "search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1} + + + + + { 'search_default_filter_order_warning':1,'search_default_filter_order_overdue':1} + + \ No newline at end of file diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index c6a04196..237d5b91 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -234,7 +234,7 @@ class CAMWorkOrderProgramKnifePlan(models.Model): sf_functional_tool_assembly_id = fields.Many2one('sf.functional.tool.assembly', '功能刀具组装', readonly=True) - active = fields.Boolean(string='已归档', default=True) + active = fields.Boolean(string='已归档', default=True, groups='base.user_root') @api.depends('functional_tool_name') def _compute_tool_number(self): @@ -1279,7 +1279,7 @@ class FunctionalToolDismantle(models.Model): item.picking_num = 0 state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True) - active = fields.Boolean('有效', default=True) + active = fields.Boolean('有效', default=True, groups='base.user_root') # 刀柄 handle_product_id = fields.Many2one('product.product', string='刀柄', compute='_compute_functional_tool_num', diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py index 55a87339..e8523cd3 100644 --- a/sf_tool_management/models/functional_tool.py +++ b/sf_tool_management/models/functional_tool.py @@ -402,7 +402,7 @@ class FunctionalToolWarning(models.Model): dispose_time = fields.Char('处理时间', readonly=True) dispose_func = fields.Char('处理方法/措施', readonly=True) - active = fields.Boolean(string='已归档', default=True) + active = fields.Boolean(string='已归档', default=True, groups='base.user_root') functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称') diff --git a/sf_tool_management/models/mrp_workorder.py b/sf_tool_management/models/mrp_workorder.py index 96198cbb..9326b511 100644 --- a/sf_tool_management/models/mrp_workorder.py +++ b/sf_tool_management/models/mrp_workorder.py @@ -194,8 +194,8 @@ class MrpProduction(models.Model): # 自动调用重新获取编程的方法 logging.info('cnc用刀校验到无效刀自动调用重新编程方法:update_programming_state()') self[0].update_programming_state() - # 修改制造订单 编程状态变为“编程中” - self.write({'programming_state': '编程中', 'work_state': '编程中'}) + # 修改制造订单 编程状态变为“编程中” 制造订单状态为‘返工’ + self.write({'programming_state': '编程中', 'work_state': '编程中', 'state': 'rework'}) if missing_tool_1: # 修改 修改cnc程序的‘刀具状态’ 为 ‘缺刀’ cnc_ids = self.env['sf.cnc.processing'].sudo().search(