优化质量检测(加工检测)及消息推送

This commit is contained in:
jinling.yang
2024-10-14 17:53:38 +08:00
parent d8bade64e1
commit 2a067778bc
18 changed files with 219 additions and 79 deletions

View File

@@ -1033,7 +1033,7 @@
name="Overview"
action="quality_alert_team_action"
parent="menu_quality_root"
sequence="5"/>
sequence="5" active="False"/>
<menuitem
id="menu_quality_control"

View File

@@ -615,14 +615,13 @@ class Manufacturing_Connect(http.Controller):
if ret['IsComplete'] is True:
# 向AGV任务调度下发下产线任务
workorders = request.env['mrp.workorder'].browse(workorder_ids)
res['workorder_ids'] = workorder_ids
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '下产线', workorders)
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
except RepeatTaskException as e:
logging.info('AGVToProduct error:%s' % e)
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e), 'workorder_ids': workorder_ids}
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -227,7 +227,7 @@
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
decoration-danger="tag_type == '重新加工'"/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
<field name="rfid_code" force_save="1" readonly="0" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
<field name="rfid_code_old" readonly="0" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>

View File

@@ -11,7 +11,7 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock'],
'depends': ['sale', 'purchase', 'sf_plan', 'jikimo_message_notify', 'stock', 'sf_quality'],
'data': [
'data/bussiness_node.xml',
'data/cron_data.xml',

View File

@@ -1 +1 @@
from . import main
from . import main

View File

@@ -89,5 +89,10 @@
<field name="model">mrp.workorder</field>
</record>
<record id="bussiness_quality_cnc_test" model="jikimo.message.bussiness.node">
<field name="name">待质量判定</field>
<field name="model">quality.cnc.test</field>
</record>
</data>
</odoo>

View File

@@ -25,5 +25,18 @@
<field name="user_id" ref="base.user_root"/>
<field name="active" eval="True"/>
</record>
<record model="ir.cron" id="ir_cron_mrp_workorder_overdue_warning">
<field name="name">检查工单是否完成并恢复正常时效</field>
<field name="model_id" ref="model_mrp_workorder"/>
<field name="state">code</field>
<field name="code">model._recover_time_warning_func()</field>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="user_id" ref="base.user_root"/>
<field name="active" eval="True"/>
</record>
</data>
</odoo>

View File

@@ -227,5 +227,16 @@
<!--单号:组装任务单[{{name}}]({{request_url}})-->
<!--事项:{{use_tool_time}}前完成组装</field>-->
<!-- </record>-->
<record id="template_quality_cnc_test" model="jikimo.message.template">
<field name="name">待质量判定</field>
<field name="model_id" ref="sf_quality.model_quality_cnc_test"/>
<field name="model">quality.cnc.test</field>
<field name="bussiness_node_id" ref="bussiness_quality_cnc_test"/>
<field name="msgtype">markdown</field>
<field name="urgency">normal</field>
<field name="content">### 待质量判定提醒
事项:共有[{{judge_num}}]({{url}})个工单需判定质量结果</field>
</record>
</data>
</odoo>

View File

@@ -7,3 +7,4 @@ from . import sf_message_functional_tool_assembly
from . import sf_message_purchase
from . import sf_message_workorder
from . import sf_message_functional_tool_dismantle
from . import sf_message_quality_cnc_test

View File

@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import logging
from datetime import datetime, timedelta
from odoo import models, fields, api, _
class SFMessageQualityCncTest(models.Model):
_name = 'quality.cnc.test'
_inherit = ['quality.cnc.test', 'jikimo.message.dispatch']
def create(self, vals_list):
res = super(SFMessageQualityCncTest, self).create(vals_list)
if res:
try:
logging.info('add_queue res:%s' % res)
res.add_queue('待质量判定')
except Exception as e:
logging.info('add_queue error:%s' % e)
return res
# 继承并重写jikimo.message.dispatch的_get_message()
def _get_message(self, message_queue_ids):
contents = []
url = self.env['ir.config_parameter'].get_param('web.base.url')
i = 0
for item in message_queue_ids:
if item.message_template_id.bussiness_node_id.name == '待质量判定':
content = item.message_template_id.content
i += 1
if i >= 1:
action_id = self.env.ref('sf_quality.action_quality_cnc_test').id
url_with_id = f"{url}/web#view_type=list&action={action_id}"
content_template = content.replace('{{judge_num}}', str(i))
content_template = content_template.replace('{{url}}', url_with_id)
contents.append(content_template)
return contents

View File

@@ -148,3 +148,8 @@ class SFMessageWork(models.Model):
args = [f'{item.routing_type}{overdue_message}']
# 获取add_queue方法并调用它传入参数列表
getattr(item, queue_method_name)(*args)
def _recover_time_warning_func(self):
workorder_done = self.env['mrp.workorder'].search([("state", "=", "done")])
workorder_overdue = workorder_done.filtered(lambda x: x.delivery_warning == 'overdue')
workorder_overdue.write({'delivery_warning': 'normal'})

View File

@@ -2,4 +2,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import controller

View File

@@ -1 +0,0 @@
from . import workorder

View File

@@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
import json
import requests
import logging
from odoo import http
from odoo.http import request
from odoo.addons.sf_manufacturing.controllers.controllers import Manufacturing_Connect
from odoo.addons.sf_base.commons.common import Common
_logger = logging.getLogger(__name__)
class SfQualityConnect(Manufacturing_Connect):
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVDownProduct(self, **kw):
res = super(SfQualityConnect, self).AGVDownProduct(**kw)
res = json.loads(res)
if res.get('workorder_ids'):
try:
_logger.info('已下产线的工单:%s' % res.get('workorder_ids'))
for order_id in res['workorder_ids']:
request.env['quality.cnc.test'].sudo().create(
{'workorder_id': order_id, 'write_uid': False, 'write_date': False})
except Exception as e:
_logger.info('AGV运送下产线接口:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -25,23 +25,44 @@ class SfQualityCncTest(models.Model):
('pass', '合格'),
('fail', '不合格')], string='判定结果')
number = fields.Integer('数量', default=1)
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果")
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], string="检测结果")
reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
detailed_reason = fields.Text('详细原因')
def submit_pass(self):
self.write({'result': 'pass', 'test_results': '合格', 'state': 'done'})
self.write({'result': 'pass', 'test_results': self.test_results, 'state': 'done'})
self.workorder_id.write({'test_results': self.test_results})
self.workorder_id.button_finish()
def submit_fail(self):
if not self.reason and not self.detailed_reason:
raise UserError(_('请填写【检测情况】里的信息'))
if not self.reason and not self.detailed_reason and not self.test_results:
raise UserError(_('请填写【判定结果】里的信息'))
else:
self.write({'result': 'fail', 'test_results': '合格', 'state': 'done'})
self.write({'result': 'fail', 'test_results': self.test_results, 'state': 'done'})
self.workorder_id.write(
{'test_results': self.test_results, 'reason': self.reason, 'detailed_reason': self.detailed_reason})
self.workorder_id.button_finish()
class SfQualityWorkOrder(models.Model):
_inherit = 'mrp.workorder'
def button_finish(self):
super(SfQualityWorkOrder, self).button_finish()
if self.routing_type == 'CNC加工':
quality_cnc_test = self.env['quality.cnc.test'].search([('workorder_id', '=', self.id)])
if quality_cnc_test:
quality_cnc_test.write({'result': 'fail' if self.test_results in ['返工', '报废'] else 'pass',
'test_results': self.test_results, 'state': 'done',
'reason': self.reason,
'detailed_reason': self.detailed_reason,
'detection_report': self.detection_report})
def write(self, vals):
res = super(SfQualityWorkOrder, self).write(vals)
if self.state == 'to be detected':
quality_cnc_test = self.env['quality.cnc.test'].search([('workorder_id', '=', self.id)])
if not quality_cnc_test:
self.env['quality.cnc.test'].sudo().create({'workorder_id': self.id})

View File

@@ -70,5 +70,8 @@ access_stock_move_group_quality_director,stock_move_group_quality_director,stock
access_quality_cnc_test_group_quality,quality_cnc_test_group_quality,model_quality_cnc_test,sf_base.group_quality,1,1,0,0
access_quality_cnc_test_group_quality_director,quality_cnc_test_group_quality_director,model_quality_cnc_test,sf_base.group_quality_director,1,1,0,0
access_quality_cnc_test_group_sf_equipment_user,quality_cnc_test_group_sf_equipment_user,model_quality_cnc_test,sf_base.group_sf_equipment_user,1,1,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
70
71
72
73
74
75
76
77

View File

@@ -39,7 +39,7 @@
filter_domain="[('production_id', 'ilike', self)]"/>
<field name="product_id" string="产品"
filter_domain="[('product_id', 'ilike', self)]"/>
<searchpanel class="account_root">
<searchpanel>
<field name="state" icon="fa-filter" enable_counters="1"/>
<field name="result" icon="fa-filter" enable_counters="1"/>
</searchpanel>
@@ -96,14 +96,20 @@
<page string="检测报告">
<field name="detection_report" string="" widget="pdf_viewer"/>
</page>
<page string="检测情况">
<page string="判定结果">
<group>
<field name="test_results"/>
<field name="reason"/>
<field name="detailed_reason"/>
<field name="test_results" attrs="{'readonly': [('state','=', 'done')]}"/>
<field name="reason" attrs="{'readonly': [('state','=', 'done')]}"/>
<field name="detailed_reason" attrs="{'readonly': [('state','=', 'done')]}"/>
</group>
</page>
<page string="其他">
<page string="2D图纸">
<!-- <field name="detection_report" string="" widget="pdf_viewer"/>-->
</page>
<page string="客户质量标准">
<!-- <field name="detection_report" string="" widget="pdf_viewer"/>-->
</page>
<page string="其他" attrs="{'readonly': [('state','=', 'done')]}">
<group>
<field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/>
<field name="write_date" string="判定时间" readonly="1"/>
@@ -115,6 +121,54 @@
</field>
</record>
<record id="quality_cnc_test_view_kanban" model="ir.ui.view">
<field name="name">quality.cnc.test.view.kanban</field>
<field name="model">quality.cnc.test</field>
<field name="arch" type="xml">
<kanban class="oe_background_grey o_kanban_dashboard o_quality_kanban" create="0">
<templates>
<t t-name="kanban-box">
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record oe_kanban_card oe_kanban_global_click">
<div class="add_flex">
<div class="o_kanban_card_header">
<div class="o_kanban_card_header_title">
<field name="name"/>
</div>
</div>
<!-- 内容 -->
<div class="container o_kanban_card_content">
<div>
<field name="production_id"/>
</div>
<div>
<field name="processing_panel"/>
</div>
<div>
<field name="state"
widget="label_selection"
options="{'classes': {'waiting': 'warning', 'done': 'success'}}"/>
</div>
</div>
</div>
<!-- <div class="show_state" t-attf-class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record oe_kanban_card oe_kanban_global_click-->
<!-- ">-->
<!-- <div t-attf-class="#{record.result.raw_value == 'pass' ? 'color_1' : ''}"></div>-->
<!-- <div t-attf-class="#{record.result.raw_value == 'fail' ? 'color_2' : ''}"></div>-->
<!-- <p class="o_kanban_record_bottom"-->
<!-- t-attf-class="#{record.result.raw_value == 'pass' ? 'font_color_1' : ''}-->
<!-- #{record.state.result == 'fail' ? 'font_color_2' : ''}}">-->
<!-- <field name="result"/>-->
<!-- </p>-->
<!-- </div>-->
</div>
</t>
</t>
</templates>
</kanban>
</field>
</record>
<menuitem
id="menu_quality_cnc_test"
name="加工质检"
@@ -123,4 +177,26 @@
parent="quality_control.menu_quality_control"
/>
<record id="action_quality_cnc_test_kanban" model="ir.actions.act_window">
<field name="name">驾驶舱</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">quality.cnc.test</field>
<field name="view_mode">kanban,form</field>
<field name="view_id" ref="quality_cnc_test_view_kanban"/>
<field name="search_view_id" ref="quality_cnc_test_search"/>
<field name="domain">[]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
暂无加工质检单
</p>
</field>
</record>
<menuitem id="menu_quality_cnc_test_dashboard"
name="驾驶舱"
action="action_quality_cnc_test_kanban"
parent="quality_control.menu_quality_root"
sequence="5"/>
</odoo>

View File

@@ -34,36 +34,36 @@
</field>
</record>
<!-- <record model="ir.ui.view" id="quality_point_view_form_inherit_sf">-->
<!-- <field name="name">quality.point.form.inherit.sf</field>-->
<!-- <field name="model">quality.point</field>-->
<!-- <field name="inherit_id" ref="quality.quality_point_view_form"/>-->
<!-- <field name="arch" type="xml">-->
<!--&lt;!&ndash; <xpath expr="//sheet//group//group//field[@name='title']" position="replace"> &ndash;&gt;-->
<!--&lt;!&ndash; <field name="title" class="custom_required" required="1"/> &ndash;&gt;-->
<!--&lt;!&ndash; </xpath> &ndash;&gt;-->
<!-- <xpath expr="//sheet//group//group//field[@name='title']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- <attribute name="required">1</attribute>-->
<!-- </xpath>-->
<!-- <xpath expr="//sheet//group//group//field[@name='picking_type_ids']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- <attribute name="required">1</attribute>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<!-- <record model="ir.ui.view" id="quality_point_view_form_inherit_sf">-->
<!-- <field name="name">quality.point.form.inherit.sf</field>-->
<!-- <field name="model">quality.point</field>-->
<!-- <field name="inherit_id" ref="quality.quality_point_view_form"/>-->
<!-- <field name="arch" type="xml">-->
<!--&lt;!&ndash; <xpath expr="//sheet//group//group//field[@name='title']" position="replace"> &ndash;&gt;-->
<!--&lt;!&ndash; <field name="title" class="custom_required" required="1"/> &ndash;&gt;-->
<!--&lt;!&ndash; </xpath> &ndash;&gt;-->
<!-- <xpath expr="//sheet//group//group//field[@name='title']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- <attribute name="required">1</attribute>-->
<!-- </xpath>-->
<!-- <xpath expr="//sheet//group//group//field[@name='picking_type_ids']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- <attribute name="required">1</attribute>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<!-- <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="model">quality.point</field>-->
<!-- <field name="inherit_id" ref="quality_control.quality_point_view_form_inherit_quality_control"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//field[@name='measure_on']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- </xpath>-->
<!-- <xpath expr="//field[@name='measure_frequency_type']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<!-- <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="model">quality.point</field>-->
<!-- <field name="inherit_id" ref="quality_control.quality_point_view_form_inherit_quality_control"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//field[@name='measure_on']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- </xpath>-->
<!-- <xpath expr="//field[@name='measure_frequency_type']" position="attributes">-->
<!-- <attribute name="class">custom_required</attribute>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
</odoo>