diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py
index ca85c807..9ea3bb60 100644
--- a/sf_manufacturing/__manifest__.py
+++ b/sf_manufacturing/__manifest__.py
@@ -41,7 +41,8 @@
'web.assets_backend': [
'sf_manufacturing/static/src/xml/kanban_change.xml',
'sf_manufacturing/static/src/js/kanban_change.js',
- 'sf_manufacturing/static/src/scss/kanban_change.scss'
+ 'sf_manufacturing/static/src/scss/kanban_change.scss',
+ 'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
]
},
diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py
index 27e18f25..6801f561 100644
--- a/sf_manufacturing/controllers/controllers.py
+++ b/sf_manufacturing/controllers/controllers.py
@@ -450,9 +450,8 @@ class Manufacturing_Connect(http.Controller):
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
if 'IsComplete' in ret:
- first_rfid_code = None
- production_ids = []
- workpiece_validate = True
+ rfid_codes = []
+ workorder_ids = []
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
logging.info('F-RfidCode:%s' % i)
@@ -460,7 +459,7 @@ class Manufacturing_Connect(http.Controller):
rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code)
if rfid_code is not None:
- first_rfid_code = first_rfid_code or rfid_code
+ rfid_codes.append(rfid_code)
domain = [
('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
@@ -468,7 +467,7 @@ class Manufacturing_Connect(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
- production_ids.append(order.production_id.id)
+ workorder_ids.append(order.id)
if order.production_line_state == '待上产线':
logging.info(
'工单产线状态:%s' % order.production_line_state)
@@ -487,19 +486,27 @@ class Manufacturing_Connect(http.Controller):
# workpiece_delivery.write({'is_manual_work': True})
# 下发
else:
- workpiece_validate = False
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
if ret['IsComplete'] is True:
# 将RFID_CODE对应的工件配送单对应的AGV任务调度状态设置为已配送
- request.env['sf.workpiece.delivery'].sudo().search(
- [('rfid_code', '=', first_rfid_code)]).agv_scheduling_id.write({'state': '已配送'})
+ # workorder_id = request.env['mrp.workorder'].sudo().search([
+ # ('rfid_code', 'in', rfid_codes),
+ # ('routing_type', '=', '装夹预调'), ('state', '!=', 'rework')
+ # ])
+ # workorder_id.agv_scheduling_ids.finish_scheduling()
+ # 将工件配送单状态设置为已配送
+ request.env['sf.workpiece.delivery'].sudo().search([
+ ('rfid_code', 'in', rfid_codes),
+ ('type', '=', '上产线'),
+ ('status', '=', '已下发')
+ ]).write({'state': '已配送'})
# 向AGV任务调度下发运送空料架任务
- productions = request.env['mrp.production'].sudo().search([('id', 'in', production_ids)])
# 获取设备ID对应的接驳站配置
agv_site = request.env['sf.agv.site'].sudo().search(
[('name', '=', ret['DeviceId'])], limit=1)
- request.env['sf.agv.scheduling'].add_scheduling(agv_site.id, '运送空料架', productions)
+ workorders = request.env['mrp.workorder'].browse(workorder_ids)
+ request.env['sf.agv.scheduling'].add_scheduling(agv_site.id, '运送空料架', workorders)
else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
else:
@@ -511,7 +518,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('AGVToProduct error:%s' % e)
return json.JSONEncoder().encode(res)
- @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
+ @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVDownProduct(self, **kw):
"""
@@ -530,7 +537,8 @@ class Manufacturing_Connect(http.Controller):
logging.info('ret:%s' % ret)
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
- delivery_Arr = []
+ # delivery_Arr = []
+ workorder_ids = []
if 'IsComplete' in ret:
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
@@ -546,6 +554,7 @@ class Manufacturing_Connect(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder:
for order in workorder:
+ workorder_ids.append(order.id)
if order.production_line_state == '已上产线':
logging.info(
'工单产线状态:%s' % order.production_line_state)
@@ -555,33 +564,42 @@ class Manufacturing_Connect(http.Controller):
if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'})
workorder.write({'state': 'to be detected'})
- workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
- [
- ('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
- ('production_id', '=', order.production_id.id),
- ('workorder_id', '=', order.id),
- ('workorder_state', '=', 'done')])
- if workpiece_delivery:
- delivery_Arr.append(workpiece_delivery.id)
+ # workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
+ # [
+ # ('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
+ # ('production_id', '=', order.production_id.id),
+ # ('workorder_id', '=', order.id),
+ # ('workorder_state', '=', 'done')])
+ # if workpiece_delivery:
+ # delivery_Arr.append(workpiece_delivery.id)
else:
res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
- if delivery_Arr:
- logging.info('delivery_Arr:%s' % delivery_Arr)
- delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
- [('id', 'in', delivery_Arr)])
- if delivery_workpiece:
- logging.info('开始向agv下发下产线任务')
- agv_site = request.env['sf.agv.site'].sudo().search([])
- if agv_site:
- has_site = agv_site.update_site_state()
- if has_site is True:
- is_free = delivery_workpiece._check_avgsite_state()
- if is_free is True:
- delivery_workpiece._delivery_avg()
- logging.info('agv下发下产线任务下发完成')
+ # if delivery_Arr:
+ # logging.info('delivery_Arr:%s' % delivery_Arr)
+ # delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
+ # [('id', 'in', delivery_Arr)])
+ # if delivery_workpiece:
+ # logging.info('开始向agv下发下产线任务')
+ # agv_site = request.env['sf.agv.site'].sudo().search([])
+ # if agv_site:
+ # has_site = agv_site.update_site_state()
+ # if has_site is True:
+ # is_free = delivery_workpiece._check_avgsite_state()
+ # if is_free is True:
+ # delivery_workpiece._delivery_avg()
+ # logging.info('agv下发下产线任务下发完成')
+ if ret['IsComplete'] is True:
+ # 向AGV任务调度下发下产线任务
+ # 获取设备ID对应的接驳站配置
+ agv_site = request.env['sf.agv.site'].sudo().search(
+ [('name', '=', ret['DeviceId'])], limit=1)
+ workorders = request.env['mrp.workorder'].browse(workorder_ids)
+ request.env['sf.agv.scheduling'].add_scheduling(agv_site.id, '下产线', 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': e}
logging.info('AGVDownProduct error:%s' % e)
@@ -640,7 +658,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('ret:%s' % ret)
if 'DeviceId' in ret and 'AtHome' in ret:
logging.info('DeviceId:%s, AtHome:%s' % (ret['DeviceId'], ret['AtHome']))
- request.env['sf.agv.site'].update_site_state(ret['DeviceId'], ret['AtHome'])
+ request.env['sf.agv.site'].update_site_state({ret['DeviceId']: '占用' if ret['AtHome'] else '空闲'})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
logging.info('AGVDownProduct error:%s' % e)
diff --git a/sf_manufacturing/models/agv_scheduling.py b/sf_manufacturing/models/agv_scheduling.py
index 084dcf49..c3600df7 100644
--- a/sf_manufacturing/models/agv_scheduling.py
+++ b/sf_manufacturing/models/agv_scheduling.py
@@ -31,8 +31,7 @@ class AgvScheduling(models.Model):
('配送中', '配送中'),
('已配送', '已配送'),
('已取消', '已取消')], string='状态', default='待下发', tracking=True)
- workorder_ids = fields.One2many('mrp.workorder', string='关联工单')
- delivery_workpieces = fields.Char('配送工件', required=True, index=True)
+ workorder_ids = fields.Many2many('mrp.workorder', 'sf_agv_scheduling_mrp_workorder_ref', string='关联工单')
task_create_time = fields.Datetime('任务创建时间')
task_delivery_time = fields.Datetime('任务下发时间')
task_completion_time = fields.Datetime('任务完成时间')
@@ -41,13 +40,12 @@ class AgvScheduling(models.Model):
@api.depends('agv_route_type')
def _compute_delivery_workpieces(self):
for record in self:
- record.delivery_workpieces = '、'.join(record.workorder_ids.mapped('production_id.name'))
if record.agv_route_type == '运送空料架':
- record.delivery_workpieces_display = '/'
+ record.delivery_workpieces = '/'
else:
- record.delivery_workpieces_display = record.delivery_workpieces
+ record.delivery_workpieces = '、'.join(record.workorder_ids.mapped('production_id.name'))
- delivery_workpieces_display = fields.Char('配送工件', compute=_compute_delivery_workpieces)
+ delivery_workpieces = fields.Char('配送工件', compute=_compute_delivery_workpieces)
@api.depends('task_completion_time', 'task_delivery_time')
def _compute_task_duration(self):
@@ -64,28 +62,35 @@ class AgvScheduling(models.Model):
vals['name'] = self.env['ir.sequence'].next_by_code('sf.agv.scheduling') or _('New')
return super().create(vals_list)
- def add_scheduling(self, agv_start_site_id, agv_route_type, productions, deliveries=None):
- """ add_scheduling(agv_start_site_id, agv_route_type, productions, deliveries) -> agv_scheduling
+ def add_scheduling(self, agv_start_site_id, agv_route_type, workorders):
+ """ add_scheduling(agv_start_site_id, agv_route_type, workorders) -> agv_scheduling
新增AGV调度
params:
agv_start_site_id: AGV起点接驳站ID
agv_route_type: AGV任务类型
- productions: 制造订单
- deliveries: 工件配送单
+ workorders: 工单
"""
- # 如果存在配送工件完全相同的AGV调度任务,则不新增
- if self.sudo().search([
- ('delivery_workpieces', '=', '、'.join(productions.mapped('name'))),
+ if not workorders:
+ raise UserError(_('工单不能为空'))
+ # 如果存在相同任务类型工单的AGV调度任务,则提示错误
+ agv_scheduling = self.sudo().search([
+ ('workorder_ids', 'in', workorders.ids),
('agv_route_type', '=', agv_route_type),
('state', 'in', ['待下发', '配送中'])
- ], limit=1):
- raise RepeatTaskException('已存在相同的AGV调度任务,请勿重复下发!')
+ ], limit=1)
+ if agv_scheduling:
+ # 计算agv_scheduling.workorder_ids与workorders的交集
+ repetitive_workorders = agv_scheduling.workorder_ids & workorders
+ raise RepeatTaskException(
+ '制造订单号【%s】已存在与【%s】AGV调度任务,请勿重复下发!' %
+ (','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
+ )
vals = {
'start_site_id': agv_start_site_id,
'agv_route_type': agv_route_type,
- 'delivery_workpieces': '、'.join(productions.mapped('name')),
- 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
+ 'workorder_ids': workorders.ids,
+ # 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
'task_create_time': fields.Datetime.now()
}
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
@@ -95,26 +100,52 @@ class AgvScheduling(models.Model):
])
if len(agv_routes) == 1:
vals.update({'end_site_id': agv_routes[0].end_site_id.id, 'agv_route_name': agv_routes[0].name})
+ else:
+ # 判断终点接驳站是否为空闲
+ idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
+ if idle_routes:
+ # 将空闲的路线按照终点接驳站名称排序
+ idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
+ vals.update({'end_site_id': idle_routes[0].end_site_id.id, 'agv_route_name': idle_routes[0].name})
try:
- dispatch = self.env['sf.agv.scheduling'].sudo().create(vals)
+ scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
+ # 触发空闲接驳站状态更新,触发新任务下发
+ if scheduling.end_site_id.state == '空闲':
+ scheduling.dispatch_scheduling(scheduling.end_site_id.id, scheduling.end_site_id.state)
+
except Exception as e:
_logger.error('添加AGV调度任务失败: %s', e)
raise UserError(_('添加AGV调度任务失败: %s', e))
- return dispatch
+ return scheduling
def on_site_state_change(self, agv_site_id, agv_site_state):
- agv_schedulings = self.env['sf.agv.scheduling'].sudo().search([('state', '=', '待下发')], order='id asc')
- for scheduling in agv_schedulings:
- if scheduling.end_site_id.id == agv_site_id and scheduling.site_state == '空闲':
- # 下发AGV调度任务并修改接驳站状态为占用
- # self._delivery_avg()
- # 修改AGV调度任务信息
- scheduling.state = '配送中'
- scheduling.task_delivery_time = fields.Datetime.now()
- scheduling.site_state = agv_site_state
- # 更新接驳站状态
- self.env['sf.agv.site'].update_site_state(scheduling.end_site_id.name, True)
+ """
+ 响应AGV接驳站站点状态变化
+ params:
+ agv_site_id: 接驳站ID
+ agv_site_state: 站点状态('空闲', '占用')
+ """
+ if agv_site_state == '空闲':
+ # 查询终点接驳站为agv_site_id的AGV路线
+ task_routes = self.env['sf.agv.task.route'].sudo().search([('end_site_id', '=', agv_site_id)])
+ agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
+ [('state', '=', '待下发'), ('agv_route_type', 'in', task_routes.mapped('route_type'))],
+ order='id asc',
+ limit=1
+ )
+ # 下发AGV调度任务并修改接驳站状态为占用
+ agv_scheduling.dispatch_scheduling(agv_site_id, agv_site_state)
+ # 更新接驳站状态
+ self.env['sf.agv.site'].update_site_state({agv_scheduling.end_site_id.name: '占用'}, False)
+ else:
+ # 如果终点接驳站变为占用,则认为任务完成
+ agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
+ [('state', '=', '配送中'), ('end_site_id', '=', agv_site_id)],
+ order='id asc',
+ limit=1
+ )
+ agv_scheduling.finish_scheduling()
def _delivery_avg(self):
config = self.env['res.config.settings'].get_values()
@@ -190,3 +221,36 @@ class AgvScheduling(models.Model):
if rec.state != '待下发':
raise UserError('只有待下发状态的AGV调度任务才能取消!')
rec.state = '已取消'
+
+ def finish_scheduling(self):
+ """
+ 完成调度任务
+ """
+ for rec in self:
+ if rec.state != '配送中':
+ return False
+ rec.state = '已配送'
+ rec.task_completion_time = fields.Datetime.now()
+
+ def dispatch_scheduling(self, agv_end_site_id, agv_site_state):
+ """
+ 下发调度任务
+ """
+ for rec in self:
+ if rec.state != '待下发':
+ return False
+ # rec._delivery_avg()
+ rec.state = '配送中'
+ rec.task_delivery_time = fields.Datetime.now()
+ rec.site_state = agv_site_state
+ rec.end_site_id = agv_end_site_id
+
+
+class ResMrpWorkOrder(models.Model):
+ _inherit = 'mrp.workorder'
+
+ agv_scheduling_ids = fields.Many2many(
+ 'sf.agv.scheduling',
+ 'sf_agv_scheduling_mrp_workorder_ref',
+ string='AGV调度',
+ domain=[('state', '!=', '已取消')])
diff --git a/sf_manufacturing/models/agv_setting.py b/sf_manufacturing/models/agv_setting.py
index 5f1f728e..820ed539 100644
--- a/sf_manufacturing/models/agv_setting.py
+++ b/sf_manufacturing/models/agv_setting.py
@@ -5,6 +5,8 @@ import time
from odoo import fields, models, api
from odoo.exceptions import UserError
+_logger = logging.getLogger(__name__)
+
class AgvSetting(models.Model):
_name = 'sf.agv.site'
@@ -57,14 +59,23 @@ class AgvSetting(models.Model):
# logging.error('工件配送-请求中控接口错误: %s', e)
# return False
- def update_site_state(self, agv_site_name, is_occupy):
- if agv_site_name:
- agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
- if agv_site:
- agv_site.state = '占用' if is_occupy else '空闲'
- self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
- else:
- raise UserError("更新失败:接驳站站点错误!")
+ def update_site_state(self, agv_site_state_arr, notify=True):
+ """
+ 更新接驳站状态
+ params:
+ agv_site_state_arr: {'A01': '空闲', 'B01': '占用'}
+ notify: 是否通知调度(非中控发起的状态改变不触发调度任务)
+ """
+ if isinstance(agv_site_state_arr, dict):
+ for agv_site_name, is_occupy in agv_site_state_arr.items():
+ agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
+ if agv_site:
+ agv_site.state = is_occupy
+ if notify:
+ self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
+ else:
+ _logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
+ raise UserError("更新失败:接驳站站点错误!")
class AgvTaskRoute(models.Model):
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py
index 2257fd51..36fab67c 100644
--- a/sf_manufacturing/models/mrp_workorder.py
+++ b/sf_manufacturing/models/mrp_workorder.py
@@ -1089,6 +1089,8 @@ class ResMrpWorkOrder(models.Model):
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工'
+ # 生成工件配送单
+ record.workpiece_delivery_ids = record._json_workpiece_delivery_list()
if record.routing_type == 'CNC加工':
record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'})
@@ -1190,8 +1192,7 @@ class ResMrpWorkOrder(models.Model):
raw_move.write({'state': 'done'})
record.production_id.button_mark_done1()
# record.production_id.state = 'done'
- # 生成工件配送单
- record.workpiece_delivery_ids = record._json_workpiece_delivery_list()
+
# 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath):
@@ -1495,7 +1496,7 @@ class SfWorkOrderBarcodes(models.Model):
class WorkPieceDelivery(models.Model):
_name = "sf.workpiece.delivery"
- _inherit = ['mail.thread', 'mail.activity.mixin', "barcodes.barcode_events_mixin"]
+ _inherit = ['mail.thread', 'mail.activity.mixin']
_description = '工件配送'
name = fields.Char('单据编码')
@@ -1573,12 +1574,12 @@ class WorkPieceDelivery(models.Model):
# 工件配送
def button_delivery(self):
- delivery_ids = []
+ # delivery_ids = []
production_ids = []
+ workorder_ids = []
is_cnc_down = 0
is_not_production_line = 0
same_production_line_id = None
- same_route_id = None
production_type = '上产线'
max_num = 4 # 最大配送数量
if len(self) > max_num:
@@ -1593,24 +1594,25 @@ class WorkPieceDelivery(models.Model):
if item.is_cnc_program_down is False:
is_cnc_down += 1
if is_cnc_down == 0 and is_not_production_line == 0:
- delivery_ids.append(item.id)
+ # delivery_ids.append(item.id)
production_ids.append(item.production_id.id)
+ workorder_ids.append(item.workorder_id.id)
if is_cnc_down >= 1:
raise UserError('您所选择制造订单的【CNC程序】暂未下发,请在程序下发后再进行配送')
if is_not_production_line >= 1:
raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认')
- if delivery_ids:
- return {
- 'name': _('确认'),
- 'type': 'ir.actions.act_window',
- 'view_mode': 'form',
- 'res_model': 'sf.workpiece.delivery.wizard',
- 'target': 'new',
- 'context': {
- 'default_delivery_ids': [(6, 0, delivery_ids)],
- 'default_production_ids': [(6, 0, production_ids)],
- 'default_type': production_type,
- }}
+ return {
+ 'name': _('确认'),
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ 'res_model': 'sf.workpiece.delivery.wizard',
+ 'target': 'new',
+ 'context': {
+ # 'default_delivery_ids': [(6, 0, delivery_ids)],
+ 'default_production_ids': [(6, 0, production_ids)],
+ 'default_type': production_type,
+ 'default_workorder_ids': [(6, 0, workorder_ids)],
+ }}
# 验证agv站点是否可用
@@ -1716,11 +1718,6 @@ class WorkPieceDelivery(models.Model):
else:
obj.delivery_duration = 0.0
- # agv调度单
- agv_scheduling_id = fields.Many2one('sf.agv.scheduling', 'agv调度单')
-
- def on_barcode_scanned(self, barcode):
- logging.info('Rfid:%s' % barcode)
class CMMprogram(models.Model):
diff --git a/sf_manufacturing/static/src/xml/button_show_on_tree.xml b/sf_manufacturing/static/src/xml/button_show_on_tree.xml
new file mode 100644
index 00000000..041b16a4
--- /dev/null
+++ b/sf_manufacturing/static/src/xml/button_show_on_tree.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_manufacturing/views/agv_scheduling_views.xml b/sf_manufacturing/views/agv_scheduling_views.xml
index 4e7271f6..ff8dc9b5 100644
--- a/sf_manufacturing/views/agv_scheduling_views.xml
+++ b/sf_manufacturing/views/agv_scheduling_views.xml
@@ -18,7 +18,7 @@
-
+
diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml
index 60c0b8a0..8ef871b9 100644
--- a/sf_manufacturing/views/mrp_workorder_view.xml
+++ b/sf_manufacturing/views/mrp_workorder_view.xml
@@ -631,7 +631,7 @@
sf.workpiece.delivery.wizard.form.view
sf.workpiece.delivery.wizard
-