diff --git a/jikimo_frontend/static/src/bye_odoo/favicon.js b/jikimo_frontend/static/src/bye_odoo/favicon.js index 9ef4f4da..e37b967d 100644 --- a/jikimo_frontend/static/src/bye_odoo/favicon.js +++ b/jikimo_frontend/static/src/bye_odoo/favicon.js @@ -6,6 +6,6 @@ import { patch } from "web.utils"; patch(WebClient.prototype, "kolpolok_custom_title_and_favicon.WebClient", { setup() { this._super(); - this.title.setParts({ zopenerp: "JIKIMO" }); + // this.title.setParts({ zopenerp: "JIKIMO" }); }, }); \ No newline at end of file diff --git a/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.css b/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.css index df6cdfb6..edaa2bb4 100644 --- a/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.css +++ b/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.css @@ -1,41 +1,99 @@ -.zoomed { - position: fixed !important; - top: 50%; - left: 50%; - transform: translate(-50%, -50%) scale(10); + +.processing-capabilities-grid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 10px; + width: 100%; } -.many2many_flex { - display: flex; +.grid-item { + display: flex; + align-items: center; } -.many2many_flex>div { - margin-right: 15px; - display: flex; - flex-direction: column; - align-items: center; +.item-content { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} +/*控制图片大小*/ +.item-icon { + width: 50px; + height: 50px; + margin-bottom: 5px; } -.many2many_flex>div>:nth-child(2) { - position: relative; +.item-label { + font-size: 12px; + word-break: break-word; } -.close { - width: 20px; - height: 20px; - position: absolute; - top: -8.8px; - right: -8.8px; - color: #fff; - background-color: #000; - opacity: 0; - text-align: center; - line-height: 20px; - font-size: 18px; +@media (max-width: 1200px) { + .processing-capabilities-grid { + grid-template-columns: repeat(4, 1fr); + } } -.img_close { - opacity: 1; - transform: scale(0.1); - cursor: pointer; +@media (max-width: 768px) { + .processing-capabilities-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (max-width: 480px) { + .processing-capabilities-grid { + grid-template-columns: repeat(2, 1fr); + } +} +.image-preview-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.9); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + opacity: 0; + transition: opacity 0.3s ease; +} + +.image-preview-container.show { + opacity: 1; +} + +.image-preview { + max-width: 90%; + max-height: 90%; + object-fit: contain; + box-shadow: 0 0 20px rgba(255, 255, 255, 0.2); + border-radius: 5px; + transform: scale(0.9); + transition: transform 0.3s ease; +} + +.image-preview-container.show .image-preview { + transform: scale(1); +} + +.image-preview-close { + position: absolute; + top: 20px; + right: 30px; + color: #fff; + font-size: 40px; + font-weight: bold; + transition: 0.3s; + cursor: pointer; + opacity: 0.7; +} + +.image-preview-close:hover, +.image-preview-close:focus { + opacity: 1; + text-decoration: none; + cursor: pointer; } \ No newline at end of file diff --git a/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.js b/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.js index a3053b72..dee78c5f 100644 --- a/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.js +++ b/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.js @@ -4,35 +4,57 @@ import {Many2ManyCheckboxesField} from "@web/views/fields/many2many_checkboxes/m import {registry} from "@web/core/registry"; export class MyCustomWidget extends Many2ManyCheckboxesField { - // 你可以重写或者添加一些方法和属性 - // 例如,你可以重写setup方法来添加一些事件监听器或者初始化一些变量 setup() { - super.setup(); // 调用父类的setup方法 - // 你自己的代码 + super.setup(); } - onImageClick(event) { - // 放大图片逻辑 - // 获取图片元素 - const img = event.target; - const close = img.nextSibling; + onImageClick(event, src) { + event.preventDefault(); + event.stopPropagation(); - // 实现放大图片逻辑 - // 比如使用 CSS 放大 - img.parentElement.classList.add('zoomed'); - close.classList.add('img_close'); - } + // 创建预览框 + const previewContainer = document.createElement('div'); + previewContainer.className = 'image-preview-container'; - onCloseClick(event) { - const close = event.target; - const img = close.previousSibling; - img.parentElement.classList.remove('zoomed'); - close.classList.remove('img_close'); + const previewImg = document.createElement('img'); + previewImg.src = src; + previewImg.className = 'image-preview'; + // 设置放大的预览图片大小 + previewImg.style.width = '600px'; + previewImg.style.height = 'auto'; // 保持宽高比 + + const closeButton = document.createElement('span'); + closeButton.innerHTML = '×'; + closeButton.className = 'image-preview-close'; + + previewContainer.appendChild(previewImg); + previewContainer.appendChild(closeButton); + document.body.appendChild(previewContainer); + + // 添加关闭预览的事件监听器 + const closePreview = () => { + previewContainer.classList.remove('show'); + setTimeout(() => { + document.body.removeChild(previewContainer); + }, 300); + }; + + closeButton.addEventListener('click', closePreview); + + // 点击预览框外部也可以关闭 + previewContainer.addEventListener('click', (e) => { + if (e.target === previewContainer) { + closePreview(); + } + }); + + // 使用 setTimeout 来触发过渡效果 + setTimeout(() => { + previewContainer.classList.add('show'); + }, 10); } } MyCustomWidget.template = "jikimo_frontend.MyCustomWidget"; -// MyCustomWidget.supportedTypes = ['many2many']; - -registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget); +registry.category("fields").add("custom_many2many_checkboxes", MyCustomWidget); \ No newline at end of file diff --git a/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.xml b/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.xml index bebae03b..9bb8797d 100644 --- a/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.xml +++ b/jikimo_frontend/static/src/fields/custom_many2many_checkboxes/custom_many2many_checkboxes.xml @@ -2,27 +2,22 @@ -
+
-
+
- - +
+ + +
-
- - -
×
-
-
-
- + \ No newline at end of file diff --git a/jikimo_frontend/static/src/scss/custom_style.scss b/jikimo_frontend/static/src/scss/custom_style.scss index d7e6414c..8eb76259 100644 --- a/jikimo_frontend/static/src/scss/custom_style.scss +++ b/jikimo_frontend/static/src/scss/custom_style.scss @@ -108,6 +108,10 @@ td.o_required_modifier { } .color_3 { + background-color: #808080; +} + +.color_4 { background-color: rgb(255, 150, 0); } diff --git a/jikimo_frontend/views/bye_odoo.xml b/jikimo_frontend/views/bye_odoo.xml index 14fd211b..d75ddd06 100644 --- a/jikimo_frontend/views/bye_odoo.xml +++ b/jikimo_frontend/views/bye_odoo.xml @@ -16,7 +16,7 @@ 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..03744ff9 --- /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) + if not ret.get('RfidCode') or not ret.get('coding'): + 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('coding'), + '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..92886ff3 --- /dev/null +++ b/jikimo_workorder_exception_notify/tests/test_jikimo_workorder_exception_notify.py @@ -0,0 +1,113 @@ +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_yc0001(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0001', + 'exception_content': '无CNC程序' + }) + + message_record = self.env['jikimo.message.queue'].search([ + ('res_id', '=', exception_record.id), + ('model', '=', 'jikimo.workorder.exception'), + ('message_status', '=', 'pending') + ]) + self.assertFalse(message_record) + + def test_create_message_queue_yc0002(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_create_message_queue_yc0003(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0003', + '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_create_message_queue_yc0004(self): + exception_record = self.env['jikimo.workorder.exception'].create({ + 'workorder_id': self.workorder.id, + 'exception_code': 'YC0004', + 'exception_content': '无CNC程序' + }) + + message_record = self.env['jikimo.message.queue'].search([ + ('res_id', '=', exception_record.id), + ('model', '=', 'jikimo.workorder.exception'), + ('message_status', '=', 'pending') + ]) + self.assertFalse(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_base/views/base_view.xml b/sf_base/views/base_view.xml index a41d3a2b..8127efb7 100644 --- a/sf_base/views/base_view.xml +++ b/sf_base/views/base_view.xml @@ -109,7 +109,7 @@ form.sf.machine_tool.type sf.machine_tool.type -
+

@@ -129,31 +129,28 @@ - +
- + + + + - - - - - - + +
- + - - - + + + - - + +
@@ -178,7 +175,7 @@

- +
-
-
-
+
+
+
+

- + t-attf-class="#{record.run_status.raw_value == '运行中' ? 'font_color_1' : ''} + #{record.run_status.raw_value == '待机' ? 'font_color_4' : ''} + #{record.run_status.raw_value == '故障' ? 'font_color_2' : ''} + #{record.run_status.raw_value == '离线' ? 'font_color_3' : ''}"> +

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_production.py b/sf_manufacturing/models/mrp_production.py index 4eac6f54..6b75ef53 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -130,10 +130,12 @@ class MrpProduction(models.Model): ], string='工序状态', default='待装夹') # 零件图号 - part_number = fields.Char('零件图号') + part_number = fields.Char('零件图号', readonly=True) # 上传零件图纸 - part_drawing = fields.Binary('零件图纸') + part_drawing = fields.Binary('零件图纸', readonly=True) + + quality_standard = fields.Binary('质检标准', readonly=True) @api.depends('product_id.manual_quotation') def _compute_manual_quotation(self): @@ -308,8 +310,13 @@ class MrpProduction(models.Model): # 编程单更新 def update_programming_state(self): try: + manufacturing_type = 'rework' + if self.is_scrap: + manufacturing_type = 'scrap' + elif self.tool_state == '2': + manufacturing_type = 'invalid_tool_rework' res = {'programming_no': self.programming_no, - 'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'} + 'manufacturing_type': manufacturing_type} logging.info('res=%s:' % res) configsettings = self.env['res.config.settings'].get_values() config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) @@ -955,6 +962,8 @@ class MrpProduction(models.Model): if production.programming_no in program_to_production_names: productions_not_delivered = self.env['mrp.production'].search( [('programming_no', '=', production.programming_no), ('programming_state', '=', '已编程未下发')]) + productions = self.env['mrp.production'].search( + [('programming_no', '=', production.programming_no), ('state', 'not in', ('cancel', 'done'))]) rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework') if rework_workorder: for rework_item in rework_workorder: @@ -967,6 +976,13 @@ class MrpProduction(models.Model): productions_not_delivered.write( {'state': 'progress', 'programming_state': '已编程', 'is_rework': False}) + # 对制造订单所以面的cnc工单的程序用刀进行校验 + try: + logging.info(f'已更新制造订单:{productions_not_delivered}') + productions.production_cnc_tool_checkout() + except Exception as e: + logging.info(f'对cnc工单的程序用刀进行校验报错:{e}') + # 从cloud获取重新编程过的最新程序 def get_new_program(self, processing_panel): try: @@ -1002,8 +1018,8 @@ class MrpProduction(models.Model): panel_workorder.cmm_ids.sudo().unlink() if panel_workorder.cnc_ids: panel_workorder.cnc_ids.sudo().unlink() - self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( - production) + # self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( + # production) # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', # processing_panel) logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel) 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_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 017f89b8..27fdb37c 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -1,4 +1,5 @@ import datetime +import logging from datetime import timedelta, time from collections import defaultdict from odoo import fields, models, api @@ -6,6 +7,8 @@ from odoo.addons.resource.models.resource import Intervals from odoo.exceptions import UserError, ValidationError import math +_logger = logging.getLogger(__name__) + class ResWorkcenter(models.Model): _name = "mrp.workcenter" @@ -163,6 +166,19 @@ class ResWorkcenter(models.Model): else: record.effective_working_hours_day = 0 + # 计算传入时间日有效工作时长 + def _compute_effective_working_hours_day1(self, date): + effective_working_hours_day = 0 + for record in self: + attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if + p.dayofweek == self.get_current_day_of_week(date)] + if attendance_ids: + for attendance_id in attendance_ids: + if attendance_id.hour_from and attendance_id.hour_to: + effective_working_hours_day += attendance_id.hour_to - attendance_id.hour_from + + return effective_working_hours_day + # 获取传入时间是星期几 def get_current_day_of_week(self, datetime): day_num = datetime.weekday() @@ -211,12 +227,17 @@ class ResWorkcenter(models.Model): ('state', 'not in', ['draft', 'cancel'])]) if plan_ids: sum_qty = sum([p.product_qty for p in plan_ids]) - if sum_qty >= self.default_capacity: + date_planned_working_hours = self._compute_effective_working_hours_day1(date_planned) + default_capacity = round( + self.production_line_hour_capacity * date_planned_working_hours, 2) + _logger.info('排程日期:%s,计划数量:%s,日产能:%s,日工时:%s' % ( + date_planned, sum_qty, default_capacity, date_planned_working_hours)) + if sum_qty >= default_capacity: return False return True # 处理排程是否超过小时产能 - def deal_available_single_machine_capacity(self, date_planned): + def deal_available_single_machine_capacity(self, date_planned, count): date_planned_start = date_planned.strftime('%Y-%m-%d %H:00:00') date_planned_end = date_planned + timedelta(hours=1) @@ -228,7 +249,11 @@ class ResWorkcenter(models.Model): if plan_ids: sum_qty = sum([p.product_qty for p in plan_ids]) - if sum_qty >= self.production_line_hour_capacity: + production_line_hour_capacity = self.production_line_hour_capacity + if sum_qty >= production_line_hour_capacity: + message = '当前计划开始时间不能预约排程,超过生产线小时产能(%d件)%d件' % ( + production_line_hour_capacity, count) + raise UserError(message) return False return True diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 7c3ec50f..0ad53892 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): @@ -225,6 +225,9 @@ class ResMrpWorkOrder(models.Model): material_height = fields.Float(string='高') # 零件图号 part_number = fields.Char(related='production_id.part_number', string='零件图号') + machining_drawings = fields.Binary('2D加工图纸', related='production_id.part_drawing', readonly=True) + quality_standard = fields.Binary('质检标准', related='production_id.quality_standard', readonly=True) + # 工序状态 process_state = fields.Selection([ ('待装夹', '待装夹'), @@ -1197,8 +1200,8 @@ class ResMrpWorkOrder(models.Model): if record.is_rework is False: if not record.material_center_point: raise UserError("坯料中心点为空,请检查") - if record.X_deviation_angle <= 0: - raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle) + # if record.X_deviation_angle <= 0: + # raise UserError("X偏差角度小于等于0,请检查!本次计算的X偏差角度为:%s" % record.X_deviation_angle) record.process_state = '待加工' # record.write({'process_state': '待加工'}) record.production_id.process_state = '待加工' @@ -1826,6 +1829,11 @@ class WorkPieceDelivery(models.Model): return is_free else: raise UserError("接驳站暂未反馈站点实时状态,请稍后再试") + + def delivery_avg(self): + is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch') + if is_agv_task_dispatch: + self._delivery_avg() # 配送至avg小车 def _delivery_avg(self): @@ -1886,7 +1894,7 @@ class WorkPieceDelivery(models.Model): logging.info('delivery_item-name:%s' % delivery_item.name) delivery_item.write({ 'task_delivery_time': fields.Datetime.now(), - 'status': '待配送' + 'status': '已下发' }) if delivery_item.type == "上产线": delivery_item.workorder_id.write({'is_delivery': True}) diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 7f4dc2b8..8e81d076 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -774,6 +774,8 @@ class ResProductMo(models.Model): # bfm下单 manual_quotation = fields.Boolean('人工编程', default=False, readonly=True) part_number = fields.Char(string='零件图号', readonly=True) + machining_drawings = fields.Binary('2D加工图纸', readonly=True) + quality_standard = fields.Binary('质检标准', readonly=True) @api.constrains('tool_length') def _check_tool_length_size(self): @@ -873,6 +875,8 @@ class ResProductMo(models.Model): 'manual_quotation': item['manual_quotation'] or False, 'part_number': item.get('part_number') or '', 'active': True, + 'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(item['machining_drawings']), + 'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']), } tax_id = self.env['account.tax'].sudo().search( [('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')]) diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index daf049eb..85290d18 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -272,6 +272,10 @@ class StockRule(models.Model): if quick_easy_order: production.write({'part_number': quick_easy_order.part_drawing_number, 'part_drawing': quick_easy_order.machining_drawings}) + else: + production.write({'part_number': production.product_id.part_number, + 'part_drawing': production.product_id.machining_drawings, + 'quality_standard': production.product_id.quality_standard}) if sale_order: # sale_order.write({'schedule_status': 'to schedule'}) self.env['sf.production.plan'].sudo().with_company(company_id).create({ diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index fd3b0d21..be71cb0d 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -103,10 +103,20 @@ access_mrp_production_split_multi_group_sf_mrp_user,access.mrp.production.split. access_mrp_production_split_group_sf_mrp_user,access.mrp.production.split,mrp.model_mrp_production_split,sf_base.group_sf_mrp_user,1,1,1,0 access_mrp_production_split_line_group_sf_mrp_user,access.mrp.production.split.line,mrp.model_mrp_production_split_line,sf_base.group_sf_mrp_user,1,1,1,0 access_mrp_workcenter_capacity_manager_group_sf_mrp_user,mrp.workcenter.capacity.manager,mrp.model_mrp_workcenter_capacity,sf_base.group_sf_mrp_user,1,1,1,0 - - +access_mrp_workcenter_group_quality,mrp_workcenter_group_quality,model_mrp_workcenter,sf_base.group_quality,1,0,0,0 +access_mrp_workcenter_group_quality_director,mrp_workcenter_group_quality_director,model_mrp_workcenter,sf_base.group_quality_director,1,0,0,0 +access_sf_detection_result_group_quality,sf_detection_result_group_quality,model_sf_detection_result,sf_base.group_quality,1,0,1,0 +access_sf_detection_result_group_quality_director,sf_detection_result_group_quality_director,model_sf_detection_result,sf_base.group_quality_director,1,0,1,0 +access_mrp_workcenter_productivity_loss_group_quality,mrp_workcenter_productivity_loss_group_quality,mrp.model_mrp_workcenter_productivity_loss,sf_base.group_quality,1,0,0,0 +access_mrp_workcenter_productivity_loss_group_quality_director,mrp_workcenter_productivity_loss_group_quality_director,mrp.model_mrp_workcenter_productivity_loss,sf_base.group_quality_director,1,0,0,0 +access_mrp_workcenter_productivity_group_quality,mrp_workcenter_productivity_group_quality,mrp.model_mrp_workcenter_productivity,sf_base.group_quality,1,1,1,0 +access_mrp_workcenter_productivity_group_quality_director,mrp_workcenter_productivity_group_quality_director,mrp.model_mrp_workcenter_productivity,sf_base.group_quality_director,1,1,1,0 access_mrp_production_group_plan_dispatch,mrp_production,model_mrp_production,sf_base.group_plan_dispatch,1,1,0,0 -access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,1,0 +access_mrp_production_group_quality,mrp_production,model_mrp_production,sf_base.group_quality,1,1,0,0 +access_mrp_production_group_quality_director,mrp_production,model_mrp_production,sf_base.group_quality_director,1,1,0,0 +access_mrp_workorder_group_quality,mrp_workorder,model_mrp_workorder,sf_base.group_quality,1,1,0,0 +access_mrp_workorder_group_quality_director,mrp_workorder,model_mrp_workorder,sf_base.group_quality_director,1,1,0,0 +access_mrp_workorder,mrp_workorder,model_mrp_workorder,sf_base.group_plan_dispatch,1,1,0,0 access_sf_production_line_group_plan_dispatch,sf.production.line,model_sf_production_line,sf_base.group_plan_dispatch,1,0,0,0 access_sf_production_line_group_plan_director,sf.production.line,model_sf_production_line,sf_base.group_plan_director,1,1,1,0 access_sf_production_line,sf.production.line,model_sf_production_line,sf_maintenance.sf_group_equipment_user,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 86a9ce9e..43e12b04 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -98,7 +98,8 @@ - + + @@ -297,8 +298,11 @@ - - + + @@ -349,8 +353,11 @@ sequence + delivery_warning == 'warning' + delivery_warning == 'overdue' + - + +
+ 品牌: + +
+
规格: diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index c3e05739..8efd76db 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -101,7 +101,9 @@ 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} +

没有工单要做! @@ -221,15 +223,54 @@ - - + + + + + + + + + + + + + + + - + + + + +

+
+ + + + 计划加工时间 @@ -246,76 +287,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -330,10 +307,6 @@ - - - - @@ -520,7 +493,8 @@