增加AGV调度功能,工单页面增加自定义筛选字段
This commit is contained in:
@@ -2,3 +2,4 @@ from . import ftp_client
|
|||||||
from . import ftp_operate
|
from . import ftp_operate
|
||||||
from . import py2opcua
|
from . import py2opcua
|
||||||
from . import res_config_setting
|
from . import res_config_setting
|
||||||
|
from . import mrp_workorder
|
||||||
|
|||||||
38
sf_machine_connect/models/mrp_workorder.py
Normal file
38
sf_machine_connect/models/mrp_workorder.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from odoo import fields, models, api
|
||||||
|
|
||||||
|
|
||||||
|
class ResMrpWorkOrder(models.Model):
|
||||||
|
_inherit = 'mrp.workorder'
|
||||||
|
|
||||||
|
mixed_search_field = fields.Char(string='坯料产品名称/RFID')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def web_read_group(self, domain, fields, groupby, limit=None, offset=0, orderby=False,
|
||||||
|
lazy=True, expand=False, expand_limit=None, expand_orderby=False):
|
||||||
|
domain = domain or []
|
||||||
|
for index, item in enumerate(domain):
|
||||||
|
if isinstance(item, list):
|
||||||
|
if item[0] == 'mixed_search_field':
|
||||||
|
if self._is_rfid_code(item[2]):
|
||||||
|
domain[index] = ['rfid_code', item[1], item[2]]
|
||||||
|
else:
|
||||||
|
domain[index] = ['product_tmpl_name', item[1], item[2]]
|
||||||
|
|
||||||
|
return super(ResMrpWorkOrder, self).web_read_group(domain, fields, groupby, limit=limit, offset=offset, orderby=orderby,
|
||||||
|
lazy=lazy, expand=expand, expand_limit=expand_limit, expand_orderby=expand_orderby)
|
||||||
|
|
||||||
|
def _is_rfid_code(self, tag):
|
||||||
|
"""
|
||||||
|
判断是否是rfid_code
|
||||||
|
"""
|
||||||
|
# 基于长度判断(假设RFID标签长度为10到16个字符)
|
||||||
|
if not 10 <= len(tag) <= 16:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 基于字符集判断(仅包含数字和字母)
|
||||||
|
if not re.match("^[0-9]*$", tag):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
<filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/>
|
<filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='production_id']" position="before">
|
<xpath expr="//field[@name='production_id']" position="before">
|
||||||
|
<field name="mixed_search_field"/>
|
||||||
<field name="product_tmpl_name"/>
|
<field name="product_tmpl_name"/>
|
||||||
<field name="rfid_code"/>
|
<field name="rfid_code"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
'wizard/rework_wizard_views.xml',
|
'wizard/rework_wizard_views.xml',
|
||||||
'wizard/production_wizard_views.xml',
|
'wizard/production_wizard_views.xml',
|
||||||
'views/mrp_views_menus.xml',
|
'views/mrp_views_menus.xml',
|
||||||
|
'views/agv_dispatch_views.xml',
|
||||||
'views/stock_lot_views.xml',
|
'views/stock_lot_views.xml',
|
||||||
'views/mrp_production_addional_change.xml',
|
'views/mrp_production_addional_change.xml',
|
||||||
'views/mrp_routing_workcenter_view.xml',
|
'views/mrp_routing_workcenter_view.xml',
|
||||||
@@ -30,7 +31,6 @@
|
|||||||
'views/model_type_view.xml',
|
'views/model_type_view.xml',
|
||||||
'views/agv_setting_views.xml',
|
'views/agv_setting_views.xml',
|
||||||
'views/sf_maintenance_equipment.xml',
|
'views/sf_maintenance_equipment.xml',
|
||||||
|
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
|
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
logging.info('LocationChange error:%s' % e)
|
logging.info('LocationChange error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
cors="*")
|
cors="*")
|
||||||
def AGVToProduct(self, **kw):
|
def AGVToProduct(self, **kw):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ from . import stock
|
|||||||
from . import res_user
|
from . import res_user
|
||||||
from . import production_line_base
|
from . import production_line_base
|
||||||
from . import agv_setting
|
from . import agv_setting
|
||||||
|
from . import agv_dispatch
|
||||||
|
|||||||
50
sf_manufacturing/models/agv_dispatch.py
Normal file
50
sf_manufacturing/models/agv_dispatch.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class AgvDispatch(models.Model):
|
||||||
|
_name = 'sf.agv.dispatch'
|
||||||
|
_description = 'agv调度'
|
||||||
|
|
||||||
|
name = fields.Char('任务单号')
|
||||||
|
|
||||||
|
def _get_agv_route_type_selection(self):
|
||||||
|
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||||
|
|
||||||
|
agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型')
|
||||||
|
agv_route_name = fields.Char('任务路线名称')
|
||||||
|
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站')
|
||||||
|
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站')
|
||||||
|
site_state = fields.Selection([
|
||||||
|
('占用', '占用'),
|
||||||
|
('空闲', '空闲')], string='终点接驳站状态')
|
||||||
|
state = fields.Selection([
|
||||||
|
('待下发', '待下发'),
|
||||||
|
('配送中', '配送中'),
|
||||||
|
('已配送', '已配送'),
|
||||||
|
('已取消', '已取消')], string='状态', default='待下发')
|
||||||
|
workpiece_delivery_ids = fields.One2many('sf.workpiece.delivery', 'agv_dispatch_id', string='工件配送单')
|
||||||
|
delivery_workpieces = fields.Char('配送工件', compute='_compute_delivery_workpieces')
|
||||||
|
|
||||||
|
@api.depends('workpiece_delivery_ids')
|
||||||
|
def _compute_delivery_workpieces(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.delivery_workpieces = '\\'.join(rec.workpiece_delivery_ids.mapped('name'))
|
||||||
|
|
||||||
|
task_create_time = fields.Datetime('任务创建时间')
|
||||||
|
task_delivery_time = fields.Datetime('任务下发时间')
|
||||||
|
task_completion_time = fields.Datetime('任务完成时间')
|
||||||
|
task_duration = fields.Char('任务时长', compute='_compute_task_duration')
|
||||||
|
|
||||||
|
@api.depends('task_completion_time', 'task_delivery_time')
|
||||||
|
def _compute_task_duration(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.task_completion_time and rec.task_delivery_time:
|
||||||
|
rec.task_duration = str(rec.task_completion_time - rec.task_delivery_time)
|
||||||
|
else:
|
||||||
|
rec.task_duration = ''
|
||||||
|
|
||||||
|
def add_queue(self, vals):
|
||||||
|
if not vals.get('agv_route_type') or vals.get('agv_route_type') not in self._get_agv_route_type_selection():
|
||||||
|
raise ValidationError('无效的路线类型!')
|
||||||
|
self.env['sf.agv.dispatch'].sudo().create(vals)
|
||||||
@@ -11,12 +11,14 @@ class AgvSetting(models.Model):
|
|||||||
_description = 'agv站点'
|
_description = 'agv站点'
|
||||||
|
|
||||||
name = fields.Char('位置编号')
|
name = fields.Char('位置编号')
|
||||||
owning_region = fields.Char('所属区域')
|
# owning_region = fields.Char('所属区域')
|
||||||
state = fields.Selection([
|
state = fields.Selection([
|
||||||
('占用', '占用'),
|
('占用', '占用'),
|
||||||
('空闲', '空闲')], string='状态')
|
('空闲', '空闲')], string='状态')
|
||||||
divide_the_work = fields.Char('主要分工')
|
divide_the_work = fields.Char('主要分工')
|
||||||
active = fields.Boolean('有效', default=True)
|
active = fields.Boolean('有效', default=True)
|
||||||
|
region = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True,
|
||||||
|
domain=[('is_agv_dispatch', '=', True)])
|
||||||
|
|
||||||
def update_site_state(self):
|
def update_site_state(self):
|
||||||
# 调取中控的接驳站接口并修改对应站点的状态
|
# 调取中控的接驳站接口并修改对应站点的状态
|
||||||
@@ -71,6 +73,15 @@ class AgvTaskRoute(models.Model):
|
|||||||
if self.end_site_id == self.start_site_id:
|
if self.end_site_id == self.start_site_id:
|
||||||
raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择")
|
raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择")
|
||||||
|
|
||||||
|
region = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_dispatch', '=', True)],
|
||||||
|
compute="_compute_region")
|
||||||
|
|
||||||
|
@api.depends('end_site_id')
|
||||||
|
def _compute_region(self):
|
||||||
|
for record in self:
|
||||||
|
if record.end_site_id:
|
||||||
|
record.region = record.end_site_id.region
|
||||||
|
|
||||||
|
|
||||||
class Center_controlInterfaceLog(models.Model):
|
class Center_controlInterfaceLog(models.Model):
|
||||||
_name = 'center_control.interface.log'
|
_name = 'center_control.interface.log'
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ class ResWorkcenter(models.Model):
|
|||||||
res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc]
|
res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# AGV是否可配送
|
||||||
|
is_agv_dispatch = fields.Boolean(string="AGV配送", tracking=True)
|
||||||
|
|
||||||
class ResWorkcenterProductivity(models.Model):
|
class ResWorkcenterProductivity(models.Model):
|
||||||
_inherit = 'mrp.workcenter.productivity'
|
_inherit = 'mrp.workcenter.productivity'
|
||||||
|
|||||||
@@ -1744,6 +1744,9 @@ class WorkPieceDelivery(models.Model):
|
|||||||
else:
|
else:
|
||||||
obj.delivery_duration = 0.0
|
obj.delivery_duration = 0.0
|
||||||
|
|
||||||
|
# agv调度单
|
||||||
|
agv_dispatch_id = fields.Many2one('sf.agv.dispatch', 'agv调度单')
|
||||||
|
|
||||||
|
|
||||||
class CMMprogram(models.Model):
|
class CMMprogram(models.Model):
|
||||||
_name = 'sf.cmm.program'
|
_name = 'sf.cmm.program'
|
||||||
|
|||||||
@@ -150,5 +150,7 @@ access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_orde
|
|||||||
access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
|
access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||||
access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
|
access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
|
||||||
|
|
||||||
|
access_sf_agv_dispatch_admin,sf_agv_dispatch_admin,model_sf_agv_dispatch,base.group_system,1,1,1,1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
50
sf_manufacturing/views/agv_dispatch_views.xml
Normal file
50
sf_manufacturing/views/agv_dispatch_views.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- agv站点 -->
|
||||||
|
<record id="view_agv_dispatch_tree" model="ir.ui.view">
|
||||||
|
<field name="name">agv调度</field>
|
||||||
|
<field name="model">sf.agv.dispatch</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree editable="bottom" delete="0" create="0">
|
||||||
|
<field name="state" widget="badge"
|
||||||
|
decoration-success="state == '已配送'"
|
||||||
|
decoration-warning="state == '待下发'"
|
||||||
|
decoration-danger="state == '配送中'"
|
||||||
|
decoration-info="state == '已取消'"
|
||||||
|
/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="agv_route_name"/>
|
||||||
|
<field name="start_site_id"/>
|
||||||
|
<field name="end_site_id"/>
|
||||||
|
<field name="site_state"/>
|
||||||
|
<field name="delivery_workpieces" readonly="1"/>
|
||||||
|
<field name="task_create_time" readonly="1"/>
|
||||||
|
<field name="task_delivery_time" readonly="1"/>
|
||||||
|
<field name="task_completion_time" readonly="1"/>
|
||||||
|
<field name="task_duration" readonly="1"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_agv_dispatch_tree" model="ir.actions.act_window">
|
||||||
|
<field name="name">AGV调度</field>
|
||||||
|
<field name="res_model">sf.agv.dispatch</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- <menuitem id="menu_action_agv_dispatch"-->
|
||||||
|
<!-- parent="mrp.menu_mrp_manufacturing"-->
|
||||||
|
<!-- name="AGV调度"-->
|
||||||
|
<!-- action="sf_manufacturing.action_agv_dispatch_tree"-->
|
||||||
|
<!-- groups="sf_base.group_sf_order_user,sf_base.group_plan_dispatch,sf_base.group_plan_director,sf_base.group_sf_mrp_manager"-->
|
||||||
|
<!-- />-->
|
||||||
|
<menuitem
|
||||||
|
id="sf_workpiece_delivery_menu1"
|
||||||
|
name="AGV调度"
|
||||||
|
sequence="28"
|
||||||
|
action="action_agv_dispatch_tree"
|
||||||
|
parent="mrp.menu_mrp_manufacturing"
|
||||||
|
/>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="owning_region" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name="region" required="1" options="{'no_create': True}"/>
|
||||||
<field name="state" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
<field name="state" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="divide_the_work" required="1"/>
|
<field name="divide_the_work" required="1"/>
|
||||||
</tree>
|
</tree>
|
||||||
@@ -40,8 +40,9 @@
|
|||||||
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
|
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
|
||||||
attrs="{'readonly': [('id', '!=', False)]}"/>
|
attrs="{'readonly': [('id', '!=', False)]}"/>
|
||||||
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
|
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
|
||||||
<field name="destination_production_line_id" required="1" options="{'no_create': True}"
|
<!-- <field name="destination_production_line_id" required="1" options="{'no_create': True}"-->
|
||||||
attrs="{'readonly': [('id', '!=', False)]}"/>
|
<!-- attrs="{'readonly': [('id', '!=', False)]}"/>-->
|
||||||
|
<field name="region"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -182,6 +182,7 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='resource_calendar_id']" position="after">
|
<xpath expr="//field[@name='resource_calendar_id']" position="after">
|
||||||
<field name="is_process_outsourcing"/>
|
<field name="is_process_outsourcing"/>
|
||||||
|
<field name="is_agv_dispatch"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
Reference in New Issue
Block a user