Merge branch 'refs/heads/develop' into feature/auxiliary_files_upload
# Conflicts: # sf_dlm_management/views/product_template_management_view.xml
This commit is contained in:
4
jikimo_workorder_exception/__init__.py
Normal file
4
jikimo_workorder_exception/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import controllers
|
||||||
21
jikimo_workorder_exception/__manifest__.py
Normal file
21
jikimo_workorder_exception/__manifest__.py
Normal file
@@ -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,
|
||||||
|
}
|
||||||
1
jikimo_workorder_exception/controllers/__init__.py
Normal file
1
jikimo_workorder_exception/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import main
|
||||||
89
jikimo_workorder_exception/controllers/main.py
Normal file
89
jikimo_workorder_exception/controllers/main.py
Normal file
@@ -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)
|
||||||
3
jikimo_workorder_exception/models/__init__.py
Normal file
3
jikimo_workorder_exception/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import jikimo_workorder_exception
|
||||||
|
from . import mrp_workorder
|
||||||
@@ -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')
|
||||||
|
|
||||||
40
jikimo_workorder_exception/models/mrp_workorder.py
Normal file
40
jikimo_workorder_exception/models/mrp_workorder.py
Normal file
@@ -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'
|
||||||
|
})
|
||||||
2
jikimo_workorder_exception/security/ir.model.access.csv
Normal file
2
jikimo_workorder_exception/security/ir.model.access.csv
Normal file
@@ -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
|
||||||
|
2
jikimo_workorder_exception/tests/__init__.py
Normal file
2
jikimo_workorder_exception/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import common
|
||||||
|
from . import test_jikimo_workorder_exception
|
||||||
48
jikimo_workorder_exception/tests/common.py
Normal file
48
jikimo_workorder_exception/tests/common.py
Normal file
@@ -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加工'
|
||||||
|
})
|
||||||
@@ -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)
|
||||||
23
jikimo_workorder_exception/views/mrp_workorder_views.xml
Normal file
23
jikimo_workorder_exception/views/mrp_workorder_views.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="jikimo_workorder_exception_form_view_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">mrp.workorder.form</field>
|
||||||
|
<field name="model">mrp.workorder</field>
|
||||||
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//notebook/page[last()]" position="after">
|
||||||
|
<page string="异常记录" name="workorder_exception" attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}">
|
||||||
|
<field name="exception_ids" nolabel="1" readonly="1">
|
||||||
|
<tree create="false" delete="false" edit="false">
|
||||||
|
<field name="exception_content" string="反馈的异常/问题信息"/>
|
||||||
|
<field name="create_date" string="时间"/>
|
||||||
|
<field name="completion_time"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
4
jikimo_workorder_exception_notify/__init__.py
Normal file
4
jikimo_workorder_exception_notify/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
22
jikimo_workorder_exception_notify/__manifest__.py
Normal file
22
jikimo_workorder_exception_notify/__manifest__.py
Normal file
@@ -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,
|
||||||
|
}
|
||||||
17
jikimo_workorder_exception_notify/data/bussiness_node.xml
Normal file
17
jikimo_workorder_exception_notify/data/bussiness_node.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="bussiness_no_functional_tool" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">无功能刀具</field>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
</record>
|
||||||
|
<record id="bussiness_no_position_data" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">无定位数据</field>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
</record>
|
||||||
|
<record id="bussiness_processing_failure" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">加工失败</field>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
38
jikimo_workorder_exception_notify/data/template_data.xml
Normal file
38
jikimo_workorder_exception_notify/data/template_data.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="template_no_function_tool" model="jikimo.message.template">
|
||||||
|
<field name="name">生产线无功能刀具提醒</field>
|
||||||
|
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_no_functional_tool"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 生产线无功能刀具提醒
|
||||||
|
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
||||||
|
原因:生产线无加工程序用的{{function_tool_name}}名称功能刀具</field>
|
||||||
|
</record>
|
||||||
|
<record id="template_no_position_data" model="jikimo.message.template">
|
||||||
|
<field name="name">工单无定位数据提醒</field>
|
||||||
|
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_no_position_data"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 生产线无功能刀具提醒
|
||||||
|
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
||||||
|
原因:无装夹定位测量数据</field>
|
||||||
|
</record>
|
||||||
|
<record id="template_processing_failure" model="jikimo.message.template">
|
||||||
|
<field name="name">工单加工失败提醒</field>
|
||||||
|
<field name="model_id" ref="jikimo_workorder_exception_notify.model_jikimo_workorder_exception"/>
|
||||||
|
<field name="model">jikimo.workorder.exception</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_processing_failure"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 工单加工失败提醒
|
||||||
|
单号:工单[{{workorder_id.production_id.name}}]({{url}})
|
||||||
|
原因:加工失败,工件下产线处理</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
3
jikimo_workorder_exception_notify/models/__init__.py
Normal file
3
jikimo_workorder_exception_notify/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import jikimo_message_template
|
||||||
|
from . import jikimo_workorder_exception
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
2
jikimo_workorder_exception_notify/tests/__init__.py
Normal file
2
jikimo_workorder_exception_notify/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import common
|
||||||
|
from . import test_jikimo_workorder_exception_notify
|
||||||
18
jikimo_workorder_exception_notify/tests/common.py
Normal file
18
jikimo_workorder_exception_notify/tests/common.py
Normal file
@@ -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)
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
@@ -38,3 +38,17 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
logging.info('get_maintenance_tool_groups_Info error:%s' % e)
|
logging.info('get_maintenance_tool_groups_Info error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
|
||||||
|
class MultiInheritController():
|
||||||
|
_sub_classes = []
|
||||||
|
|
||||||
|
def __init_subclass__(cls):
|
||||||
|
"""
|
||||||
|
多继承,解决多个字类时方法调用super的问题
|
||||||
|
"""
|
||||||
|
super().__init_subclass__()
|
||||||
|
if len(cls._sub_classes) > 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)
|
||||||
|
|||||||
@@ -402,20 +402,45 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
try:
|
try:
|
||||||
plan_obj = request.env['sf.production.plan'].sudo()
|
plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
production_obj = request.env['mrp.production'].sudo()
|
production_obj = request.env['mrp.production'].sudo()
|
||||||
|
work_order_obj = request.env['mrp.workorder'].sudo()
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
# print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
|
|
||||||
# 工单计划量
|
|
||||||
plan_data_total_counts = production_obj.search_count(
|
|
||||||
[('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
|
||||||
('active', '=', True)])
|
|
||||||
# 工单完成量
|
|
||||||
plan_data_finish_counts = plan_obj.search_count(
|
|
||||||
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
|
||||||
# # 工单计划量
|
# # 工单计划量
|
||||||
# plan_data_plan_counts = plan_obj.search_count(
|
# plan_data_total_counts = production_obj.search_count(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
|
# [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
||||||
|
# ('active', '=', True)])
|
||||||
|
|
||||||
|
# 工单计划量切换为CNC工单
|
||||||
|
plan_data_total_counts = work_order_obj.search_count(
|
||||||
|
[('production_id.production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
|
||||||
|
|
||||||
|
# # 工单完成量
|
||||||
|
# plan_data_finish_counts = plan_obj.search_count(
|
||||||
|
# [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
|
|
||||||
|
# 工单完成量切换为CNC工单
|
||||||
|
plan_data_finish_counts = work_order_obj.search_count(
|
||||||
|
[('production_id.production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
|
||||||
|
|
||||||
|
# 超期完成量
|
||||||
|
# 搜索所有已经完成的工单
|
||||||
|
plan_data_overtime = work_order_obj.search([
|
||||||
|
('production_id.production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['done']),
|
||||||
|
('routing_type', '=', 'CNC加工')
|
||||||
|
])
|
||||||
|
|
||||||
|
# 使用 filtered 进行字段比较
|
||||||
|
plan_data_overtime_counts = plan_data_overtime.filtered(
|
||||||
|
lambda order: order.date_finished > order.date_planned_finished
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取数量
|
||||||
|
plan_data_overtime_counts = len(plan_data_overtime_counts)
|
||||||
|
|
||||||
# 查找符合条件的生产计划记录
|
# 查找符合条件的生产计划记录
|
||||||
plan_data = plan_obj.search([
|
plan_data = plan_obj.search([
|
||||||
@@ -517,7 +542,10 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'on_time_rate': on_time_rate,
|
'on_time_rate': on_time_rate,
|
||||||
# 'detection_data': detection_data,
|
# 'detection_data': detection_data,
|
||||||
'detection_data': plan_data_finish_counts,
|
'detection_data': plan_data_finish_counts,
|
||||||
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts
|
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts,
|
||||||
|
'plan_data_overtime_counts': plan_data_overtime_counts,
|
||||||
|
'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts
|
||||||
|
if plan_data_finish_counts > 0 else 0,
|
||||||
}
|
}
|
||||||
res['data'][line] = data
|
res['data'][line] = data
|
||||||
|
|
||||||
@@ -1244,6 +1272,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
time_threshold = datetime.now() - timedelta(days=1)
|
time_threshold = datetime.now() - timedelta(days=1)
|
||||||
|
|
||||||
alarm_last_24_time = 0.0
|
alarm_last_24_time = 0.0
|
||||||
|
alarm_all_time = 0.0
|
||||||
|
|
||||||
def fetch_result_as_dict(cursor):
|
def fetch_result_as_dict(cursor):
|
||||||
"""辅助函数:将查询结果转为字典"""
|
"""辅助函数:将查询结果转为字典"""
|
||||||
@@ -1304,6 +1333,35 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
alarm_last_24_time += float(result[0])
|
alarm_last_24_time += float(result[0])
|
||||||
else:
|
else:
|
||||||
alarm_last_24_time += 0.0
|
alarm_last_24_time += 0.0
|
||||||
|
|
||||||
|
alarm_all_nums = []
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("""
|
||||||
|
SELECT DISTINCT ON (alarm_start_time) alarm_time, alarm_start_time
|
||||||
|
FROM device_data
|
||||||
|
WHERE device_name = %s
|
||||||
|
AND alarm_start_time IS NOT NULL;
|
||||||
|
""", (item,))
|
||||||
|
results = cur.fetchall()
|
||||||
|
for result in results:
|
||||||
|
alarm_all_nums.append(result[1])
|
||||||
|
if result[0]:
|
||||||
|
if float(result[0]) >= 1000:
|
||||||
|
continue
|
||||||
|
alarm_all_time += float(result[0])
|
||||||
|
else:
|
||||||
|
alarm_all_time += 0.0
|
||||||
|
|
||||||
|
# with conn.cursor() as cur:
|
||||||
|
# cur.execute("""
|
||||||
|
# SELECT * FROM device_data
|
||||||
|
# WHERE device_name = %s
|
||||||
|
# AND total_count IS NOT NULL
|
||||||
|
# ORDER BY time ASC
|
||||||
|
# LIMIT 1;
|
||||||
|
# """, (item, ))
|
||||||
|
# total_count = fetch_result_as_dict(cur)
|
||||||
|
|
||||||
# 返回数据
|
# 返回数据
|
||||||
res['data'][item] = {
|
res['data'][item] = {
|
||||||
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0,
|
||||||
@@ -1315,6 +1373,9 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
'alarm_last_24_nums': len(list(set(alarm_last_24_nums))),
|
||||||
'idle_count': idle_count,
|
'idle_count': idle_count,
|
||||||
'first_online_time': first_online_duration,
|
'first_online_time': first_online_duration,
|
||||||
|
'alarm_all_time': alarm_all_time,
|
||||||
|
'alarm_all_nums': len(list(set(alarm_all_nums)))
|
||||||
|
# 'total_count': total_count['total_count'] if total_count else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
'security/group_security.xml',
|
'security/group_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'security/ir_rule_data.xml',
|
'security/ir_rule_data.xml',
|
||||||
|
'data/scheduled_actions.xml',
|
||||||
'views/maintenance_logs_views.xml',
|
'views/maintenance_logs_views.xml',
|
||||||
'views/maintenance_equipment_oee_views.xml',
|
'views/maintenance_equipment_oee_views.xml',
|
||||||
'views/maintenance_views.xml',
|
'views/maintenance_views.xml',
|
||||||
|
|||||||
14
sf_maintenance/data/scheduled_actions.xml
Normal file
14
sf_maintenance/data/scheduled_actions.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="ir_cron_oee_get_running_datas" model="ir.cron">
|
||||||
|
<field name="name">设备运行数据</field>
|
||||||
|
<field name="model_id" ref="model_maintenance_equipment_oee"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model.get_running_datas()</field>
|
||||||
|
<field name="interval_number">15</field>
|
||||||
|
<field name="interval_type">minutes</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
<field name="active" eval="True"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
3583
sf_maintenance/i18n/zh_CN.po
Normal file
3583
sf_maintenance/i18n/zh_CN.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -88,6 +88,69 @@ class SfMaintenanceEquipmentOEE(models.Model):
|
|||||||
begin_time = fields.Date('开始时间')
|
begin_time = fields.Date('开始时间')
|
||||||
end_time = fields.Date('结束时间')
|
end_time = fields.Date('结束时间')
|
||||||
|
|
||||||
|
def get_running_datas(self):
|
||||||
|
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
url_time = base_url + '/api/RunningTimeDetail'
|
||||||
|
cnc_list_obj = self.env['maintenance.equipment'].sudo().search(
|
||||||
|
[('function_type', '!=', False), ('active', '=', True)])
|
||||||
|
machine_list = list(map(lambda x: x.code, cnc_list_obj))
|
||||||
|
# print('machine_list: %s' % machine_list)
|
||||||
|
|
||||||
|
data_time = {
|
||||||
|
"machine_list": str(machine_list)
|
||||||
|
}
|
||||||
|
# 发送POST请求
|
||||||
|
response_time = requests.post(url_time, json={}, data=data_time)
|
||||||
|
# print(response_time.json())
|
||||||
|
if response_time.status_code == 200:
|
||||||
|
result_time = response_time.json()
|
||||||
|
if result_time['status'] == 1:
|
||||||
|
real_dict = result_time['data']
|
||||||
|
for key in real_dict:
|
||||||
|
# print(key)
|
||||||
|
equipment_obj = self.env['maintenance.equipment.oee'].sudo().search([('equipment_code', '=', key)])
|
||||||
|
if real_dict[key]['power_on_time'] == 0:
|
||||||
|
equipment_obj.online_time = 0
|
||||||
|
equipment_obj.idle_time = 0
|
||||||
|
equipment_obj.idle_rate = 0
|
||||||
|
equipment_obj.work_rate = 0
|
||||||
|
equipment_obj.fault_time = 0
|
||||||
|
equipment_obj.fault_rate = 0
|
||||||
|
equipment_obj.fault_nums = 0
|
||||||
|
equipment_obj.idle_nums = 0
|
||||||
|
equipment_obj.work_time = 0
|
||||||
|
else:
|
||||||
|
equipment_obj.online_time = round(convert_to_seconds(real_dict[key]['power_on_time']) / 3600, 2)
|
||||||
|
equipment_obj.work_time = round(convert_to_seconds(real_dict[key]['cut_time']) / 3600, 2)
|
||||||
|
equipment_obj.fault_nums = real_dict[key]['alarm_all_nums']
|
||||||
|
equipment_obj.idle_nums = real_dict[key]['idle_count']
|
||||||
|
equipment_obj.fault_time = round((float(real_dict[key]['alarm_all_time']) if real_dict[key][
|
||||||
|
'alarm_all_time'] else 0) / 3600, 2)
|
||||||
|
equipment_obj.idle_time = float(equipment_obj.online_time) - float(
|
||||||
|
equipment_obj.work_time) if equipment_obj.online_time and equipment_obj.work_time else 0
|
||||||
|
equipment_obj.idle_rate = round(
|
||||||
|
float(equipment_obj.idle_time) / (
|
||||||
|
float(equipment_obj.online_time) if equipment_obj.online_time else 1) * 100, 2)
|
||||||
|
equipment_obj.work_rate = round(
|
||||||
|
float(equipment_obj.work_time) / (
|
||||||
|
float(equipment_obj.online_time) if equipment_obj.online_time else 1) * 100, 2)
|
||||||
|
|
||||||
|
equipment_obj.fault_rate = round(
|
||||||
|
float(equipment_obj.fault_time) / (
|
||||||
|
float(equipment_obj.online_time) if equipment_obj.online_time else 1) * 100, 2)
|
||||||
|
|
||||||
|
# 获取当前时间的时间戳
|
||||||
|
current_timestamp = datetime.datetime.now().timestamp()
|
||||||
|
# 机床上线时间段
|
||||||
|
first_online_duration = current_timestamp - int(equipment_obj.equipment_id.first_online_time.timestamp())
|
||||||
|
|
||||||
|
if equipment_obj.online_time:
|
||||||
|
equipment_obj.offline_time = round((first_online_duration - float(equipment_obj.online_time)) / 3600, 2)
|
||||||
|
else:
|
||||||
|
equipment_obj.offline_time = False
|
||||||
|
# equipment_obj.offline_time = equipment_obj.equipment_id.first_online_time - (
|
||||||
|
# float(equipment_obj.online_time) if equipment_obj.online_time else 0)
|
||||||
|
|
||||||
# 获取日志详情
|
# 获取日志详情
|
||||||
def get_day_logs(self):
|
def get_day_logs(self):
|
||||||
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
@@ -150,12 +213,11 @@ class SfMaintenanceEquipmentOEE(models.Model):
|
|||||||
self.fault_nums = real_dict['alarm_last_24_nums']
|
self.fault_nums = real_dict['alarm_last_24_nums']
|
||||||
self.idle_nums = real_dict['idle_count']
|
self.idle_nums = real_dict['idle_count']
|
||||||
self.work_time = round(
|
self.work_time = round(
|
||||||
(convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(real_dict['cut_24_time'])) / 3600,
|
(convert_to_seconds(real_dict['cut_time']) - convert_to_seconds(
|
||||||
|
real_dict['cut_24_time'])) / 3600,
|
||||||
2)
|
2)
|
||||||
self.offline_time = 24 - (float(self.online_time) if self.online_time else 0)
|
self.offline_time = 24 - (float(self.online_time) if self.online_time else 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
result = response.json()
|
result = response.json()
|
||||||
print('============', result)
|
print('============', result)
|
||||||
|
|||||||
@@ -79,12 +79,12 @@
|
|||||||
</group>
|
</group>
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<!-- <notebook> -->
|
||||||
<page string="24H日志详情">
|
<!-- <page string="24H日志详情"> -->
|
||||||
<group>
|
<!-- <group> -->
|
||||||
<button name="get_day_logs" type="object" string="查看24H日志" t-attf-style="white-space:nowrap;"/>
|
<!-- <button name="get_day_logs" type="object" string="查看24H日志" t-attf-style="white-space:nowrap;"/> -->
|
||||||
</group>
|
<!-- </group> -->
|
||||||
<field name="day_logs_detail" readonly="1" widget="html"/>
|
<!-- <field name="day_logs_detail" readonly="1" widget="html"/> -->
|
||||||
<!-- <field name="page_num"/> -->
|
<!-- <field name="page_num"/> -->
|
||||||
<!-- <group> -->
|
<!-- <group> -->
|
||||||
<!-- <group> -->
|
<!-- <group> -->
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
<!-- </div> -->
|
<!-- </div> -->
|
||||||
<!-- </group> -->
|
<!-- </group> -->
|
||||||
</page>
|
<!-- </page> -->
|
||||||
<!-- <page string="历史日志详情"> -->
|
<!-- <page string="历史日志详情"> -->
|
||||||
<!-- <group> -->
|
<!-- <group> -->
|
||||||
<!-- <group> -->
|
<!-- <group> -->
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
<!-- </group> -->
|
<!-- </group> -->
|
||||||
<!-- <field name="history_logs_detail"/> -->
|
<!-- <field name="history_logs_detail"/> -->
|
||||||
<!-- </page> -->
|
<!-- </page> -->
|
||||||
</notebook>
|
<!-- </notebook> -->
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
|
request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
|
||||||
{'actual_start_time': workorder.date_start,
|
{'actual_start_time': workorder.date_start,
|
||||||
'state': 'processing'})
|
'state': 'processing'})
|
||||||
|
res.update({'workorder_id': workorder.id})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
|
|||||||
@@ -950,6 +950,8 @@ class MrpProduction(models.Model):
|
|||||||
if production.programming_no in program_to_production_names:
|
if production.programming_no in program_to_production_names:
|
||||||
productions_not_delivered = self.env['mrp.production'].search(
|
productions_not_delivered = self.env['mrp.production'].search(
|
||||||
[('programming_no', '=', production.programming_no), ('programming_state', '=', '已编程未下发')])
|
[('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')
|
rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework')
|
||||||
if rework_workorder:
|
if rework_workorder:
|
||||||
for rework_item in rework_workorder:
|
for rework_item in rework_workorder:
|
||||||
@@ -962,6 +964,13 @@ class MrpProduction(models.Model):
|
|||||||
productions_not_delivered.write(
|
productions_not_delivered.write(
|
||||||
{'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
{'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获取重新编程过的最新程序
|
# 从cloud获取重新编程过的最新程序
|
||||||
def get_new_program(self, processing_panel):
|
def get_new_program(self, processing_panel):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class ResMrpRoutingWorkcenter(models.Model):
|
|||||||
def get_company_id(self):
|
def get_company_id(self):
|
||||||
self.company_id = self.env.user.company_id.id
|
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)
|
||||||
|
|
||||||
# 排产的时候, 根据坯料的长宽高比对一下机床的最大加工尺寸.不符合就不要分配给这个加工中心(机床).
|
# 排产的时候, 根据坯料的长宽高比对一下机床的最大加工尺寸.不符合就不要分配给这个加工中心(机床).
|
||||||
# 工单对应的工作中心,根据工序中的工作中心去匹配,
|
# 工单对应的工作中心,根据工序中的工作中心去匹配,
|
||||||
|
|||||||
@@ -774,8 +774,8 @@ class ResProductMo(models.Model):
|
|||||||
# bfm下单
|
# bfm下单
|
||||||
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
||||||
part_number = fields.Char(string='零件图号', readonly=True)
|
part_number = fields.Char(string='零件图号', readonly=True)
|
||||||
machining_drawings = fields.Binary('2D加工图纸', readonly=True)
|
# machining_drawings = fields.Binary('2D加工图纸', readonly=True)
|
||||||
quality_standard = fields.Binary('质检标准', readonly=True)
|
# quality_standard = fields.Binary('质检标准', readonly=True)
|
||||||
|
|
||||||
@api.constrains('tool_length')
|
@api.constrains('tool_length')
|
||||||
def _check_tool_length_size(self):
|
def _check_tool_length_size(self):
|
||||||
@@ -875,8 +875,8 @@ class ResProductMo(models.Model):
|
|||||||
'manual_quotation': item['manual_quotation'] or False,
|
'manual_quotation': item['manual_quotation'] or False,
|
||||||
'part_number': item.get('part_number') or '',
|
'part_number': item.get('part_number') or '',
|
||||||
'active': True,
|
'active': True,
|
||||||
'machining_drawings': '' if not item['machining_drawings'] else base64.b64decode(item['machining_drawings']),
|
# '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']),
|
# 'quality_standard': '' if not item['quality_standard'] else base64.b64decode(item['quality_standard']),
|
||||||
}
|
}
|
||||||
tax_id = self.env['account.tax'].sudo().search(
|
tax_id = self.env['account.tax'].sudo().search(
|
||||||
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
|
||||||
|
|||||||
@@ -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_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_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_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_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_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_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
|
access_sf_production_line,sf.production.line,model_sf_production_line,sf_maintenance.sf_group_equipment_user,1,1,1,0
|
||||||
|
|||||||
|
@@ -586,6 +586,12 @@
|
|||||||
position="after">
|
position="after">
|
||||||
<field name="duration" string="实际时长"/>
|
<field name="duration" string="实际时长"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<xpath expr="//page[@name='time_tracking']" position="attributes">
|
||||||
|
<attribute name="groups">
|
||||||
|
mrp.group_mrp_manager,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user,sf_base.group_sf_order_user
|
||||||
|
</attribute>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ class MessageSfMrsConnect(Sf_Mrs_Connect):
|
|||||||
def get_cnc_processing_create(self, **kw):
|
def get_cnc_processing_create(self, **kw):
|
||||||
res = super(MessageSfMrsConnect, self).get_cnc_processing_create(**kw)
|
res = super(MessageSfMrsConnect, self).get_cnc_processing_create(**kw)
|
||||||
res = json.loads(res)
|
res = json.loads(res)
|
||||||
|
_logger.info('已进入消息推送:%s' % res)
|
||||||
if res.get('production_ids'):
|
if res.get('production_ids'):
|
||||||
try:
|
try:
|
||||||
_logger.info('已编程的制造订单:%s' % res.get('production_ids'))
|
_logger.info('已编程的制造订单:%s' % res.get('production_ids'))
|
||||||
productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))])
|
productions = request.env['mrp.production'].sudo().search([('id', 'in', res.get('production_ids'))])
|
||||||
# 过滤programming_state为已编程,tool_state为2的制造订单
|
# 过滤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:
|
if tool_state_valid_productions:
|
||||||
data = {
|
data = {
|
||||||
'name': tool_state_valid_productions[0].programming_no
|
'name': tool_state_valid_productions[0].programming_no
|
||||||
@@ -38,3 +39,22 @@ class MessageSfMrsConnect(Sf_Mrs_Connect):
|
|||||||
_logger.info('无效用刀异常消息推送接口:%s' % e)
|
_logger.info('无效用刀异常消息推送接口:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
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)
|
||||||
|
|
||||||
|
|||||||
@@ -58,17 +58,8 @@
|
|||||||
<field name="name">订单发货提醒</field>
|
<field name="name">订单发货提醒</field>
|
||||||
<field name="model">stock.picking</field>
|
<field name="model">stock.picking</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- <record id="bussiness_mrp_workorder_pre_overdue_warning" model="jikimo.message.bussiness.node">-->
|
|
||||||
<!-- <field name="name">装夹预调工单逾期预警</field>-->
|
|
||||||
<!-- <field name="model">mrp.workorder</field>-->
|
|
||||||
<!-- </record>-->
|
|
||||||
<!-- <record id="bussiness_mrp_workorder_pre_overdue" model="jikimo.message.bussiness.node">-->
|
|
||||||
<!-- <field name="name">装夹预调工单已逾期</field>-->
|
|
||||||
<!-- <field name="model">mrp.workorder</field>-->
|
|
||||||
<!-- </record>-->
|
|
||||||
|
|
||||||
|
<record id="bussiness_mrp_workorder_pre_overdue_warning" model="jikimo.message.bussiness.node">
|
||||||
<record id="bussiness_mrp_workorder_pre_overdue_warning" model="jikimo.message.bussiness.node">
|
|
||||||
<field name="name">装夹预调工单逾期预警</field>
|
<field name="name">装夹预调工单逾期预警</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -77,12 +68,12 @@
|
|||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="bussiness_mrp_workorder_cnc_overdue_warning" model="jikimo.message.bussiness.node">
|
<record id="bussiness_mrp_workorder_cnc_overdue_warning" model="jikimo.message.bussiness.node">
|
||||||
<field name="name">CNC加工工单逾期预警</field>
|
<field name="name">CNC加工工单逾期预警</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
<record id="bussiness_mrp_workorder_cnc_overdue" model="jikimo.message.bussiness.node">
|
<record id="bussiness_mrp_workorder_cnc_overdue" model="jikimo.message.bussiness.node">
|
||||||
<field name="name">CNC工单已逾期</field>
|
<field name="name">CNC加工工单已逾期</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -96,7 +87,7 @@
|
|||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="bussiness_mrp_workorder_surface_overdue_warning" model="jikimo.message.bussiness.node">
|
<record id="bussiness_mrp_workorder_surface_overdue_warning" model="jikimo.message.bussiness.node">
|
||||||
<field name="name">表面工艺工单逾期预警</field>
|
<field name="name">表面工艺工单逾期预警</field>
|
||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -111,5 +102,9 @@
|
|||||||
<field name="model">quality.cnc.test</field>
|
<field name="model">quality.cnc.test</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="bussiness_maintenance_logs" model="jikimo.message.bussiness.node">
|
||||||
|
<field name="name">设备故障</field>
|
||||||
|
<field name="model">sf.maintenance.logs</field>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<field name="active" eval="True"/>
|
<field name="active" eval="True"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.cron" id="ir_cron_mrp_workorder_overdue_warning">
|
<record model="ir.cron" id="ir_cron_mrp_workorder_recover_time_warning">
|
||||||
<field name="name">检查工单是否完成并恢复正常时效</field>
|
<field name="name">检查工单是否完成并恢复正常时效</field>
|
||||||
<field name="model_id" ref="model_mrp_workorder"/>
|
<field name="model_id" ref="model_mrp_workorder"/>
|
||||||
<field name="state">code</field>
|
<field name="state">code</field>
|
||||||
|
|||||||
@@ -264,5 +264,16 @@
|
|||||||
<field name="content">### 待质量判定提醒
|
<field name="content">### 待质量判定提醒
|
||||||
事项:共有[{{judge_num}}]({{url}})个工单需判定质量结果</field>
|
事项:共有[{{judge_num}}]({{url}})个工单需判定质量结果</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record id="template_maintenance_logs" model="jikimo.message.template">
|
||||||
|
<field name="name">设备故障</field>
|
||||||
|
<field name="model_id" ref="sf_maintenance.model_sf_maintenance_logs"/>
|
||||||
|
<field name="model">sf.maintenance.logs</field>
|
||||||
|
<field name="bussiness_node_id" ref="bussiness_maintenance_logs"/>
|
||||||
|
<field name="msgtype">markdown</field>
|
||||||
|
<field name="urgency">urgent</field>
|
||||||
|
<field name="content">### 设备故障及异常提醒:
|
||||||
|
机台号:[{{maintenance_equipment_id.name}}]({{url}})
|
||||||
|
事项:{{create_date}}故障报警</field>
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -9,3 +9,4 @@ from . import sf_message_workorder
|
|||||||
from . import sf_message_functional_tool_dismantle
|
from . import sf_message_functional_tool_dismantle
|
||||||
from . import sf_message_mrp_production
|
from . import sf_message_mrp_production
|
||||||
from . import sf_message_quality_cnc_test
|
from . import sf_message_quality_cnc_test
|
||||||
|
from . import sf_message_maintenance_logs
|
||||||
|
|||||||
22
sf_message/models/sf_message_maintenance_logs.py
Normal file
22
sf_message/models/sf_message_maintenance_logs.py
Normal file
@@ -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
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
@@ -35,19 +36,20 @@ class SFMessageMrpProduction(models.Model):
|
|||||||
[('origin', '=', mrp_production.origin), ('picking_type_id.sequence_code', '=', 'SFP'),
|
[('origin', '=', mrp_production.origin), ('picking_type_id.sequence_code', '=', 'SFP'),
|
||||||
('state', '=', 'assigned')], limit=1)
|
('state', '=', 'assigned')], limit=1)
|
||||||
if stock_picking_sfp:
|
if stock_picking_sfp:
|
||||||
url = self.request_url()
|
url = self.request_url(stock_picking_sfp.id)
|
||||||
content = content.replace('{{name}}', stock_picking_sfp.name).replace(
|
content = content.replace('{{name}}', stock_picking_sfp.name).replace(
|
||||||
'{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url)
|
'{{sale_order_name}}', mrp_production.origin).replace('{{request_url}}', url)
|
||||||
contents.append(content)
|
contents.append(content)
|
||||||
|
logging.info('生产完工入库提醒: %s' % contents)
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
def request_url(self):
|
def request_url(self, id):
|
||||||
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
action_id = self.env.ref('mrp.mrp_production_action').id
|
action_id = self.env.ref('stock.action_picking_tree_all').id
|
||||||
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
||||||
# 查询参数
|
# 查询参数
|
||||||
params = {'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
|
params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'mrp.production',
|
||||||
'view_type': 'kanban'}
|
'view_type': 'form'}
|
||||||
# 拼接查询参数
|
# 拼接查询参数
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
# 拼接URL
|
# 拼接URL
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
from odoo import models, fields, api, _
|
from odoo import models, fields, api, _
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
@@ -40,9 +41,10 @@ class SFMessageStockPicking(models.Model):
|
|||||||
('picking_type_id.sequence_code', '=', 'OUT')])
|
('picking_type_id.sequence_code', '=', 'OUT')])
|
||||||
if stock_picking_out and len(stock_picking_out) > 0:
|
if stock_picking_out and len(stock_picking_out) > 0:
|
||||||
content = message_queue_id.message_template_id.content
|
content = message_queue_id.message_template_id.content
|
||||||
url = self.request_url()
|
url = self.request_url1(stock_picking_out.id)
|
||||||
content = content.replace('{{name}}', stock_picking_out.name).replace(
|
content = content.replace('{{name}}', stock_picking_out.name).replace(
|
||||||
'{{sale_order_name}}', stock_picking_out.origin).replace('{{request_url}}', url)
|
'{{sale_order_name}}', stock_picking_out.origin).replace('{{request_url}}', url)
|
||||||
|
logging.info('订单发货提醒: %s' % content)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def _get_message(self, message_queue_ids):
|
def _get_message(self, message_queue_ids):
|
||||||
@@ -96,3 +98,16 @@ class SFMessageStockPicking(models.Model):
|
|||||||
# 拼接URL
|
# 拼接URL
|
||||||
full_url = url + "/web#" + query_string
|
full_url = url + "/web#" + query_string
|
||||||
return full_url
|
return full_url
|
||||||
|
|
||||||
|
def request_url1(self, id):
|
||||||
|
url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||||
|
action_id = self.env.ref('stock.action_picking_tree_all').id
|
||||||
|
menu_id = self.env['ir.model.data'].search([('name', '=', 'module_theme_treehouse')]).id
|
||||||
|
# 查询参数
|
||||||
|
params = {'id': id, 'menu_id': menu_id, 'action': action_id, 'model': 'stock.picking',
|
||||||
|
'view_type': 'form'}
|
||||||
|
# 拼接查询参数
|
||||||
|
query_string = urlencode(params)
|
||||||
|
# 拼接URL
|
||||||
|
full_url = url + "/web#" + query_string
|
||||||
|
return full_url
|
||||||
|
|||||||
@@ -14,4 +14,6 @@ class SfMessageTemplate(models.Model):
|
|||||||
res.append('sf.functional.tool.dismantle')
|
res.append('sf.functional.tool.dismantle')
|
||||||
res.append('purchase.order')
|
res.append('purchase.order')
|
||||||
res.append('mrp.workorder')
|
res.append('mrp.workorder')
|
||||||
|
res.append('sf.maintenance.logs')
|
||||||
|
res.append('quality.cnc.test')
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class SFMessageWork(models.Model):
|
|||||||
_name = 'mrp.workorder'
|
_name = 'mrp.workorder'
|
||||||
_inherit = ['mrp.workorder', 'jikimo.message.dispatch']
|
_inherit = ['mrp.workorder', 'jikimo.message.dispatch']
|
||||||
|
|
||||||
@api.depends('production_availability', 'blocked_by_workorder_ids.state')
|
@api.depends('production_availability', 'blocked_by_workorder_ids.state', 'production_id.tool_state')
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
super(SFMessageWork, self)._compute_state()
|
super(SFMessageWork, self)._compute_state()
|
||||||
for workorder in self:
|
for workorder in self:
|
||||||
@@ -109,16 +109,21 @@ class SFMessageWork(models.Model):
|
|||||||
for item in orders:
|
for item in orders:
|
||||||
if item.date_planned_finished:
|
if item.date_planned_finished:
|
||||||
current_time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
current_time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
current_time_datetime = datetime.strptime(current_time_str, '%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(
|
date_planned_finished_str = self.env['sf.sync.common'].sudo().get_add_time(
|
||||||
item.date_planned_finished.strftime("%Y-%m-%d %H:%M:%S"))
|
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')
|
date_planned_finished = datetime.strptime(date_planned_finished_str, '%Y-%m-%d %H:%M:%S')
|
||||||
logging.info(f"Workorder: {item.production_id.name}, Current Time: {current_time_datetime}, "
|
|
||||||
f"Planned Finish: {date_planned_finished}")
|
|
||||||
twelve_hours_ago = current_time_datetime - timedelta(hours=12)
|
twelve_hours_ago = current_time_datetime - timedelta(hours=12)
|
||||||
if current_time_datetime >= date_planned_finished:
|
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'
|
item.delivery_warning = 'overdue'
|
||||||
elif twelve_hours_ago <= current_time_datetime <= date_planned_finished:
|
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'
|
item.delivery_warning = 'warning'
|
||||||
business_node_ids = {
|
business_node_ids = {
|
||||||
'装夹预调': self.env.ref('sf_message.bussiness_mrp_workorder_pre_overdue_warning').id,
|
'装夹预调': self.env.ref('sf_message.bussiness_mrp_workorder_pre_overdue_warning').id,
|
||||||
@@ -148,6 +153,6 @@ class SFMessageWork(models.Model):
|
|||||||
getattr(item, queue_method_name)(*args)
|
getattr(item, queue_method_name)(*args)
|
||||||
|
|
||||||
def _recover_time_warning_func(self):
|
def _recover_time_warning_func(self):
|
||||||
workorder_done = self.env['mrp.workorder'].search([("state", "=", "done")])
|
workorder_done = self.env['mrp.workorder'].search([("state", "in", ["done", "rework", "cancel"])])
|
||||||
workorder_overdue = workorder_done.filtered(lambda x: x.delivery_warning in ['overdue', 'warning'])
|
workorder_overdue = workorder_done.filtered(lambda x: x.delivery_warning in ['overdue', 'warning'])
|
||||||
workorder_overdue.write({'delivery_warning': 'normal'})
|
workorder_overdue.write({'delivery_warning': 'normal'})
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
from odoo import http
|
from odoo import http, fields, models
|
||||||
from odoo.http import request
|
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,
|
@http.route('/api/cnc_processing/create', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||||
cors="*")
|
cors="*")
|
||||||
@@ -81,24 +82,29 @@ class Sf_Mrs_Connect(http.Controller):
|
|||||||
if files_panel:
|
if files_panel:
|
||||||
for file in files_panel:
|
for file in files_panel:
|
||||||
file_extension = os.path.splitext(file)[1]
|
file_extension = os.path.splitext(file)[1]
|
||||||
logging.info('file_extension:%s' % file_extension)
|
|
||||||
if file_extension.lower() == '.pdf':
|
if file_extension.lower() == '.pdf':
|
||||||
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
||||||
logging.info('panel_file_path:%s' % panel_file_path)
|
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())})
|
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
|
logging.info('更新工作指令完成:%s' % cnc_workorder)
|
||||||
pre_workorder = productions.workorder_ids.filtered(
|
pre_workorder = productions.workorder_ids.filtered(
|
||||||
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework'
|
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework'
|
||||||
'cancel'] and ap.processing_panel == panel)
|
'cancel'] and ap.processing_panel == panel)
|
||||||
if pre_workorder:
|
if pre_workorder:
|
||||||
|
logging.info('更新加工图纸:%s' % pre_workorder)
|
||||||
pre_workorder.write(
|
pre_workorder.write(
|
||||||
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
|
logging.info('更新加工图纸完成:%s' % pre_workorder)
|
||||||
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
||||||
|
logging.info('已更新制造订单编程状态:%s' % productions.ids)
|
||||||
res.update({
|
res.update({
|
||||||
'production_ids': productions.ids
|
'production_ids': productions.ids
|
||||||
})
|
})
|
||||||
|
|
||||||
# 对制造订单所以面的cnc工单的程序用刀进行校验
|
# 对制造订单所以面的cnc工单的程序用刀进行校验
|
||||||
try:
|
try:
|
||||||
|
logging.info(f'已更新制造订单:{productions}')
|
||||||
productions.production_cnc_tool_checkout()
|
productions.production_cnc_tool_checkout()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info(f'对cnc工单的程序用刀进行校验报错:{e}')
|
logging.info(f'对cnc工单的程序用刀进行校验报错:{e}')
|
||||||
@@ -110,5 +116,6 @@ class Sf_Mrs_Connect(http.Controller):
|
|||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'status': -1, 'message': '系统解析失败'}
|
res = {'status': -1, 'message': '系统解析失败'}
|
||||||
|
request.cr.rollback()
|
||||||
logging.info('get_cnc_processing_create error:%s' % e)
|
logging.info('get_cnc_processing_create error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|||||||
@@ -31,19 +31,31 @@ class SfQualityCncTest(models.Model):
|
|||||||
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
|
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
|
||||||
detailed_reason = fields.Text('详细原因')
|
detailed_reason = fields.Text('详细原因')
|
||||||
|
|
||||||
|
# machining_drawings = fields.Binary(related='workorder_id.machining_drawings', string='2D加工图纸', readonly=True)
|
||||||
|
# quality_standard = fields.Binary(related='workorder_id.quality_standard', string='质检标准', readonly=True)
|
||||||
|
|
||||||
def submit_pass(self):
|
def submit_pass(self):
|
||||||
self.write({'result': 'pass', 'test_results': self.test_results, 'state': 'done'})
|
if self.test_results in ['返工', '报废']:
|
||||||
|
raise UserError(_('请重新选择【判定结果】-【检测结果】'))
|
||||||
|
self.write({'result': 'pass', 'test_results': '合格', 'state': 'done'})
|
||||||
self.workorder_id.write({'test_results': self.test_results})
|
self.workorder_id.write({'test_results': self.test_results})
|
||||||
self.workorder_id.button_finish()
|
self.workorder_id.button_finish()
|
||||||
|
|
||||||
def submit_fail(self):
|
def submit_fail(self):
|
||||||
if not self.reason and not self.detailed_reason and not self.test_results:
|
if not self.test_results:
|
||||||
raise UserError(_('请填写【判定结果】里的信息'))
|
raise UserError(_('请填写【判定结果】里的信息'))
|
||||||
else:
|
if self.test_results == '合格':
|
||||||
self.write({'result': 'fail', 'test_results': self.test_results, 'state': 'done'})
|
raise UserError(_('请重新选择【判定结果】-【检测结果】'))
|
||||||
self.workorder_id.write(
|
self.write({'result': 'fail', 'test_results': self.test_results, 'state': 'done'})
|
||||||
{'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason})
|
self.workorder_id.write(
|
||||||
self.workorder_id.button_finish()
|
{'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason})
|
||||||
|
self.workorder_id.button_finish()
|
||||||
|
|
||||||
|
@api.onchange('test_results')
|
||||||
|
def _onchange_test_results(self):
|
||||||
|
if self.test_results == '合格':
|
||||||
|
self.reason = False
|
||||||
|
self.detailed_reason = False
|
||||||
|
|
||||||
|
|
||||||
class SfQualityWorkOrder(models.Model):
|
class SfQualityWorkOrder(models.Model):
|
||||||
@@ -62,7 +74,9 @@ class SfQualityWorkOrder(models.Model):
|
|||||||
|
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
res = super(SfQualityWorkOrder, self).write(vals)
|
res = super(SfQualityWorkOrder, self).write(vals)
|
||||||
if self.state == 'to be detected':
|
for item in self:
|
||||||
quality_cnc_test = self.env['quality.cnc.test'].search([('workorder_id', '=', self.id)])
|
if item.state == 'to be detected':
|
||||||
if not quality_cnc_test:
|
quality_cnc_test = self.env['quality.cnc.test'].search([('workorder_id', '=', item.id)])
|
||||||
self.env['quality.cnc.test'].sudo().create({'workorder_id': self.id})
|
if not quality_cnc_test:
|
||||||
|
self.env['quality.cnc.test'].sudo().create({'workorder_id': item.id})
|
||||||
|
return res
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<field name="name">加工质检单编码规则</field>
|
<field name="name">加工质检单编码规则</field>
|
||||||
<field name="code">quality.cnc.test</field>
|
<field name="code">quality.cnc.test</field>
|
||||||
<field name="prefix">QCT</field>
|
<field name="prefix">QCT</field>
|
||||||
<field name="padding">4</field>
|
<field name="padding">5</field>
|
||||||
<field name="company_id" eval="False"/>
|
<field name="company_id" eval="False"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -20,10 +20,12 @@
|
|||||||
<field name="product_id"/>
|
<field name="product_id"/>
|
||||||
<field name="part_number"/>
|
<field name="part_number"/>
|
||||||
<field name="number"/>
|
<field name="number"/>
|
||||||
<field name="state"/>
|
<field name="state" widget="badge"
|
||||||
<field name="result"/>
|
decoration-success="state == 'done'"
|
||||||
<field name="write_uid" widget='many2one_avatar_user' string="判定人" optional="hide"/>
|
decoration-warning="state == 'waiting'"/>
|
||||||
<field name="write_date" string="判定时间" optional="hide"/>
|
<field name="result" widget="badge"
|
||||||
|
decoration-success="result == 'pass'"
|
||||||
|
decoration-danger="result == 'fail'"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -89,7 +91,6 @@
|
|||||||
<group>
|
<group>
|
||||||
<field name="part_number"/>
|
<field name="part_number"/>
|
||||||
<field name="processing_panel"/>
|
<field name="processing_panel"/>
|
||||||
<field name="detection_report"/>
|
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<notebook>
|
<notebook>
|
||||||
@@ -99,17 +100,19 @@
|
|||||||
<page string="判定结果">
|
<page string="判定结果">
|
||||||
<group>
|
<group>
|
||||||
<field name="test_results" attrs="{'readonly': [('state','=', 'done')]}"/>
|
<field name="test_results" attrs="{'readonly': [('state','=', 'done')]}"/>
|
||||||
<field name="reason" attrs="{'readonly': [('state','=', 'done')]}"/>
|
<field name="reason"
|
||||||
<field name="detailed_reason" attrs="{'readonly': [('state','=', 'done')]}"/>
|
attrs="{'readonly': [('state','=', 'done')],'required': [('test_results','in', ['返工','报废'])],'invisible': [('test_results','in', ['合格',False])]}"/>
|
||||||
|
<field name="detailed_reason"
|
||||||
|
attrs="{'readonly': [('state','=', 'done')],'required': [('reason','!=', False)],'invisible': [('test_results','in', ['合格',False])]}"/>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
<page string="2D图纸">
|
<page string="2D图纸">
|
||||||
<!-- <field name="detection_report" string="" widget="pdf_viewer"/>-->
|
<!-- <field name="machining_drawings" string="" widget="pdf_viewer"/>-->
|
||||||
</page>
|
</page>
|
||||||
<page string="客户质量标准">
|
<page string="客户质量标准">
|
||||||
<!-- <field name="detection_report" string="" widget="pdf_viewer"/>-->
|
<!-- <field name="quality_standard" string=""/>-->
|
||||||
</page>
|
</page>
|
||||||
<page string="其他" attrs="{'readonly': [('state','=', 'done')]}">
|
<page string="其他" attrs="{'invisible': [('state','=', 'waiting')]}">
|
||||||
<group>
|
<group>
|
||||||
<field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/>
|
<field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/>
|
||||||
<field name="write_date" string="判定时间" readonly="1"/>
|
<field name="write_date" string="判定时间" readonly="1"/>
|
||||||
@@ -171,7 +174,7 @@
|
|||||||
<field name="name">驾驶舱</field>
|
<field name="name">驾驶舱</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
<field name="res_model">quality.cnc.test</field>
|
<field name="res_model">quality.cnc.test</field>
|
||||||
<field name="view_mode">kanban,form</field>
|
<field name="view_mode">kanban,tree,form</field>
|
||||||
<field name="view_id" ref="quality_cnc_test_view_kanban"/>
|
<field name="view_id" ref="quality_cnc_test_view_kanban"/>
|
||||||
<field name="search_view_id" ref="quality_cnc_test_search"/>
|
<field name="search_view_id" ref="quality_cnc_test_search"/>
|
||||||
<field name="domain">[]</field>
|
<field name="domain">[]</field>
|
||||||
|
|||||||
@@ -34,36 +34,36 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="quality_point_view_form_inherit_sf">
|
<record model="ir.ui.view" id="quality_point_view_form_inherit_sf">
|
||||||
<field name="name">quality.point.form.inherit.sf</field>
|
<field name="name">quality.point.form.inherit.sf</field>
|
||||||
<field name="model">quality.point</field>
|
<field name="model">quality.point</field>
|
||||||
<field name="inherit_id" ref="quality.quality_point_view_form"/>
|
<field name="inherit_id" ref="quality.quality_point_view_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<!-- <xpath expr="//sheet//group//group//field[@name='title']" position="replace"> -->
|
<!-- <xpath expr="//sheet//group//group//field[@name='title']" position="replace"> -->
|
||||||
<!-- <field name="title" class="custom_required" required="1"/> -->
|
<!-- <field name="title" class="custom_required" required="1"/> -->
|
||||||
<!-- </xpath> -->
|
<!-- </xpath> -->
|
||||||
<xpath expr="//sheet//group//group//field[@name='title']" position="attributes">
|
<xpath expr="//sheet//group//group//field[@name='title']" position="attributes">
|
||||||
<attribute name="class">custom_required</attribute>
|
<attribute name="class">custom_required</attribute>
|
||||||
<attribute name="required">1</attribute>
|
<attribute name="required">1</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//sheet//group//group//field[@name='picking_type_ids']" position="attributes">
|
<xpath expr="//sheet//group//group//field[@name='picking_type_ids']" position="attributes">
|
||||||
<attribute name="class">custom_required</attribute>
|
<attribute name="class">custom_required</attribute>
|
||||||
<attribute name="required">1</attribute>
|
<attribute name="required">1</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record model="ir.ui.view" id="sf_quality_point_view_form_inherit_quality_control">
|
<record model="ir.ui.view" id="sf_quality_point_view_form_inherit_quality_control">
|
||||||
<field name="name">sf.quality.point.form.inherit.sf</field>
|
<field name="name">sf.quality.point.form.inherit.sf</field>
|
||||||
<field name="model">quality.point</field>
|
<field name="model">quality.point</field>
|
||||||
<field name="inherit_id" ref="quality_control.quality_point_view_form_inherit_quality_control"/>
|
<field name="inherit_id" ref="quality_control.quality_point_view_form_inherit_quality_control"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='measure_on']" position="attributes">
|
<xpath expr="//field[@name='measure_on']" position="attributes">
|
||||||
<attribute name="class">custom_required</attribute>
|
<attribute name="class">custom_required</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='measure_frequency_type']" position="attributes">
|
<xpath expr="//field[@name='measure_frequency_type']" position="attributes">
|
||||||
<attribute name="class">custom_required</attribute>
|
<attribute name="class">custom_required</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ class ReSaleOrder(models.Model):
|
|||||||
|
|
||||||
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效')
|
delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效')
|
||||||
|
|
||||||
|
|
||||||
# 业务平台分配工厂后在智能工厂先创建销售订单
|
# 业务平台分配工厂后在智能工厂先创建销售订单
|
||||||
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
|
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
|
||||||
deadline_of_delivery, payments_way, pay_way):
|
deadline_of_delivery, payments_way, pay_way):
|
||||||
@@ -128,7 +127,9 @@ class ReSaleOrder(models.Model):
|
|||||||
'price_unit': product.list_price,
|
'price_unit': product.list_price,
|
||||||
'product_uom_qty': item['number'],
|
'product_uom_qty': item['number'],
|
||||||
'model_glb_file': base64.b64decode(item['model_file']),
|
'model_glb_file': base64.b64decode(item['model_file']),
|
||||||
'remark': item.get('remark')
|
'remark': item.get('remark'),
|
||||||
|
'is_incoming_material': item.get('is_incoming_material'),
|
||||||
|
'incoming_size': item.get('incoming_size'),
|
||||||
}
|
}
|
||||||
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
|
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
|
||||||
|
|
||||||
@@ -169,6 +170,9 @@ class ResaleOrderLine(models.Model):
|
|||||||
check_status = fields.Selection(related='order_id.check_status')
|
check_status = fields.Selection(related='order_id.check_status')
|
||||||
remark = fields.Char('备注')
|
remark = fields.Char('备注')
|
||||||
|
|
||||||
|
is_incoming_material = fields.Boolean('是否带料', default=False)
|
||||||
|
incoming_size = fields.Char('带料尺寸')
|
||||||
|
|
||||||
@api.depends('product_template_id')
|
@api.depends('product_template_id')
|
||||||
def _compute_model_glb_file(self):
|
def _compute_model_glb_file(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
|
|||||||
@@ -80,8 +80,8 @@
|
|||||||
<field name="unit_price"/>
|
<field name="unit_price"/>
|
||||||
<field name="price" options="{'format': false}"/>
|
<field name="price" options="{'format': false}"/>
|
||||||
<field name="part_drawing_number"/>
|
<field name="part_drawing_number"/>
|
||||||
<field name="machining_drawings" filename="machining_drawings_name" widget="pdf_viewer"/>
|
<!-- <field name="machining_drawings" filename="machining_drawings_name" widget="pdf_viewer"/>-->
|
||||||
<field name="machining_drawings_name" invisible="1"/>
|
<!-- <field name="machining_drawings_name" invisible="1"/>-->
|
||||||
<field name="sale_order_id"
|
<field name="sale_order_id"
|
||||||
attrs='{"invisible": [("sale_order_id","=",False)],"readonly": [("sale_order_id","!=",False)]}'/>
|
attrs='{"invisible": [("sale_order_id","=",False)],"readonly": [("sale_order_id","!=",False)]}'/>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -118,6 +118,8 @@
|
|||||||
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="replace">
|
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="replace">
|
||||||
<field name="name" widget="section_and_note_text" optional="show"
|
<field name="name" widget="section_and_note_text" optional="show"
|
||||||
string="参数说明(长/宽/高/体积/精度/材质)"/>
|
string="参数说明(长/宽/高/体积/精度/材质)"/>
|
||||||
|
<field name="is_incoming_material" optional="hide"/>
|
||||||
|
<field name="incoming_size" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<field name="user_id" position="attributes">
|
<field name="user_id" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||||
@@ -162,6 +164,11 @@
|
|||||||
<xpath expr="//button[@name='action_cancel']" position="attributes">
|
<xpath expr="//button[@name='action_cancel']" position="attributes">
|
||||||
<attribute name="string">拒绝接单</attribute>
|
<attribute name="string">拒绝接单</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<!--新增带料字段-->
|
||||||
|
<xpath expr="//field[@name='order_line']/form//group//group//field[@name='analytic_distribution']" position="after">
|
||||||
|
<field name="is_incoming_material"/>
|
||||||
|
<field name="incoming_size" attrs="{'invisible': [('is_incoming_material', '=', False)],'readonly':1}"/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -265,7 +272,6 @@
|
|||||||
</field>
|
</field>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="sale.product_template_action" model="ir.actions.act_window">
|
<record id="sale.product_template_action" model="ir.actions.act_window">
|
||||||
<field name="context">{"search_default_categ_id":1,
|
<field name="context">{"search_default_categ_id":1,
|
||||||
"search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1}
|
"search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1}
|
||||||
|
|||||||
@@ -687,7 +687,7 @@ class FunctionalToolAssembly(models.Model):
|
|||||||
sf_cam_work_order_program_knife_plan_id = fields.Many2one('sf.cam.work.order.program.knife.plan',
|
sf_cam_work_order_program_knife_plan_id = fields.Many2one('sf.cam.work.order.program.knife.plan',
|
||||||
'CAM工单程序用刀计划', readonly=True)
|
'CAM工单程序用刀计划', readonly=True)
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True, groups='base.user_root')
|
active = fields.Boolean(string='已归档', default=True)
|
||||||
|
|
||||||
code = fields.Char('功能刀具编码', compute='_compute_code')
|
code = fields.Char('功能刀具编码', compute='_compute_code')
|
||||||
|
|
||||||
|
|||||||
@@ -554,7 +554,7 @@ class RealTimeDistributionOfFunctionalTools(models.Model):
|
|||||||
sf_functional_tool_entity_ids = fields.One2many('sf.functional.cutting.tool.entity', 'safe_inventory_id',
|
sf_functional_tool_entity_ids = fields.One2many('sf.functional.cutting.tool.entity', 'safe_inventory_id',
|
||||||
string='功能刀具信息')
|
string='功能刀具信息')
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True, groups='base.user_root')
|
active = fields.Boolean(string='已归档', default=True)
|
||||||
|
|
||||||
@api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle',
|
@api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle',
|
||||||
'functional_name_id.functional_cutting_tool_model_id')
|
'functional_name_id.functional_cutting_tool_model_id')
|
||||||
|
|||||||
@@ -170,11 +170,14 @@ class MrpProduction(models.Model):
|
|||||||
# 修改cnc程序的‘刀具状态’
|
# 修改cnc程序的‘刀具状态’
|
||||||
workorder_ids = self.env['mrp.workorder'].sudo().search([('production_id', 'in', self.ids)])
|
workorder_ids = self.env['mrp.workorder'].sudo().search([('production_id', 'in', self.ids)])
|
||||||
if invalid_tool:
|
if invalid_tool:
|
||||||
|
logging.info(f'无效刀:{invalid_tool}')
|
||||||
# 修改cnc程序的‘刀具状态’为 ‘无效刀’
|
# 修改cnc程序的‘刀具状态’为 ‘无效刀’
|
||||||
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
||||||
[('workorder_id', 'in', workorder_ids.ids), ('cutting_tool_name', 'in', invalid_tool)])
|
[('workorder_id', 'in', workorder_ids.ids), ('cutting_tool_name', 'in', invalid_tool)])
|
||||||
if cnc_ids:
|
if cnc_ids:
|
||||||
cnc_ids.write({'tool_state': '2'})
|
for cnc_id in cnc_ids:
|
||||||
|
cnc_id.tool_state = '2'
|
||||||
|
# cnc_ids.write({'tool_state': '2'})
|
||||||
# 创建制造订单无效刀检测结果记录
|
# 创建制造订单无效刀检测结果记录
|
||||||
for production_id in self:
|
for production_id in self:
|
||||||
for processing_panel in list(set(invalid_tool_processing_panel)):
|
for processing_panel in list(set(invalid_tool_processing_panel)):
|
||||||
@@ -194,20 +197,27 @@ class MrpProduction(models.Model):
|
|||||||
# 自动调用重新获取编程的方法
|
# 自动调用重新获取编程的方法
|
||||||
logging.info('cnc用刀校验到无效刀自动调用重新编程方法:update_programming_state()')
|
logging.info('cnc用刀校验到无效刀自动调用重新编程方法:update_programming_state()')
|
||||||
self[0].update_programming_state()
|
self[0].update_programming_state()
|
||||||
# 修改制造订单 编程状态变为“编程中”
|
self[0].write({'is_rework': False})
|
||||||
self.write({'programming_state': '编程中', 'work_state': '编程中'})
|
# 修改制造订单 编程状态变为“编程中” 制造订单状态为‘返工’
|
||||||
|
self.write({'programming_state': '编程中', 'work_state': '编程中', 'state': 'rework'})
|
||||||
if missing_tool_1:
|
if missing_tool_1:
|
||||||
|
logging.info(f'线边、机内缺刀:{missing_tool_1}')
|
||||||
# 修改 修改cnc程序的‘刀具状态’ 为 ‘缺刀’
|
# 修改 修改cnc程序的‘刀具状态’ 为 ‘缺刀’
|
||||||
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
||||||
[('workorder_id', 'in', workorder_ids.ids), ('cutting_tool_name', 'in', missing_tool_1)])
|
[('workorder_id', 'in', workorder_ids.ids), ('cutting_tool_name', 'in', missing_tool_1)])
|
||||||
if cnc_ids:
|
if cnc_ids:
|
||||||
cnc_ids.write({'tool_state': '1'})
|
for cnc_id in cnc_ids:
|
||||||
if missing_tool_2 and not invalid_tool:
|
cnc_id.tool_state = '1'
|
||||||
|
# cnc_ids.write({'tool_state': '1'})
|
||||||
|
if missing_tool_2 and invalid_tool == []:
|
||||||
|
logging.info(f'库存缺刀:{missing_tool_2}')
|
||||||
# 调用CAM工单程序用刀计划创建方法
|
# 调用CAM工单程序用刀计划创建方法
|
||||||
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
cnc_ids = self.env['sf.cnc.processing'].sudo().search(
|
||||||
[('workorder_id', 'in', workorder_ids.filtered(lambda a: a.production_id == self[0].id).ids),
|
[('workorder_id', 'in', workorder_ids.filtered(lambda a: a.production_id == self[0]).ids),
|
||||||
('cutting_tool_name', 'in', missing_tool_2)])
|
('cutting_tool_name', 'in', missing_tool_2)])
|
||||||
if cnc_ids:
|
if cnc_ids:
|
||||||
logging.info('调用CAM工单程序用刀计划创建方法!!!')
|
logging.info('调用CAM工单程序用刀计划创建方法!!!')
|
||||||
self.env['sf.cam.work.order.program.knife.plan'].sudo().create_cam_work_plan(cnc_ids)
|
self.env['sf.cam.work.order.program.knife.plan'].sudo().create_cam_work_plan(cnc_ids)
|
||||||
|
if not invalid_tool and not missing_tool_1:
|
||||||
|
logging.info('校验cnc用刀正常!!!')
|
||||||
logging.info('工单cnc程序用刀校验完成!!!')
|
logging.info('工单cnc程序用刀校验完成!!!')
|
||||||
|
|||||||
Reference in New Issue
Block a user