Accept Merge Request #990: (feature/修复工件配送 -> develop)

Merge Request: 优化最新AGV及新增中控日志接口调用

Created By: @杨金灵
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @杨金灵
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/990
This commit is contained in:
杨金灵
2024-05-06 11:51:54 +08:00
committed by Coding
14 changed files with 471 additions and 121 deletions

View File

@@ -13,6 +13,7 @@
'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse'],
'data': [
'data/stock_data.xml',
'data/empty_racks_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Manufacturing_Connect(http.Controller):
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
@http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*")
def get_Work_Info(self, **kw):
"""
@@ -21,6 +21,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetWoInfo'})
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search([('rfid_code', '=', ret['RfidCode'])])
@@ -60,6 +62,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/GetShiftPlan'})
if 'ProductionLine' in ret:
workorder_ids = request.env['mrp.workorder'].sudo().get_plan_workorder(ret['ProductionLine'])
else:
@@ -113,6 +117,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/QcCheck'})
logging.info('RfidCode:%s' % ret['RfidCode'])
if 'RfidCode' in ret:
workorder = request.env['mrp.workorder'].sudo().search(
@@ -150,6 +156,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': ['工单已开始']}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/FeedBackStart'})
production_id = ret['BillId']
routing_type = ret['CraftId']
equipment_id = ret["DeviceId"]
@@ -193,6 +201,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': ['工单已结束']}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/FeedBackEnd'})
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
@@ -237,6 +247,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/PartQualityInspect'})
production_id = ret['BillId']
routing_type = ret['CraftId']
workorder = request.env['mrp.workorder'].sudo().search(
@@ -293,6 +305,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/CMMProgDolod'})
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
@@ -331,6 +345,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/NCProgDolod'})
if 'RfidCode' in ret:
logging.info('RfidCode:%s' % ret['RfidCode'])
workorder = request.env['mrp.workorder'].sudo().search(
@@ -370,6 +386,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True, 'Datas': []}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/LocationChange'})
logging.info('LocationChange_ret===========:%s' % ret)
RfidCode = ret['RfidCode']
ChangeType = ret['ChangeType']
@@ -426,11 +444,13 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/AGVToProduct'})
logging.info('ret:%s' % ret)
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
if 'IsComplete' in ret:
if ret['IsComplete'] is True:
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
logging.info('F-RfidCode:%s' % i)
if f'RfidCode{i}' in ret:
@@ -477,12 +497,14 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/AGVDownProduct'})
logging.info('ret:%s' % ret)
if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId'])
delivery_Arr = []
if 'IsComplete' in ret:
if ret['IsComplete'] is True:
if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5):
logging.info('F-RfidCode:%s' % i)
if f'RfidCode{i}' in ret:
@@ -538,6 +560,8 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/EquipmentBaseCoordinate'})
if 'DeviceId' in ret:
equipment = request.env['maintenance.equipment'].sudo().search('name', '=', ret['DeviceId'])
if equipment:

View File

@@ -28,7 +28,7 @@ class Workpiece(http.Controller):
req_codes = ret['reqCode'].split(',')
for req_code in req_codes:
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('delivery_num', '=', req_code.strip()), ('task_completion_time', '=', False)])
[('name', '=', req_code.strip()), ('task_completion_time', '=', False)])
if workpiece_delivery:
workpiece_delivery.write({'status': '已配送', 'task_completion_time': datetime.now()})
else:

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="sequence_workpiece_delivery" model="ir.sequence">
<field name="name">工件配送</field>
<field name="code">sf.workpiece.delivery</field>
<field name="prefix">WDO%(year)s%(month)s%(day)s</field>
<field name="padding">4</field>
<field name="company_id" eval="False"/>
</record>
<record id="workpiece_delivery_empty_racks_1" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:C01-A01</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','C01'),('end_site_id.name','=','A01')]"/>
<field name="feeder_station_start_id" search="[('name','=','C01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','A01')]"/>
</record>
<record id="workpiece_delivery_empty_racks_2" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:B01-B02</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','B01'),('end_site_id.name','=','B02')]"/>
<field name="feeder_station_start_id" search="[('name','=','B01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','B02')]"/>
</record>
<record id="workpiece_delivery_empty_racks_3" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:B01-A01</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','B01'),('end_site_id.name','=','A01')]"/>
<field name="feeder_station_start_id" search="[('name','=','B01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','A01')]"/>
</record>
<record id="workpiece_delivery_empty_racks_4" model="sf.workpiece.delivery">
<field name="name">运送空料架路线:C01-B02</field>
<field name="type">运送空料架</field>
<field name="route_id"
search="[('start_site_id.name','=','C01'),('end_site_id.name','=','B02')]"/>
<field name="feeder_station_start_id" search="[('name','=','C01')]"/>
<field name="feeder_station_destination_id" search="[('name','=','B02')]"/>
</record>
</data>
</odoo>

View File

@@ -32,6 +32,8 @@ class AgvSetting(models.Model):
if da['DeviceId'] == item.name:
if da['AtHome'] is True:
item.state = '占用'
else:
item.state = '空闲'
class AgvTaskRoute(models.Model):
@@ -40,15 +42,20 @@ class AgvTaskRoute(models.Model):
name = fields.Char('名称')
type = fields.Selection([
('F01', '搬运'), ], string='类型', default="F01")
('F01', '搬运'), ], string='任务类型', default="F01")
route_type = fields.Selection([
('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站位置编号')
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站位置编号')
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
priority = fields.Selection([
('0', '正常'),
('1', ''),
('2', ''),
('3', ''),
('4', '紧急'),
], string='优先级', default='0')
active = fields.Boolean('有效', default=True)
class Center_controlInterfaceLog(models.Model):
_name = 'center_control.interface.log'
_description = '中控接口调用日志'
name = fields.Char('接口名称')
content = fields.Char('接口内容')
interface_call_date = fields.Datetime("调用时间", default=fields.Datetime.now, readonly=True)
active = fields.Boolean('有效', default=True)

View File

@@ -180,6 +180,11 @@ class ResMrpWorkOrder(models.Model):
detection_report = fields.Binary('检测报告', readonly=True)
is_remanufacture = fields.Boolean(string='是否重新生成制造订单', default=True)
@api.onchange('rfid_code')
def _onchange(self):
if self.rfid_code and self.state == 'progress':
self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
def get_plan_workorder(self, production_line):
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
tomorrow_start = tomorrow + ' 00:00:00'
@@ -422,6 +427,7 @@ class ResMrpWorkOrder(models.Model):
if not item.route_id:
raise UserError('【工件配送】明细中请选择【任务路线】')
else:
if self.state == 'done':
if item.is_cnc_program_down is True:
if item.status == '待下发':
return {
@@ -435,6 +441,8 @@ class ResMrpWorkOrder(models.Model):
}}
else:
raise UserError(_("该制造订单还未下发CNC程序单无法进行工件配送"))
else:
raise UserError(_("该工单暂未完成,无法进行工件配送"))
# 拼接工单对象属性值
def json_workorder_str(self, k, production, route):
@@ -485,9 +493,8 @@ class ResMrpWorkOrder(models.Model):
def _json_workpiece_delivery_list(self, production):
return [
[0, '', {'production_id': production.id, 'type': '上产线', 'delivery_num': '%s-%s' % (production.name, 1)}],
[0, '',
{'production_id': production.id, 'type': '下产线', 'delivery_num': '%s-%s' % (production.name, 2)}]]
[0, '', {'production_id': production.id, 'type': '上产线'}],
[0, '', {'production_id': production.id, 'type': '下产线'}]]
# 拼接工单对象属性值(表面工艺)
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
@@ -632,6 +639,33 @@ class ResMrpWorkOrder(models.Model):
# 'domain': [('production_id', '=', self.id)],
# 'target':'new'
# }
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
def _compute_state(self):
for workorder in self:
if workorder.routing_type == '装夹预调':
if not workorder.cnc_ids:
workorder.state = 'waiting'
else:
for item in workorder.cnc_ids:
functional_cutting_tool = self.env['sf.functional.cutting.tool.entity'].search(
[('tool_name_id.name', '=', item.cutting_tool_name)])
if not functional_cutting_tool:
workorder.state = 'waiting'
if workorder.state == 'pending':
if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting'
continue
if workorder.state not in ('waiting', 'ready'):
continue
if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
workorder.state = 'pending'
continue
if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
continue
if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting':
workorder.state = 'ready'
elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
workorder.state = 'waiting'
def recreateManufacturingOrWorkerOrder(self):
"""
@@ -777,14 +811,11 @@ class ResMrpWorkOrder(models.Model):
raise UserError("请对前置三元检测定位参数进行计算定位")
if not self.rfid_code:
raise UserError("请扫RFID码进行绑定")
self.workpiece_delivery_ids[0].write({'status': '待下发'})
if self.routing_type == '解除装夹':
'''
记录结束时间
'''
self.date_finished = datetime.now()
if self.picking_out_id:
picking_out = self.env['stock.picking'].search([('id', '=', self.picking_out_id.id)])
if picking_out.workorder_out_id:
@@ -821,6 +852,8 @@ class ResMrpWorkOrder(models.Model):
self.process_state = '已完工'
self.production_id.button_mark_done1()
# self.production_id.state = 'done'
# if self.routing_type == '装夹预调':
# self.workpiece_delivery_ids.write({''})
# 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath):
@@ -918,6 +951,9 @@ class CNCprocessing(models.Model):
if workpiece_delivery:
for item in workpiece_delivery:
item.is_cnc_program_down = True
if item.workorder_id.state == 'waiting':
item.workorder_id.state = 'ready'
# cnc_workorder.time_ids.date_end = datetime.now()
# cnc_workorder.button_finish()
@@ -1096,12 +1132,11 @@ class WorkPieceDelivery(models.Model):
_name = "sf.workpiece.delivery"
_description = '工件配送'
delivery_num = fields.Char('工件配送编码')
name = fields.Char('单据编号')
workorder_id = fields.Many2one('mrp.workorder', string='工单', readonly=True)
workorder_state = fields.Selection(related='workorder_id.state', string='工单状态')
production_id = fields.Many2one('mrp.production', string='制造订单号', readonly=True)
production_line_id = fields.Many2one('sf.production.line', compute='_compute_production_line_id',
string='目的生产线', readonly=True,
store=True)
production_line_id = fields.Many2one('sf.production.line', string='目的生产线')
plan_start_processing_time = fields.Datetime('计划开始加工时间', readonly=True)
route_id = fields.Many2one('sf.agv.task.route', '任务路线')
@@ -1115,9 +1150,25 @@ class WorkPieceDelivery(models.Model):
status = fields.Selection(
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送')], string='状态', default='待下发')
is_cnc_program_down = fields.Boolean('程序是否下发', default=False)
rfid_code = fields.Char('rfid码')
active = fields.Boolean(string="有效", default=True)
# @api.model
# def create(self, vals):
@api.model
def create(self, vals):
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
obj = super(WorkPieceDelivery, self).create(vals)
return obj
def action_delivery_history(self):
return {
'name': _('配送历史'),
'type': 'ir.actions.act_window',
'view_mode': 'tree',
'res_model': 'sf.workpiece.delivery',
'view_id': self.env.ref('sf_manufacturing.sf_workpiece_delivery_empty_racks_tree').id,
'domain': [('type', '=', '运送空料架'), ('route_id', '=', self.route_id.id), ('name', 'ilike', 'WDO')]
}
@api.onchange('route_id')
def onchange_route(self):
@@ -1128,6 +1179,7 @@ class WorkPieceDelivery(models.Model):
# 工件配送
def button_delivery(self):
delivery_ids = []
production_ids = []
is_cnc_down = 0
is_not_production_line = 0
is_not_route = 0
@@ -1138,8 +1190,18 @@ class WorkPieceDelivery(models.Model):
num = 0
for item in self:
num += 1
if production_type is None:
production_type = item.type
if item.type == "运送空料架":
if num >= 2:
raise UserError('仅选择一条路线进行配送,请重新选择')
else:
delivery_ids.append(item.id)
else:
if num > 4:
raise UserError('仅限于配送1-4个制造订单请重新选择')
if item.status in ['待配送', '已配送']:
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if item.route_id:
if same_route_id is None:
same_route_id = item.route_id.id
@@ -1147,12 +1209,16 @@ class WorkPieceDelivery(models.Model):
is_not_route += 1
else:
raise UserError('请选择【任务路线】再进行配送')
if production_type is None:
production_type = item.type
if production_type != item.type:
raise UserError('请选择类型为%s的制造订单进行配送' % production_type)
if down_status != item.status:
up_workpiece = self.search([('type', '=', '上产线'), ('production_id', '=', item.production_id),
('status', '=', '待下发')])
if up_workpiece:
raise UserError('您所选择的制造订单暂未上产线,请在上产线后再进行配送')
else:
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if same_production_line_id is None:
same_production_line_id = item.production_line_id.id
if item.production_line_id.id != same_production_line_id:
@@ -1161,6 +1227,7 @@ class WorkPieceDelivery(models.Model):
is_cnc_down += 1
if is_cnc_down == 0 and is_not_production_line == 0 and is_not_route == 0:
delivery_ids.append(item.id)
production_ids.append(item.production_id.id)
if is_cnc_down >= 1:
raise UserError('您所选择制造订单的【CNC程序】暂未下发请在程序下发后再进行配送')
if is_not_production_line >= 1:
@@ -1178,9 +1245,17 @@ class WorkPieceDelivery(models.Model):
'target': 'new',
'context': {
'default_delivery_ids': [(6, 0, delivery_ids)],
'default_production_ids': [(6, 0, production_ids)],
'default_destination_production_line_id': same_production_line_id,
'default_route_id': same_route_id,
'default_type': production_type,
}}
else:
raise UserError("您所选择制造订单的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时进行配送")
if self.type == '运送空料架':
raise UserError("您所选择的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时进行配送")
else:
raise UserError(
"您所选择制造订单的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时或选择其他路线进行配送")
# 验证agv站点是否可用
def _check_avgsite_state(self):
@@ -1189,15 +1264,18 @@ class WorkPieceDelivery(models.Model):
if agv_site:
agv_site.update_site_state()
for item in self:
if item.type in ["上产线", "下产线"]:
logging.info('工件配送-起点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-终点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if item.type in ['上产线', '下产线']:
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
@@ -1208,12 +1286,23 @@ class WorkPieceDelivery(models.Model):
delivery_Arr = []
feeder_station_start = None
feeder_station_destination = None
route_id = None
for item in self:
delivery_Arr.append(item.name)
if item.type in ['上产线', '下产线']:
if route_id is None:
route_id = item.route_id.id
if feeder_station_start is None:
feeder_station_start = item.feeder_station_start_id.name
if feeder_station_destination is None:
feeder_station_destination = item.feeder_station_destination_id.name
delivery_Arr.append(item.delivery_num)
item.route_id = route_id
else:
self = self.create(
{'name': self.env['ir.sequence'].next_by_code('sf.workpiece.delivery'),
'route_id': self.route_id.id,
'feeder_station_start_id': self.feeder_station_start_id.id,
'feeder_station_destination_id': self.feeder_station_destination_id.id})
delivery_str = ','.join(map(str, delivery_Arr))
if feeder_station_start is not None:
positionCode_Arr.append({
@@ -1242,24 +1331,19 @@ class WorkPieceDelivery(models.Model):
req_codes = ret['reqCode'].split(',')
for delivery_item in self:
for req_code in req_codes:
if delivery_item.delivery_num == req_code.strip():
logging.info('delivery_num:%s' % delivery_item.delivery_num)
if delivery_item.name == req_code.strip():
logging.info('delivery_item-name:%s' % delivery_item.name)
delivery_item.write({
'task_delivery_time': fields.Datetime.now(),
'status': '待配送'
})
if delivery_item == "上产线":
delivery_item.workorder_id.write({'is_delivery': True})
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('config-e:%s' % e)
raise UserError("工件配送请求agv失败")
@api.onchange('production_id.production_line_id')
def _compute_production_line_id(self):
if self.production_id.production_line_id:
self.production_line_id = self.production_id.production_line_id.id
self.plan_start_processing_time = self.production_id.plan_start_processing_time
raise UserError("工件配送请求agv失败:%s" % e)
@api.depends('task_delivery_time', 'task_completion_time')
def _compute_delivery_duration(self):

View File

@@ -133,3 +133,4 @@ access_sf_workpiece_delivery_group_plan_dispatch,sf.workpiece.delivery,sf_manufa
access_sf_agv_site_group_sf_order_user,sf_agv_site_group_sf_order_user,model_sf_agv_site,sf_base.group_sf_order_user,1,1,1,0
access_sf_agv_task_route_group_sf_order_user,sf_agv_task_route_group_sf_order_user,model_sf_agv_task_route,sf_base.group_sf_order_user,1,1,1,0
access_center_control_interface_log_admin,center_control_interface_log_admin,model_center_control_interface_log,base.group_system,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
133
134
135
136

View File

@@ -34,7 +34,8 @@
<field name="arch" type="xml">
<tree editable="bottom">
<field name="name" required="1"/>
<field name="type" readonly="1"/>
<field name="type" readonly="1" string="任务类型"/>
<field name="route_type" string="类型"/>
<field name="start_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}"/>
@@ -54,4 +55,34 @@
sequence="13"
action="action_agv_task_route_form"/>
</data>
<record model="ir.ui.view" id="center_control_interface_logging_tree_view">
<field name="model">center_control.interface.log</field>
<field name="arch" type="xml">
<tree string="Logs" create="0" edit="0" delete="0">
<field name="name"/>
<field name="content"/>
<field name="interface_call_date"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="center_control_interface_logging_search_view">
<field name="model">center_control.interface.log</field>
<field name="arch" type="xml">
<search string="Logs">
<field name="name"/>
<field name="interface_call_date"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="center_control_interface_logging_act">
<field name="name">中控调用日志</field>
<field name="res_model">center_control.interface.log</field>
<field name="view_mode">tree</field>
</record>
<menuitem parent="base.next_id_9" id="center_control_interface_logging_menu"
action="center_control_interface_logging_act"/>
</odoo>

View File

@@ -80,12 +80,12 @@
<field name="schedule_state" invisible='1'/>
<field name="manual_quotation" readonly="1"/>
<field name="programming_state" readonly="1"/>
<field name="production_line_id" readonly="1"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="before">
<field name="plan_start_processing_time" readonly="1"/>
</xpath>
<xpath expr="//field[@name='user_id']" position="after">
<field name="production_line_id" readonly="1"/>
<field name="production_line_state" readonly="1"/>
<field name="part_number"/>
<field name="part_drawing"/>

View File

@@ -43,7 +43,7 @@
<attribute name="attrs">{'invisible':
['|',("user_permissions","=",False),("name","=","获取CNC加工程序")]}
</attribute>
<!-- <attribute name="string">停工</attribute> -->
<!-- <attribute name="string">停工</attribute> -->
</xpath>
<xpath expr="//button[@name='action_open_wizard']" position="attributes">
<attribute name="invisible">1</attribute>
@@ -111,15 +111,15 @@
<field name='name' invisible="1"/>
<field name='is_delivery' invisible="1"/>
<!-- 工单form页面的开始停工按钮等 -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','ready'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user"/> -->
<!-- <button name="button_pending" type="object" string="暂停" class="btn-warning" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user"/> -->
<!-- <button name="button_finish" type="object" string="完成" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','ready'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user"/> -->
<!-- <button name="button_pending" type="object" string="暂停" class="btn-warning" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user"/> -->
<!-- <button name="button_finish" type="object" string="完成" class="btn-success" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=', False)]}"/>
@@ -128,19 +128,21 @@
<button name="button_finish" type="object" string="完成" class="btn-success"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞" context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked')]}"/>
<button name="button_unblock" type="object" string="取消阻塞" context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
<button name="button_unblock" type="object" string="取消阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
attrs="{'invisible': ['|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked')]}"/>
<!-- <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"/> -->
<!-- <button name="button_unblock" type="object" string="Unblock" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<!-- <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="停工" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', ('production_state', '!=', 'pending_processing'), ('state','!=','progress')]}"/> -->
<!-- <button name="button_unblock" type="object" string="Unblock" -->
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','装夹预调'),('is_delivery','=',True)]}"/>
@@ -196,6 +198,8 @@
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="equipment_id"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="production_line_id"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="production_line_state"
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
<field name="functional_fixture_id"
@@ -602,8 +606,7 @@
decoration-warning="status == '待下发'"
decoration-danger="status == '待配送'"/>
<field name="production_id"/>
<field name="type"/>
<!-- <field name="delivery_num" />-->
<field name="type" readonly="1"/>
<field name="production_line_id" options="{'no_create': True}"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
@@ -621,9 +624,9 @@
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<search string="工件配送">
<filter string="待下发" name="on_down" domain="[('status', '=', '待下发'),('type','=',['上产线'])]"/>
<filter string="产线" name="down" domain="[('type', '=', '产线')]"/>
<filter string="下产线" name="up" domain="[('type', '=', '下产线')]"/>
<filter string="上产线" name="up" domain="[('type', '=', '上产线')]"/>
<filter string="产线" name="down" domain="[('type', '=', '产线' )]"/>
<field name="rfid_code"/>
<field name="production_id"/>
<field name="feeder_station_start_id"/>
<field name="production_line_id"/>
@@ -644,9 +647,68 @@
<field name="name">工件配送</field>
<field name="res_model">sf.workpiece.delivery</field>
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
<field name="context">{'search_default_on_down':1}</field>
<!-- <field name="context">{'search_default_on_up':1}</field>-->
<field name="view_mode">tree,search</field>
<field name="domain">[('type','in',['上产线','下产线'])]</field>
<field name="domain">[('type','in',['上产线','下产线']),('workorder_state','=','done')]</field>
</record>
<!--=========================================运送空料架列表======================================-->
<record id="sf_workpiece_delivery_empty_racks_template_tree" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="工件配送" class="center" create="0" edit="0" delete="0">
<header>
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
</header>
<field name="name" string="路线名称" readonly="1"/>
<field name="route_id" options="{'no_create': True}"/>
<field name="feeder_station_start_id" readonly="1"/>
<field name="feeder_station_destination_id" readonly="1"/>
<button name="action_delivery_history" type="object" class="btn btn-link text-info" icon="fa-history"
string="历史"/>
</tree>
</field>
</record>
<record id="sf_workpiece_delivery_empty_racks_tree" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<tree string="运送空料架" class="center" create="0" delete="0" edit="0">
<field name="status"/>
<field name="route_id"/>
<field name="feeder_station_start_id"/>
<field name="feeder_station_destination_id"/>
<field name="task_delivery_time"/>
<field name="task_completion_time"/>
<field name="delivery_duration" widget="float_time"/>
</tree>
</field>
</record>
<record id="sf_workpiece_delivery_empty_racks_search" model="ir.ui.view">
<field name="name">空料架配送</field>
<field name="model">sf.workpiece.delivery</field>
<field name="arch" type="xml">
<search string="运送空料架">
<field name="route_id"/>
<field name="feeder_station_start_id"/>
<field name="feeder_station_destination_id"/>
</search>
</field>
</record>
<record id="sf_workpiece_delivery_empty_racks_act" model="ir.actions.act_window">
<field name="name">空料架配送</field>
<field name="res_model">sf.workpiece.delivery</field>
<field name="view_id" ref="sf_workpiece_delivery_empty_racks_template_tree"/>
<field name="search_view_id" ref="sf_workpiece_delivery_empty_racks_search"/>
<field name="view_mode">tree</field>
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
</record>
</odoo>

View File

@@ -5,13 +5,32 @@
<field name="model">sf.workpiece.delivery.wizard</field>
<field name="arch" type="xml">
<form>
<sheet>
<field name="delivery_ids" invisible="True"/>
<field name="workorder_id" invisible="True"/>
<div>是否确定配送?</div>
<field name="type" invisible="True"/>
<group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
<div class="o_address_format">
<lable for="rfid_code"></lable>
<field name="rfid_code" class="o_address_zip"/>
<button name="recognize_production" string="识别" type="object" class="oe_highlight"/>
</div>
<field name="destination_production_line_id" readonly="1"/>
<field name="route_id"/>
</group>
<group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
<field name="feeder_station_start_id" focesave="1" readonly="1"/>
<field name="feeder_station_destination_id" focesave="1" readonly="1"/>
</group>
<div attrs="{'invisible': [('type', 'in', ['上产线','下产线'])]}">
是否确定配送
</div>
<footer>
<button string="确认" name="confirm" type="object" class="oe_highlight"/>
<button string="配送" name="confirm" type="object" class="oe_highlight"/>
<button string="取消" class="btn btn-secondary" special="cancel"/>
</footer>
</sheet>
</form>
</field>
</record>

View File

@@ -2,7 +2,7 @@
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import UserError, ValidationError
from datetime import datetime
from odoo import models, api, fields
from odoo import models, api, fields, _
class WorkpieceDeliveryWizard(models.TransientModel):
@@ -10,10 +10,74 @@ class WorkpieceDeliveryWizard(models.TransientModel):
_description = '工件配送'
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送')
rfid_code = fields.Char('rfid码')
workorder_id = fields.Many2one('mrp.workorder', string='工单')
production_ids = fields.Many2many('mrp.production', string='制造订单号')
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
route_id = fields.Many2one('sf.agv.task.route', '任务路线', domain=[('route_type', 'in', ['上产线', '下产线'])])
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
type = fields.Selection(
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
def confirm(self):
if self.type != '运送空料架':
if not self.route_id:
raise UserError('请选择路线')
if self.workorder_id:
self.workorder_id.workpiece_delivery_ids[0]._delivery_avg()
else:
is_not_production_line = 0
same_production_line_id = None
notsame_production_line_arr = []
for item in self.production_ids:
if same_production_line_id is None:
same_production_line_id = item.production_line_id.id
if item.production_line_id.id != same_production_line_id:
notsame_production_line_arr.append(item.name)
notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
if is_not_production_line >= 1:
raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
else:
self.delivery_ids._delivery_avg()
def recognize_production(self):
# production_ids = []
# delivery_ids = []
if len(self.production_ids) > 4:
raise UserError('只能配送四个制造订单')
else:
if self.rfid_code:
wd = self.env['sf.workpiece.delivery'].search(
[('type', '=', self.delivery_ids[0].type), ('rfid_code', '=', self.rfid_code),
('status', '=', self.delivery_ids[0].status)])
if wd:
if wd.production_line_id.id == self.delivery_ids[0].production_line_id.id:
# production_ids.append(wd.production_id)
# delivery_ids.append(wd.id)
# 将对象添加到对应的同模型且是多对多类型里
self.production_ids |= wd.production_id
self.delivery_ids |= wd
self.rfid_code = False
# self.production_ids = [(6, 0, production_ids)]
# self.delivery_ids = [(6, 0, delivery_ids)]
else:
raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % wd.production_id.name)
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_delivery_ids': [(6, 0, self.delivery_ids.ids)],
'default_production_ids': [(6, 0, self.production_ids.ids)],
'default_route_id': self.delivery_ids[0].route_id.id,
'default_type': self.delivery_ids[0].type
}}
@api.onchange('route_id')
def onchange_route(self):
if self.route_id:
self.feeder_station_start_id = self.route_id.start_site_id.id
self.feeder_station_destination_id = self.route_id.end_site_id.id

View File

@@ -84,7 +84,8 @@ class sf_production_plan(models.Model):
item.sudo().production_id.production_line_id = item.production_line_id.id
item.sudo().production_id.workorder_ids.filtered(
lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write(
{'production_line_id': item.production_line_id.id})
{'production_line_id': item.production_line_id.id,
'plan_start_processing_time': item.plan_start_processing_time})
# item.sudo().production_id.plan_start_processing_time = item.date_planned_start
# @api.onchange('state')

View File

@@ -306,6 +306,14 @@
action="sf_manufacturing.sf_workpiece_delivery_act"
parent="mrp.menu_mrp_manufacturing"
/>
<menuitem
id="sf_workpiece_delivery__empty_racks_menu"
name="空料架配送"
sequence="11"
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
parent="mrp.menu_mrp_manufacturing"
/>
<!-- <menuitem -->
<!-- id="sale_custom_menu" -->
<!-- name="报价单" -->