Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into develop
This commit is contained in:
@@ -53,6 +53,23 @@ const tableRequiredList = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
|
||||||
|
setup() {
|
||||||
|
owl.onMounted(() => {
|
||||||
|
try {
|
||||||
|
const dom = this.__owl__.bdom.el
|
||||||
|
const buttonsDom = $(dom).find('.o_form_status_indicator_buttons ')
|
||||||
|
if (buttonsDom) {
|
||||||
|
const dom1 = buttonsDom.children('.o_form_button_save')
|
||||||
|
const dom2 = buttonsDom.children('.o_form_button_cancel')
|
||||||
|
dom1.append('保存')
|
||||||
|
dom2.append('取消')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
// 你可以重写或者添加一些方法和属性
|
// 你可以重写或者添加一些方法和属性
|
||||||
async _onDiscardChanges() {
|
async _onDiscardChanges() {
|
||||||
// var self = this;
|
// var self = this;
|
||||||
@@ -183,17 +200,6 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
|
|||||||
// })
|
// })
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
document.addEventListener('click', function () {
|
|
||||||
const dom = $('.o_form_status_indicator_buttons ')
|
|
||||||
if (dom) {
|
|
||||||
const dom1 = dom.children().eq(0)
|
|
||||||
const dom2 = dom.children().eq(1)
|
|
||||||
if (!dom1.text()) {
|
|
||||||
dom1.append('保存')
|
|
||||||
dom2.append('取消')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function customRequired() {
|
function customRequired() {
|
||||||
let timer = null
|
let timer = null
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
'views/machine_info_present.xml',
|
'views/machine_info_present.xml',
|
||||||
'views/delivery_record.xml',
|
'views/delivery_record.xml',
|
||||||
'views/res_config_settings_views.xml',
|
'views/res_config_settings_views.xml',
|
||||||
|
'views/maintenance_views.xml',
|
||||||
|
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
|
|||||||
@@ -1,11 +1,45 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import re
|
||||||
import ast
|
import ast
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_seconds(time_str):
|
||||||
|
# 修改正则表达式,使 H、M、S 部分可选
|
||||||
|
|
||||||
|
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
|
||||||
|
match = re.match(pattern, time_str)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
# 提取各时间单位,如果某个单位缺失则默认设为0
|
||||||
|
hours = int(match.group(1)) if match.group(1) else 0
|
||||||
|
minutes = int(match.group(2)) if match.group(2) else 0
|
||||||
|
seconds = int(match.group(3)) if match.group(3) else 0
|
||||||
|
|
||||||
|
# 计算总秒数
|
||||||
|
total_seconds = hours * 3600 + minutes * 60 + seconds
|
||||||
|
if total_seconds == 0:
|
||||||
|
# return None
|
||||||
|
pattern = r"(?:(\d+)小时)?(?:(\d+)分钟)?(?:(\d+)秒)?"
|
||||||
|
match = re.match(pattern, time_str)
|
||||||
|
if match:
|
||||||
|
# 提取各时间单位,如果某个单位缺失则默认设为0
|
||||||
|
hours = int(match.group(1)) if match.group(1) else 0
|
||||||
|
minutes = int(match.group(2)) if match.group(2) else 0
|
||||||
|
seconds = int(match.group(3)) if match.group(3) else 0
|
||||||
|
|
||||||
|
# 计算总秒数
|
||||||
|
total_seconds = hours * 3600 + minutes * 60 + seconds
|
||||||
|
return total_seconds
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return total_seconds
|
||||||
|
|
||||||
|
|
||||||
class Sf_Dashboard_Connect(http.Controller):
|
class Sf_Dashboard_Connect(http.Controller):
|
||||||
|
|
||||||
@http.route('/api/get_machine_datas/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
@http.route('/api/get_machine_datas/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
@@ -18,6 +52,11 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
"""
|
"""
|
||||||
res = {'status': 1, 'message': '成功', 'data': []}
|
res = {'status': 1, 'message': '成功', 'data': []}
|
||||||
logging.info('前端请求机床数据的参数为:%s' % kw)
|
logging.info('前端请求机床数据的参数为:%s' % kw)
|
||||||
|
|
||||||
|
# 获取当前时间的时间戳
|
||||||
|
current_timestamp = datetime.now().timestamp()
|
||||||
|
print(current_timestamp)
|
||||||
|
|
||||||
# tem_list = [
|
# tem_list = [
|
||||||
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
||||||
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
|
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
|
||||||
@@ -33,10 +72,24 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
machine_list = ast.literal_eval(kw['machine_list'])
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
for item in machine_list:
|
for item in machine_list:
|
||||||
machine_data = equipment_obj.search([('code', '=', item)])
|
machine_data = equipment_obj.search([('code', '=', item)])
|
||||||
|
|
||||||
|
# 机床上线时间段
|
||||||
|
first_online_duration = current_timestamp - int(machine_data.first_online_time.timestamp())
|
||||||
|
|
||||||
|
power_off_time = None
|
||||||
|
power_off_rate = None
|
||||||
|
if machine_data.machine_power_on_time:
|
||||||
|
power_off_time = first_online_duration - convert_to_seconds(machine_data.machine_power_on_time)
|
||||||
|
power_off_rate = round((power_off_time / first_online_duration), 3)
|
||||||
|
else:
|
||||||
|
power_off_time = False
|
||||||
|
power_off_rate = False
|
||||||
if machine_data:
|
if machine_data:
|
||||||
res['data'].append({
|
res['data'].append({
|
||||||
|
'active': machine_data.status,
|
||||||
'id': machine_data.id,
|
'id': machine_data.id,
|
||||||
'name': machine_data.name,
|
'name': machine_data.name,
|
||||||
|
'brand': machine_data.type_id.name,
|
||||||
'code': machine_data.code,
|
'code': machine_data.code,
|
||||||
'status': machine_data.status,
|
'status': machine_data.status,
|
||||||
'run_status': machine_data.run_status,
|
'run_status': machine_data.run_status,
|
||||||
@@ -88,6 +141,16 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
'alarm_time': machine_data.alarm_time,
|
'alarm_time': machine_data.alarm_time,
|
||||||
'alarm_msg': machine_data.alarm_msg,
|
'alarm_msg': machine_data.alarm_msg,
|
||||||
'clear_time': machine_data.clear_time,
|
'clear_time': machine_data.clear_time,
|
||||||
|
# 计算出来的数据
|
||||||
|
# 开动率:运行时间/通电时间
|
||||||
|
'run_rate': machine_data.run_rate,
|
||||||
|
# 关机时长:初次上线时间 - 通电时间
|
||||||
|
'power_off_time': power_off_time,
|
||||||
|
# 关机率:关机时长/初次上线时间
|
||||||
|
'power_off_rate': power_off_rate,
|
||||||
|
'first_online_duration': first_online_duration,
|
||||||
|
# 停机时间:关机时间 - 运行时间
|
||||||
|
# 停机时长:关机时间 - 初次上线时间
|
||||||
})
|
})
|
||||||
|
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
@@ -96,3 +159,126 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
res['status'] = -1
|
res['status'] = -1
|
||||||
res['message'] = '前端请求机床数据失败,原因:%s' % e
|
res['message'] = '前端请求机床数据失败,原因:%s' % e
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
# @http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
|
# cors="*")
|
||||||
|
# def logs_list(self, **kw):
|
||||||
|
# """
|
||||||
|
# 拿到日志数据返回给大屏展示
|
||||||
|
# :param kw:
|
||||||
|
# :return:
|
||||||
|
# """
|
||||||
|
# res = {'status': 1, 'message': '成功', 'data': []}
|
||||||
|
# logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||||
|
#
|
||||||
|
# try:
|
||||||
|
# # 获取请求的日志数据
|
||||||
|
# logs_obj = request.env['maintenance.equipment.oee.log.detail'].sudo()
|
||||||
|
# # 获取请求的机床数据
|
||||||
|
# machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
|
# begin_time_str = kw['begin_time'].strip('"')
|
||||||
|
# end_time_str = kw['end_time'].strip('"')
|
||||||
|
#
|
||||||
|
# begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
# end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
#
|
||||||
|
# print('begin_time: %s' % begin_time)
|
||||||
|
# for item in machine_list:
|
||||||
|
# log_datas = logs_obj.search(
|
||||||
|
# [('equipment_code', '=', item), ('time', '>=', begin_time), ('time', '<=', end_time)])
|
||||||
|
# print('log_datas: %s' % log_datas)
|
||||||
|
# for log_data in log_datas:
|
||||||
|
# res['data'].append({
|
||||||
|
# 'equipment_code': log_data.equipment_code,
|
||||||
|
# 'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
# 'state': log_data.state
|
||||||
|
#
|
||||||
|
# })
|
||||||
|
#
|
||||||
|
# return json.JSONEncoder().encode(res)
|
||||||
|
#
|
||||||
|
# except Exception as e:
|
||||||
|
# logging.info('前端请求日志数据失败,原因:%s' % e)
|
||||||
|
# res['status'] = -1
|
||||||
|
# res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||||
|
# return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
@http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
|
def logs_list(self, **kw):
|
||||||
|
"""
|
||||||
|
拿到日志数据返回给大屏展示
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
|
logging.info('前端请求日志数据的参数为:%s' % kw)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 获取请求的日志数据
|
||||||
|
logs_obj = request.env['maintenance.equipment.oee.log.detail'].sudo()
|
||||||
|
# 获取请求的机床数据
|
||||||
|
machine_list = ast.literal_eval(kw['machine_list'])
|
||||||
|
begin_time_str = kw['begin_time'].strip('"')
|
||||||
|
end_time_str = kw['end_time'].strip('"')
|
||||||
|
|
||||||
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
print('begin_time: %s' % begin_time)
|
||||||
|
|
||||||
|
for item in machine_list:
|
||||||
|
log_datas = logs_obj.search(
|
||||||
|
[('equipment_code', '=', item), ('time', '>=', begin_time), ('time', '<=', end_time)])
|
||||||
|
print('log_datas: %s' % log_datas)
|
||||||
|
|
||||||
|
# 将数据按照 equipment_code 进行分组
|
||||||
|
if item not in res['data']:
|
||||||
|
res['data'][item] = []
|
||||||
|
|
||||||
|
for log_data in log_datas:
|
||||||
|
res['data'][item].append({
|
||||||
|
'time': log_data.time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'state': log_data.state,
|
||||||
|
'production_name': log_data.production_name,
|
||||||
|
})
|
||||||
|
|
||||||
|
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('前端请求日志数据失败,原因:%s' % e)
|
||||||
|
res['status'] = -1
|
||||||
|
res['message'] = '前端请求日志数据失败,原因:%s' % e
|
||||||
|
return json.dumps(res)
|
||||||
|
|
||||||
|
# 返回CNC机床列表
|
||||||
|
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def CNCList(self, **kw):
|
||||||
|
"""
|
||||||
|
获取CNC机床列表
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
# logging.info('CNCList:%s' % kw)
|
||||||
|
try:
|
||||||
|
res = {'Succeed': True}
|
||||||
|
# cnc_list = request.env['sf.cnc.equipment'].sudo().search([])
|
||||||
|
# cnc_list = ["XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
|
||||||
|
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
|
||||||
|
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-6", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-7",
|
||||||
|
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-8", "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-2",
|
||||||
|
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-9", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-10",
|
||||||
|
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-11", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-12",
|
||||||
|
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-13", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-14"]
|
||||||
|
|
||||||
|
cnc_list_obj = request.env['maintenance.equipment'].sudo().search(
|
||||||
|
[('function_type', '!=', False), ('active', '=', True)])
|
||||||
|
cnc_list = list(map(lambda x: x.code, cnc_list_obj))
|
||||||
|
print('cnc_list: %s' % cnc_list)
|
||||||
|
res['CNCList'] = cnc_list
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
|
logging.info('CNCList error:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -121,6 +121,13 @@ class Machine_ftp(models.Model):
|
|||||||
"""
|
"""
|
||||||
_inherit = 'maintenance.equipment'
|
_inherit = 'maintenance.equipment'
|
||||||
|
|
||||||
|
# 机床首次上线时间(默认取值2024年08月01日零点)
|
||||||
|
|
||||||
|
def _get_default_online_time(self):
|
||||||
|
return datetime(2024, 1, 1, 0, 0, 0)
|
||||||
|
|
||||||
|
first_online_time = fields.Datetime(string='首次上线时间', default=_get_default_online_time)
|
||||||
|
|
||||||
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
|
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
|
||||||
|
|
||||||
# # 机床配置项目
|
# # 机床配置项目
|
||||||
@@ -275,7 +282,28 @@ class Machine_ftp(models.Model):
|
|||||||
alarm_msg = fields.Char('故障报警信息', readonly=True)
|
alarm_msg = fields.Char('故障报警信息', readonly=True)
|
||||||
clear_time = fields.Char('故障消除时间(复原时间)', readonly=True)
|
clear_time = fields.Char('故障消除时间(复原时间)', readonly=True)
|
||||||
|
|
||||||
# 当前程序名, 机床累计运行时间, 机床系统日期, 机床系统时间, 当前刀具号, 机床循环时间
|
# # 开动率
|
||||||
|
run_rate = fields.Char('开动率', readonly=True)
|
||||||
|
|
||||||
|
# 同步CNC设备到oee
|
||||||
|
def sync_oee(self):
|
||||||
|
"""
|
||||||
|
同步CNC设备到oee
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
for record in self:
|
||||||
|
record.ensure_one()
|
||||||
|
cnc_oee_dict = {
|
||||||
|
'equipment_id': record.id,
|
||||||
|
'type_id': record.type_id.id,
|
||||||
|
'machine_tool_picture': record.machine_tool_picture,
|
||||||
|
'equipment_code': record.code,
|
||||||
|
'function_type': record.function_type,
|
||||||
|
}
|
||||||
|
if self.env['maintenance.equipment.oee.logs'].search([('equipment_id', '=', record.id)]):
|
||||||
|
self.env['maintenance.equipment.oee.logs'].write(cnc_oee_dict)
|
||||||
|
else:
|
||||||
|
self.env['maintenance.equipment.oee.logs'].create(cnc_oee_dict)
|
||||||
|
|
||||||
|
|
||||||
class WorkCenterBarcode(models.Model):
|
class WorkCenterBarcode(models.Model):
|
||||||
|
|||||||
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>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
<field name="run_status"/>
|
<field name="run_status"/>
|
||||||
<field name="run_time"/>
|
<field name="run_time"/>
|
||||||
<field name="system_date"/>
|
<field name="system_date"/>
|
||||||
|
<field name="first_online_time"/>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="cut_status"/>
|
<field name="cut_status"/>
|
||||||
|
|||||||
17
sf_machine_connect/views/maintenance_views.xml
Normal file
17
sf_machine_connect/views/maintenance_views.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- 修改设备列表视图-->
|
||||||
|
<record id="sf_machine_hr_equipment_view_tree_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">sf.machine.hr.equipment.view.tree.inherit</field>
|
||||||
|
<field name="model">maintenance.equipment</field>
|
||||||
|
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//tree" position="inside">
|
||||||
|
<header>
|
||||||
|
<button name="sync_oee" type="object" string="同步设备至OEE"/>
|
||||||
|
</header>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -41,29 +41,32 @@ class SfMaintenanceEquipmentOEELog(models.Model):
|
|||||||
_name = 'maintenance.equipment.oee.logs'
|
_name = 'maintenance.equipment.oee.logs'
|
||||||
_description = '设备运行日志'
|
_description = '设备运行日志'
|
||||||
|
|
||||||
equipment_id = fields.Many2one('maintenance.equipment', '机台号')
|
equipment_id = fields.Many2one('maintenance.equipment', '机台号', readonly='True')
|
||||||
equipment_code = fields.Char('设备编码')
|
equipment_code = fields.Char('设备编码', readonly='True')
|
||||||
name = fields.Char('设备名称', readonly='True')
|
name = fields.Char('设备名称', readonly='True')
|
||||||
|
function_type = fields.Selection(
|
||||||
|
[("ZXJGZX", "钻铣加工中心"), ("CXJGZX", "车削加工中心"), ("FHJGZX", "复合加工中心")],
|
||||||
|
default="", string="功能类型")
|
||||||
machine_tool_picture = fields.Binary('设备图片')
|
machine_tool_picture = fields.Binary('设备图片')
|
||||||
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号')
|
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号', reaonly='True')
|
||||||
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
|
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
|
||||||
("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
|
("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
|
||||||
online_time = fields.Char('开机时长')
|
online_time = fields.Char('开机时长', reaonly='True')
|
||||||
|
|
||||||
offline_time = fields.Char('关机时长')
|
offline_time = fields.Char('关机时长', reaonly='True')
|
||||||
offline_nums = fields.Integer('关机次数')
|
offline_nums = fields.Integer('关机次数', reaonly='True')
|
||||||
# 待机时长
|
# 待机时长
|
||||||
|
|
||||||
idle_time = fields.Char('待机时长')
|
idle_time = fields.Char('待机时长', reaonly='True')
|
||||||
|
|
||||||
# 待机率
|
# 待机率
|
||||||
idle_rate = fields.Char('待机率')
|
idle_rate = fields.Char('待机率', reaonly='True')
|
||||||
|
|
||||||
work_time = fields.Char('加工时长')
|
work_time = fields.Char('加工时长', reaonly='True')
|
||||||
work_rate = fields.Char('可用率')
|
work_rate = fields.Char('可用率', reaonly='True')
|
||||||
fault_time = fields.Char('故障时长')
|
fault_time = fields.Char('故障时长', reaonly='True')
|
||||||
fault_rate = fields.Char('故障率')
|
fault_rate = fields.Char('故障率', reaonly='True')
|
||||||
fault_nums = fields.Integer('故障次数')
|
fault_nums = fields.Integer('故障次数', reaonly='True')
|
||||||
|
|
||||||
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
|
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
|
||||||
|
|
||||||
@@ -81,12 +84,15 @@ class SfMaintenanceEquipmentOEELog(models.Model):
|
|||||||
class SfMaintenanceEquipmentOEELogDetail(models.Model):
|
class SfMaintenanceEquipmentOEELogDetail(models.Model):
|
||||||
_name = 'maintenance.equipment.oee.log.detail'
|
_name = 'maintenance.equipment.oee.log.detail'
|
||||||
_description = '设备运行日志详情'
|
_description = '设备运行日志详情'
|
||||||
|
_order = 'time desc'
|
||||||
|
|
||||||
sequence = fields.Integer('序号')
|
# sequence = fields.Integer('序号', related='id')
|
||||||
time = fields.Datetime('时间')
|
time = fields.Datetime('时间')
|
||||||
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
|
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
|
||||||
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
|
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
|
||||||
production_id = fields.Many2one('mrp.production', '加工工单')
|
production_name = fields.Char('加工工单')
|
||||||
|
|
||||||
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
|
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
|
||||||
|
# equipment_code = fields.Char('设备编码', related='log_id.equipment_code')
|
||||||
|
equipment_code = fields.Char('设备编码', readonly='True')
|
||||||
|
|
||||||
|
|||||||
@@ -159,6 +159,8 @@
|
|||||||
<field name="equipment_id" domain="[('name','ilike','加工中心')]"/>
|
<field name="equipment_id" domain="[('name','ilike','加工中心')]"/>
|
||||||
<field name="type_id"/>
|
<field name="type_id"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
|
<field name="equipment_code"/>
|
||||||
|
<field name="function_type"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
@@ -202,10 +204,10 @@
|
|||||||
<!-- <field name="detail_ids" domain="[('time','<',(datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'))]"> -->
|
<!-- <field name="detail_ids" domain="[('time','<',(datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'))]"> -->
|
||||||
<field name="detail_ids" domain="[('state','ilike','加工')]">
|
<field name="detail_ids" domain="[('state','ilike','加工')]">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="sequence"/>
|
<!-- <field name="sequence"/> -->
|
||||||
<field name="time"/>
|
<field name="time"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<field name="production_id"/>
|
<field name="production_name"/>
|
||||||
</tree>
|
</tree>
|
||||||
<!-- <form> -->
|
<!-- <form> -->
|
||||||
<!-- <field name="sequence"/> -->
|
<!-- <field name="sequence"/> -->
|
||||||
@@ -219,10 +221,10 @@
|
|||||||
<page string="历史日志详情">
|
<page string="历史日志详情">
|
||||||
<field name="detail_ids">
|
<field name="detail_ids">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="sequence"/>
|
<!-- <field name="sequence"/> -->
|
||||||
<field name="time"/>
|
<field name="time"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<field name="production_id"/>
|
<field name="production_name"/>
|
||||||
</tree>
|
</tree>
|
||||||
<!-- <form> -->
|
<!-- <form> -->
|
||||||
<!-- <field name="sequence"/> -->
|
<!-- <field name="sequence"/> -->
|
||||||
@@ -263,10 +265,10 @@
|
|||||||
<field name="model">maintenance.equipment.oee.log.detail</field>
|
<field name="model">maintenance.equipment.oee.log.detail</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="sequence"/>
|
<!-- <field name="sequence"/> -->
|
||||||
<field name="time"/>
|
<field name="time"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<field name="production_id"/>
|
<field name="production_name"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -280,10 +282,10 @@
|
|||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<field name="production_id"/>
|
<!-- <field name="production_id"/> -->
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="sequence"/>
|
<!-- <field name="sequence"/> -->
|
||||||
<field name="time"/>
|
<field name="time"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -15,12 +15,14 @@
|
|||||||
'data/stock_data.xml',
|
'data/stock_data.xml',
|
||||||
'data/empty_racks_data.xml',
|
'data/empty_racks_data.xml',
|
||||||
'data/panel_data.xml',
|
'data/panel_data.xml',
|
||||||
|
'data/agv_scheduling_data.xml',
|
||||||
'security/group_security.xml',
|
'security/group_security.xml',
|
||||||
'security/ir.model.access.csv',
|
'security/ir.model.access.csv',
|
||||||
'wizard/workpiece_delivery_views.xml',
|
'wizard/workpiece_delivery_views.xml',
|
||||||
'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_scheduling_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 +32,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': {
|
||||||
|
|
||||||
@@ -40,7 +41,9 @@
|
|||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
'sf_manufacturing/static/src/xml/kanban_change.xml',
|
'sf_manufacturing/static/src/xml/kanban_change.xml',
|
||||||
'sf_manufacturing/static/src/js/kanban_change.js',
|
'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',
|
||||||
|
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
|
||||||
]
|
]
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
|
||||||
from odoo import http
|
from odoo import http
|
||||||
from odoo.http import request
|
from odoo.http import request
|
||||||
|
|
||||||
@@ -386,7 +388,7 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
ret = json.loads(datas)
|
ret = json.loads(datas)
|
||||||
request.env['center_control.interface.log'].sudo().create(
|
request.env['center_control.interface.log'].sudo().create(
|
||||||
{'content': ret, 'name': 'AutoDeviceApi/LocationChange'})
|
{'content': ret, 'name': 'AutoDeviceApi/LocationChange'})
|
||||||
logging.info('LocationChange_ret===========:%s' % ret)
|
logging.info('库位变更LocationChange_ret:%s' % ret)
|
||||||
RfidCode = ret['RfidCode']
|
RfidCode = ret['RfidCode']
|
||||||
ChangeType = ret['ChangeType']
|
ChangeType = ret['ChangeType']
|
||||||
OldDeciveId = ret['OldDeciveId']
|
OldDeciveId = ret['OldDeciveId']
|
||||||
@@ -396,34 +398,79 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
OldDeciveStart = ret['OldDeciveStart']
|
OldDeciveStart = ret['OldDeciveStart']
|
||||||
OldDeciveEnd = ret['OldDeciveEnd']
|
OldDeciveEnd = ret['OldDeciveEnd']
|
||||||
|
|
||||||
temp_val_sn_id = None
|
if ChangeType == 'Part':
|
||||||
old_localtion = None
|
temp_val_sn_id = None
|
||||||
# if ChangeType == 'Part' or ChangeType == 'Tool':
|
old_localtion = None
|
||||||
stock_lot_obj = request.env['stock.lot'].sudo().search(
|
# if ChangeType == 'Part' or ChangeType == 'Tool':
|
||||||
[('rfid', '=', RfidCode)], limit=1)
|
stock_lot_obj = request.env['stock.lot'].sudo().search(
|
||||||
logging.info('stock_lot_obj===========:%s' % stock_lot_obj)
|
[('rfid', '=', RfidCode)], limit=1)
|
||||||
if not stock_lot_obj:
|
logging.info('stock_lot_obj===========:%s' % stock_lot_obj)
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'}
|
if not stock_lot_obj:
|
||||||
return json.JSONEncoder().encode(res)
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'}
|
||||||
if OldPosition:
|
return json.JSONEncoder().encode(res)
|
||||||
old_localtion = request.env['sf.shelf.location'].sudo().search(
|
if OldPosition:
|
||||||
[('barcode', '=', OldPosition)], limit=1)
|
old_localtion = request.env['sf.shelf.location'].sudo().search(
|
||||||
logging.info('old_localtion===========:%s' % old_localtion)
|
[('barcode', '=', OldPosition)], limit=1)
|
||||||
new_localtion = request.env['sf.shelf.location'].sudo().search(
|
logging.info('old_localtion===========:%s' % old_localtion)
|
||||||
[('barcode', '=', NewPosition)], limit=1)
|
new_localtion = request.env['sf.shelf.location'].sudo().search(
|
||||||
logging.info('new_localtion===========:%s' % new_localtion)
|
[('barcode', '=', NewPosition)], limit=1)
|
||||||
if not new_localtion:
|
logging.info('new_localtion===========:%s' % new_localtion)
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
|
if not new_localtion:
|
||||||
return json.JSONEncoder().encode(res)
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
|
||||||
if old_localtion:
|
return json.JSONEncoder().encode(res)
|
||||||
temp_val_sn_id = old_localtion.product_sn_id
|
if old_localtion:
|
||||||
logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id)
|
temp_val_sn_id = old_localtion.product_sn_id
|
||||||
old_localtion.product_sn_id = None
|
logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id)
|
||||||
new_localtion.product_sn_id = temp_val_sn_id
|
old_localtion.product_sn_id = None
|
||||||
logging.info('====1======')
|
new_localtion.product_sn_id = temp_val_sn_id
|
||||||
else:
|
logging.info('====1======')
|
||||||
new_localtion.product_sn_id = stock_lot_obj.id
|
else:
|
||||||
logging.info('=====2======')
|
new_localtion.product_sn_id = stock_lot_obj.id
|
||||||
|
logging.info('=====2======')
|
||||||
|
elif ChangeType == 'Tool':
|
||||||
|
# 对功能刀具库位变更信息进行更改
|
||||||
|
def write_tool(DeciveId):
|
||||||
|
if 'Tool' in DeciveId:
|
||||||
|
shelfinfo = list(filter(lambda x: x.get('DeviceId') == DeciveId,
|
||||||
|
request.env['sf.shelf.location'].sudo().get_sf_shelf_location_info(
|
||||||
|
DeciveId)))
|
||||||
|
total_data = request.env['sf.shelf.location.datasync'].sudo().get_total_data()
|
||||||
|
for item in shelfinfo:
|
||||||
|
shelf_barcode = request.env['sf.shelf.location.datasync'].sudo().find_our_code(
|
||||||
|
total_data, item['Postion'])
|
||||||
|
location_id = request.env['sf.shelf.location'].sudo().search(
|
||||||
|
[('barcode', '=', shelf_barcode)],
|
||||||
|
limit=1)
|
||||||
|
if location_id:
|
||||||
|
# 如果是线边刀库信息,则对功能刀具移动生成记录
|
||||||
|
if 'Tool' in item['Postion']:
|
||||||
|
tool = request.env['sf.functional.cutting.tool.entity'].sudo().search(
|
||||||
|
[('rfid', '=', item['RfidCode']), ('functional_tool_status', '!=', '已拆除')])
|
||||||
|
tool.sudo().tool_in_out_stock_location(location_id)
|
||||||
|
if tool:
|
||||||
|
location_id.product_sn_id = tool.barcode_id.id
|
||||||
|
# 修改功能刀具状态
|
||||||
|
tool_state = {'Nomal': '正常', 'Warning': '报警'}
|
||||||
|
if tool_state.get(item.get('State')):
|
||||||
|
if tool_state.get(item.get('State')) != tool.functional_tool_status:
|
||||||
|
tool.write({
|
||||||
|
'functional_tool_status': tool_state.get(item['State'])
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
location_id.product_sn_id = False
|
||||||
|
logging.info('货架已获取信息:%s' % item)
|
||||||
|
else:
|
||||||
|
equipment_id = request.env['maintenance.equipment'].sudo().search([('name', '=', DeciveId)])
|
||||||
|
if equipment_id:
|
||||||
|
equipment_id.sudo().register_equipment_tool()
|
||||||
|
else:
|
||||||
|
res_1 = {'Succeed': False, 'ErrorCode': 202, 'Error': f'设备【{DeciveId}】不存在'}
|
||||||
|
return json.JSONEncoder().encode(res_1)
|
||||||
|
|
||||||
|
if OldDeciveId:
|
||||||
|
write_tool(OldDeciveId)
|
||||||
|
elif NewDeciveId:
|
||||||
|
write_tool(NewDeciveId)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
logging.info('LocationChange error:%s' % e)
|
logging.info('LocationChange error:%s' % e)
|
||||||
@@ -448,13 +495,16 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
if 'DeviceId' in ret:
|
if 'DeviceId' in ret:
|
||||||
logging.info('DeviceId:%s' % ret['DeviceId'])
|
logging.info('DeviceId:%s' % ret['DeviceId'])
|
||||||
if 'IsComplete' in ret:
|
if 'IsComplete' in ret:
|
||||||
|
rfid_codes = []
|
||||||
|
workorder_ids = []
|
||||||
if ret['IsComplete'] is True or ret['IsComplete'] is False:
|
if ret['IsComplete'] is True or ret['IsComplete'] is False:
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
logging.info('F-RfidCode:%s' % i)
|
logging.info('F-RfidCode:%s' % i)
|
||||||
if f'RfidCode{i}' in ret:
|
if f'RfidCode{i}' in ret:
|
||||||
rfid_code = ret[f'RfidCode{i}']
|
rfid_code = ret[f'RfidCode{i}']
|
||||||
logging.info('RfidCode:%s' % rfid_code)
|
logging.info('RfidCode:%s' % rfid_code)
|
||||||
if rfid_code is not None:
|
if rfid_code is not None and rfid_code != '':
|
||||||
|
rfid_codes.append(rfid_code)
|
||||||
domain = [
|
domain = [
|
||||||
('rfid_code', '=', rfid_code),
|
('rfid_code', '=', rfid_code),
|
||||||
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
|
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
|
||||||
@@ -462,6 +512,7 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
|
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
|
||||||
if workorder:
|
if workorder:
|
||||||
for order in workorder:
|
for order in workorder:
|
||||||
|
workorder_ids.append(order.id)
|
||||||
if order.production_line_state == '待上产线':
|
if order.production_line_state == '待上产线':
|
||||||
logging.info(
|
logging.info(
|
||||||
'工单产线状态:%s' % order.production_line_state)
|
'工单产线状态:%s' % order.production_line_state)
|
||||||
@@ -470,23 +521,30 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
('processing_panel', '=', order.processing_panel)])
|
('processing_panel', '=', order.processing_panel)])
|
||||||
if panel_workorder:
|
if panel_workorder:
|
||||||
panel_workorder.write({'production_line_state': '已上产线'})
|
panel_workorder.write({'production_line_state': '已上产线'})
|
||||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||||
[
|
# [
|
||||||
('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
|
# ('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
|
||||||
('production_id', '=', order.production_id.id),
|
# ('production_id', '=', order.production_id.id),
|
||||||
('workorder_id', '=', order.id),
|
# ('workorder_id', '=', order.id),
|
||||||
('workorder_state', '=', 'done')])
|
# ('workorder_state', '=', 'done')])
|
||||||
if workpiece_delivery.status == '待下发':
|
# if workpiece_delivery.status == '待下发':
|
||||||
workpiece_delivery.write({'is_manual_work': True})
|
# workpiece_delivery.write({'is_manual_work': True})
|
||||||
|
# 下发
|
||||||
else:
|
else:
|
||||||
res = {'Succeed': False, 'ErrorCode': 204,
|
res = {'Succeed': False, 'ErrorCode': 204,
|
||||||
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
|
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
|
||||||
|
if ret['IsComplete'] is True:
|
||||||
|
# 向AGV任务调度下发运送空料架任务
|
||||||
|
workorders = request.env['mrp.workorder'].browse(workorder_ids)
|
||||||
|
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '运送空料架', workorders)
|
||||||
else:
|
else:
|
||||||
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
|
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
|
||||||
else:
|
else:
|
||||||
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
|
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
|
||||||
|
except RepeatTaskException as e:
|
||||||
|
logging.info('AGVToProduct error:%s' % e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
|
||||||
logging.info('AGVToProduct error:%s' % e)
|
logging.info('AGVToProduct error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
@@ -509,7 +567,8 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
logging.info('ret:%s' % ret)
|
logging.info('ret:%s' % ret)
|
||||||
if 'DeviceId' in ret:
|
if 'DeviceId' in ret:
|
||||||
logging.info('DeviceId:%s' % ret['DeviceId'])
|
logging.info('DeviceId:%s' % ret['DeviceId'])
|
||||||
delivery_Arr = []
|
# delivery_Arr = []
|
||||||
|
workorder_ids = []
|
||||||
if 'IsComplete' in ret:
|
if 'IsComplete' in ret:
|
||||||
if ret['IsComplete'] is True or ret['IsComplete'] is False:
|
if ret['IsComplete'] is True or ret['IsComplete'] is False:
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
@@ -517,7 +576,7 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
if f'RfidCode{i}' in ret:
|
if f'RfidCode{i}' in ret:
|
||||||
rfid_code = ret[f'RfidCode{i}']
|
rfid_code = ret[f'RfidCode{i}']
|
||||||
logging.info('RfidCode:%s' % rfid_code)
|
logging.info('RfidCode:%s' % rfid_code)
|
||||||
if rfid_code is not None:
|
if rfid_code is not None and rfid_code != '':
|
||||||
domain = [
|
domain = [
|
||||||
('rfid_code', '=', rfid_code),
|
('rfid_code', '=', rfid_code),
|
||||||
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
|
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
|
||||||
@@ -525,6 +584,7 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
|
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
|
||||||
if workorder:
|
if workorder:
|
||||||
for order in workorder:
|
for order in workorder:
|
||||||
|
workorder_ids.append(order.id)
|
||||||
if order.production_line_state == '已上产线':
|
if order.production_line_state == '已上产线':
|
||||||
logging.info(
|
logging.info(
|
||||||
'工单产线状态:%s' % order.production_line_state)
|
'工单产线状态:%s' % order.production_line_state)
|
||||||
@@ -534,35 +594,41 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
if panel_workorder:
|
if panel_workorder:
|
||||||
panel_workorder.write({'production_line_state': '已下产线'})
|
panel_workorder.write({'production_line_state': '已下产线'})
|
||||||
workorder.write({'state': 'to be detected'})
|
workorder.write({'state': 'to be detected'})
|
||||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||||
[
|
# [
|
||||||
('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
|
# ('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
|
||||||
('production_id', '=', order.production_id.id),
|
# ('production_id', '=', order.production_id.id),
|
||||||
('workorder_id', '=', order.id),
|
# ('workorder_id', '=', order.id),
|
||||||
('workorder_state', '=', 'done')])
|
# ('workorder_state', '=', 'done')])
|
||||||
if workpiece_delivery:
|
# if workpiece_delivery:
|
||||||
delivery_Arr.append(workpiece_delivery.id)
|
# delivery_Arr.append(workpiece_delivery.id)
|
||||||
else:
|
else:
|
||||||
res = {'Succeed': False, 'ErrorCode': 204,
|
res = {'Succeed': False, 'ErrorCode': 204,
|
||||||
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
|
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
|
||||||
if delivery_Arr:
|
# if delivery_Arr:
|
||||||
logging.info('delivery_Arr:%s' % delivery_Arr)
|
# logging.info('delivery_Arr:%s' % delivery_Arr)
|
||||||
delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
|
# delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
|
||||||
[('id', 'in', delivery_Arr)])
|
# [('id', 'in', delivery_Arr)])
|
||||||
if delivery_workpiece:
|
# if delivery_workpiece:
|
||||||
logging.info('开始向agv下发下产线任务')
|
# logging.info('开始向agv下发下产线任务')
|
||||||
agv_site = request.env['sf.agv.site'].sudo().search([])
|
# agv_site = request.env['sf.agv.site'].sudo().search([])
|
||||||
if agv_site:
|
# if agv_site:
|
||||||
has_site = agv_site.update_site_state()
|
# has_site = agv_site.update_site_state()
|
||||||
if has_site is True:
|
# if has_site is True:
|
||||||
is_free = delivery_workpiece._check_avgsite_state()
|
# is_free = delivery_workpiece._check_avgsite_state()
|
||||||
if is_free is True:
|
# if is_free is True:
|
||||||
delivery_workpiece._delivery_avg()
|
# delivery_workpiece._delivery_avg()
|
||||||
logging.info('agv下发下产线任务下发完成')
|
# logging.info('agv下发下产线任务下发完成')
|
||||||
|
if ret['IsComplete'] is True:
|
||||||
|
# 向AGV任务调度下发下产线任务
|
||||||
|
workorders = request.env['mrp.workorder'].browse(workorder_ids)
|
||||||
|
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '下产线', workorders)
|
||||||
else:
|
else:
|
||||||
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
|
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
|
||||||
|
except RepeatTaskException as e:
|
||||||
|
logging.info('AGVToProduct error:%s' % e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
|
||||||
logging.info('AGVDownProduct error:%s' % e)
|
logging.info('AGVDownProduct error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
@@ -600,3 +666,27 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
logging.info('AGVDownProduct error:%s' % e)
|
logging.info('AGVDownProduct error:%s' % e)
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
|
|
||||||
|
@http.route('/AutoDeviceApi/GetAgvStationState', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||||
|
cors="*")
|
||||||
|
def AGVStationState(self, **kw):
|
||||||
|
"""
|
||||||
|
中控推送接驳站状态
|
||||||
|
:param kw:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
logging.info('AGVStationState:%s' % kw)
|
||||||
|
try:
|
||||||
|
res = {'Succeed': True}
|
||||||
|
datas = request.httprequest.data
|
||||||
|
ret = json.loads(datas)
|
||||||
|
request.env['center_control.interface.log'].sudo().create(
|
||||||
|
{'content': ret, 'name': 'AutoDeviceApi/AGVStationState'})
|
||||||
|
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']: '占用' if ret['AtHome'] else '空闲'})
|
||||||
|
except Exception as e:
|
||||||
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
|
||||||
|
logging.info('AGVDownProduct error:%s' % e)
|
||||||
|
return json.JSONEncoder().encode(res)
|
||||||
@@ -25,15 +25,14 @@ class Workpiece(http.Controller):
|
|||||||
if 'reqCode' in ret:
|
if 'reqCode' in ret:
|
||||||
if 'method' in ret:
|
if 'method' in ret:
|
||||||
if ret['method'] == 'end':
|
if ret['method'] == 'end':
|
||||||
req_codes = ret['reqCode'].split(',')
|
# 找到对应的AGV调度任务
|
||||||
for req_code in req_codes:
|
agv_scheduling = request.env['sf.agv.scheduling'].sudo().search(
|
||||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
[('name', '=', ret['reqCode']), ('state', '=', '配送中')])
|
||||||
[('name', '=', req_code.strip()), ('task_completion_time', '=', False)])
|
if agv_scheduling:
|
||||||
if workpiece_delivery:
|
agv_scheduling.finish_scheduling()
|
||||||
workpiece_delivery.write({'status': '已配送', 'task_completion_time': datetime.now()})
|
else:
|
||||||
else:
|
res = {'Succeed': False, 'ErrorCode': 203,
|
||||||
res = {'Succeed': False, 'ErrorCode': 203,
|
'Error': '该reqCode暂未查到对应的AGV任务记录'}
|
||||||
'Error': '该reqCode暂未查到对应的工件配送记录'}
|
|
||||||
else:
|
else:
|
||||||
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '未传method字段'}
|
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '未传method字段'}
|
||||||
else:
|
else:
|
||||||
|
|||||||
15
sf_manufacturing/data/agv_scheduling_data.xml
Normal file
15
sf_manufacturing/data/agv_scheduling_data.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="sequence_agv_scheduling" model="ir.sequence">
|
||||||
|
<field name="name">AGV调度</field>
|
||||||
|
<field name="code">sf.agv.scheduling</field>
|
||||||
|
<field name="prefix">B%(year)s%(month)s%(day)s</field>
|
||||||
|
<field name="padding">4</field>
|
||||||
|
<field name="number_next">1</field>
|
||||||
|
<field name="implementation">standard</field>
|
||||||
|
<field name="use_date_range">True</field>
|
||||||
|
<field name="company_id" eval="False"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -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_scheduling
|
||||||
|
|||||||
253
sf_manufacturing/models/agv_scheduling.py
Normal file
253
sf_manufacturing/models/agv_scheduling.py
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class RepeatTaskException(UserError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AgvScheduling(models.Model):
|
||||||
|
_name = 'sf.agv.scheduling'
|
||||||
|
_description = 'agv调度'
|
||||||
|
_order = 'id desc'
|
||||||
|
|
||||||
|
name = fields.Char('任务单号', index=True, copy=False)
|
||||||
|
|
||||||
|
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='任务类型', required=True)
|
||||||
|
agv_route_name = fields.Char('任务路线名称')
|
||||||
|
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True)
|
||||||
|
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True)
|
||||||
|
site_state = fields.Selection([
|
||||||
|
('占用', '占用'),
|
||||||
|
('空闲', '空闲')], string='终点接驳站状态', default='占用')
|
||||||
|
state = fields.Selection([
|
||||||
|
('待下发', '待下发'),
|
||||||
|
('配送中', '配送中'),
|
||||||
|
('已配送', '已配送'),
|
||||||
|
('已取消', '已取消')], string='状态', default='待下发', tracking=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('任务完成时间')
|
||||||
|
task_duration = fields.Char('任务时长', compute='_compute_task_duration')
|
||||||
|
|
||||||
|
@api.depends('agv_route_type')
|
||||||
|
def _compute_delivery_workpieces(self):
|
||||||
|
for record in self:
|
||||||
|
if record.agv_route_type == '运送空料架':
|
||||||
|
record.delivery_workpieces = '/'
|
||||||
|
else:
|
||||||
|
record.delivery_workpieces = '、'.join(record.workorder_ids.mapped('production_id.name'))
|
||||||
|
|
||||||
|
delivery_workpieces = fields.Char('配送工件', compute=_compute_delivery_workpieces)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def web_search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, count_limit=None):
|
||||||
|
domain = domain or []
|
||||||
|
new_domain = []
|
||||||
|
for index, item in enumerate(domain):
|
||||||
|
if isinstance(item, list):
|
||||||
|
if item[0] == 'delivery_workpieces':
|
||||||
|
new_domain.append('&')
|
||||||
|
new_domain.append(['workorder_ids.production_id.name', item[1], item[2]])
|
||||||
|
new_domain.append(['agv_route_type', '!=', '运送空料架'])
|
||||||
|
continue
|
||||||
|
new_domain.append(item)
|
||||||
|
|
||||||
|
return super(AgvScheduling, self).web_search_read(new_domain, fields, limit=limit, offset=offset)
|
||||||
|
|
||||||
|
@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 = ''
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
# We generate a standard reference
|
||||||
|
for vals in vals_list:
|
||||||
|
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_name, agv_route_type, workorders):
|
||||||
|
""" add_scheduling(agv_start_site_id, agv_route_type, workorders) -> agv_scheduling
|
||||||
|
新增AGV调度
|
||||||
|
params:
|
||||||
|
agv_start_site_name: AGV起点接驳站名称
|
||||||
|
agv_route_type: AGV任务类型
|
||||||
|
workorders: 工单
|
||||||
|
"""
|
||||||
|
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s】' % (agv_start_site_name, agv_route_type, workorders))
|
||||||
|
if not workorders:
|
||||||
|
raise UserError(_('工单不能为空'))
|
||||||
|
agv_start_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)], limit=1)
|
||||||
|
if not agv_start_site:
|
||||||
|
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
|
||||||
|
# 如果存在相同任务类型工单的AGV调度任务,则提示错误
|
||||||
|
agv_scheduling = self.sudo().search([
|
||||||
|
('workorder_ids', 'in', workorders.ids),
|
||||||
|
('agv_route_type', '=', agv_route_type),
|
||||||
|
('state', 'in', ['待下发', '配送中'])
|
||||||
|
], 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,
|
||||||
|
'workorder_ids': workorders.ids,
|
||||||
|
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
|
||||||
|
'task_create_time': fields.Datetime.now()
|
||||||
|
}
|
||||||
|
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
|
||||||
|
agv_routes = self.env['sf.agv.task.route'].sudo().search([
|
||||||
|
('route_type', '=', agv_route_type),
|
||||||
|
('start_site_id', '=', agv_start_site.id)
|
||||||
|
])
|
||||||
|
if not agv_routes:
|
||||||
|
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
|
||||||
|
idle_route = None
|
||||||
|
if len(agv_routes) == 1:
|
||||||
|
idle_route = agv_routes[0]
|
||||||
|
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_name': idle_route.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)
|
||||||
|
idle_route = idle_routes[0]
|
||||||
|
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_name': idle_route.name})
|
||||||
|
try:
|
||||||
|
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
|
||||||
|
# 触发空闲接驳站状态更新,触发新任务下发
|
||||||
|
if idle_route and idle_route.end_site_id.state == '空闲':
|
||||||
|
scheduling.dispatch_scheduling(idle_route)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('添加AGV调度任务失败: %s', e)
|
||||||
|
raise UserError(_('添加AGV调度任务失败: %s', e))
|
||||||
|
|
||||||
|
return scheduling
|
||||||
|
|
||||||
|
def on_site_state_change(self, agv_site_id, agv_site_state):
|
||||||
|
"""
|
||||||
|
响应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
|
||||||
|
)
|
||||||
|
task_route = task_routes.filtered(
|
||||||
|
lambda r: r.start_site_id == agv_scheduling.start_site_id and r.start_site_id == agv_scheduling.start_site_id
|
||||||
|
)
|
||||||
|
# 下发AGV调度任务并修改接驳站状态为占用
|
||||||
|
agv_scheduling.dispatch_scheduling(task_route)
|
||||||
|
|
||||||
|
def _delivery_avg(self):
|
||||||
|
config = self.env['res.config.settings'].get_values()
|
||||||
|
position_code_arr = [{
|
||||||
|
'positionCode': self.start_site_id.name,
|
||||||
|
'code': '00'
|
||||||
|
}, {
|
||||||
|
'positionCode': self.end_site_id.name,
|
||||||
|
'code': '00'
|
||||||
|
}]
|
||||||
|
res = {'reqCode': self.name, 'reqTime': '', 'clientCode': '', 'tokenCode': '',
|
||||||
|
'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'],
|
||||||
|
'positionCodePath': position_code_arr,
|
||||||
|
'podCode': '',
|
||||||
|
'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '',
|
||||||
|
'data': ''}
|
||||||
|
try:
|
||||||
|
logging.info('AGV请求路径:%s' % config['agv_rcs_url'])
|
||||||
|
logging.info('AGV-json:%s' % res)
|
||||||
|
headers = {'Content-Type': 'application/json'}
|
||||||
|
ret = requests.post((config['agv_rcs_url']), json=res, headers=headers)
|
||||||
|
ret = ret.json()
|
||||||
|
logging.info('config-ret:%s' % ret)
|
||||||
|
if ret['code'] == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise UserError(ret['message'])
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('config-e:%s' % e)
|
||||||
|
raise UserError("工件配送请求agv失败:%s" % e)
|
||||||
|
|
||||||
|
def button_cancel(self):
|
||||||
|
# 弹出二次确认窗口后执行
|
||||||
|
for rec in self:
|
||||||
|
if rec.state != '待下发':
|
||||||
|
raise UserError('只有待下发状态的AGV调度任务才能取消!')
|
||||||
|
rec.state = '已取消'
|
||||||
|
|
||||||
|
def finish_scheduling(self):
|
||||||
|
"""
|
||||||
|
完成调度任务
|
||||||
|
"""
|
||||||
|
for rec in self:
|
||||||
|
if rec.state != '配送中':
|
||||||
|
return False
|
||||||
|
_logger.info('AGV任务调度:完成任务%s' % rec)
|
||||||
|
rec.state = '已配送'
|
||||||
|
rec.task_completion_time = fields.Datetime.now()
|
||||||
|
|
||||||
|
def dispatch_scheduling(self, agv_task_route):
|
||||||
|
"""
|
||||||
|
下发调度任务
|
||||||
|
params:
|
||||||
|
agv_route sf.agv.task.route对象
|
||||||
|
"""
|
||||||
|
for rec in self:
|
||||||
|
if rec.state != '待下发':
|
||||||
|
return False
|
||||||
|
_logger.info('AGV任务调度:下发调度任务,路线为%s' % agv_task_route)
|
||||||
|
# rec._delivery_avg()
|
||||||
|
rec.state = '配送中'
|
||||||
|
rec.task_delivery_time = fields.Datetime.now()
|
||||||
|
rec.site_state = '空闲'
|
||||||
|
rec.end_site_id = agv_task_route.end_site_id.id
|
||||||
|
rec.agv_route_name = agv_task_route.name
|
||||||
|
# 更新接驳站状态
|
||||||
|
rec.env['sf.agv.site'].update_site_state({rec.end_site_id.name: '占用'}, False)
|
||||||
|
|
||||||
|
def write(self, vals):
|
||||||
|
if vals.get('state', False):
|
||||||
|
if vals['state'] == '已取消':
|
||||||
|
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({'status': '待下发'})
|
||||||
|
elif vals['state'] == '已配送':
|
||||||
|
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({'status': '已配送'})
|
||||||
|
return super().write(vals)
|
||||||
|
|
||||||
|
|
||||||
|
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', '!=', '已取消')])
|
||||||
@@ -5,50 +5,77 @@ import time
|
|||||||
from odoo import fields, models, api
|
from odoo import fields, models, api
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AgvSetting(models.Model):
|
class AgvSetting(models.Model):
|
||||||
_name = 'sf.agv.site'
|
_name = 'sf.agv.site'
|
||||||
_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)
|
||||||
|
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True,
|
||||||
|
domain=[('is_agv_scheduling', '=', True)])
|
||||||
|
|
||||||
def update_site_state(self):
|
# name必须唯一
|
||||||
# 调取中控的接驳站接口并修改对应站点的状态
|
_sql_constraints = [
|
||||||
config = self.env['res.config.settings'].get_values()
|
('name_uniq', 'unique (name)', '站点编号必须唯一!'),
|
||||||
# token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
|
]
|
||||||
headers = {'Authorization': config['center_control_Authorization']}
|
|
||||||
center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
|
# def update_site_state(self):
|
||||||
timestamp = int(time.time())
|
# # 调取中控的接驳站接口并修改对应站点的状态
|
||||||
center_control_url += str(timestamp)
|
# config = self.env['res.config.settings'].get_values()
|
||||||
logging.info('工件配送-请求中控地址:%s' % center_control_url)
|
# # token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
|
||||||
try:
|
# headers = {'Authorization': config['center_control_Authorization']}
|
||||||
center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
|
# center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
|
||||||
ret = center_control_r.json()
|
# timestamp = int(time.time())
|
||||||
logging.info('工件配送-请求中控站点信息:%s' % ret)
|
# center_control_url += str(timestamp)
|
||||||
self.env['center_control.interface.log'].sudo().create(
|
# logging.info('工件配送-请求中控地址:%s' % center_control_url)
|
||||||
{'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
|
# try:
|
||||||
if ret['Succeed'] is True:
|
# center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
|
||||||
datas = ret['Datas']
|
# ret = center_control_r.json()
|
||||||
for item in self:
|
# logging.info('工件配送-请求中控站点信息:%s' % ret)
|
||||||
for da in datas:
|
# self.env['center_control.interface.log'].sudo().create(
|
||||||
if da['DeviceId'] == item.name:
|
# {'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
|
||||||
if da['AtHome'] is True:
|
# if ret['Succeed'] is True:
|
||||||
item.state = '占用'
|
# datas = ret['Datas']
|
||||||
else:
|
# for item in self:
|
||||||
item.state = '空闲'
|
# for da in datas:
|
||||||
return True
|
# if da['DeviceId'] == item.name:
|
||||||
except requests.exceptions.Timeout:
|
# if da['AtHome'] is True:
|
||||||
logging.error('工件配送-请求中控接口超时')
|
# item.state = '占用'
|
||||||
return False
|
# else:
|
||||||
except requests.exceptions.RequestException as e:
|
# item.state = '空闲'
|
||||||
logging.error('工件配送-请求中控接口错误: %s', e)
|
# return True
|
||||||
return False
|
# except requests.exceptions.Timeout:
|
||||||
|
# logging.error('工件配送-请求中控接口超时')
|
||||||
|
# return False
|
||||||
|
# except requests.exceptions.RequestException as e:
|
||||||
|
# logging.error('工件配送-请求中控接口错误: %s', e)
|
||||||
|
# return False
|
||||||
|
|
||||||
|
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):
|
class AgvTaskRoute(models.Model):
|
||||||
@@ -71,6 +98,17 @@ 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("您选择的终点接驳站与起点接驳站重复,请重新选择")
|
||||||
|
|
||||||
|
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_scheduling', '=', True)],
|
||||||
|
compute="_compute_region")
|
||||||
|
|
||||||
|
@api.depends('end_site_id')
|
||||||
|
def _compute_region(self):
|
||||||
|
for record in self:
|
||||||
|
if record.end_site_id:
|
||||||
|
record.workcenter_id = record.end_site_id.workcenter_id
|
||||||
|
else:
|
||||||
|
record.workcenter_id = None
|
||||||
|
|
||||||
|
|
||||||
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_scheduling = fields.Boolean(string="AGV所属区域", tracking=True)
|
||||||
|
|
||||||
class ResWorkcenterProductivity(models.Model):
|
class ResWorkcenterProductivity(models.Model):
|
||||||
_inherit = 'mrp.workcenter.productivity'
|
_inherit = 'mrp.workcenter.productivity'
|
||||||
|
|||||||
@@ -135,6 +135,9 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
|
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
|
||||||
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
|
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
|
||||||
|
|
||||||
|
# 是否绑定托盘
|
||||||
|
is_trayed = fields.Boolean(string='是否绑定托盘', default=False)
|
||||||
|
|
||||||
@api.depends('name', 'production_id.name')
|
@api.depends('name', 'production_id.name')
|
||||||
def _compute_surface_technics_picking_ids(self):
|
def _compute_surface_technics_picking_ids(self):
|
||||||
for workorder in self:
|
for workorder in self:
|
||||||
@@ -371,10 +374,10 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
vals['leave_id'] = leave.id
|
vals['leave_id'] = leave.id
|
||||||
self.write(vals)
|
self.write(vals)
|
||||||
|
|
||||||
@api.onchange('rfid_code')
|
# @api.onchange('rfid_code')
|
||||||
def _onchange(self):
|
# def _onchange(self):
|
||||||
if self.rfid_code and self.state == 'progress':
|
# if self.rfid_code and self.state == 'progress':
|
||||||
self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
|
# self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
|
||||||
|
|
||||||
def get_plan_workorder(self, production_line):
|
def get_plan_workorder(self, production_line):
|
||||||
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
|
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
|
||||||
@@ -687,25 +690,35 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
k, item),
|
k, item),
|
||||||
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
|
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
|
||||||
item),
|
item),
|
||||||
'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
|
# 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
|
||||||
production)
|
# production)
|
||||||
}]
|
}]
|
||||||
return workorders_values_str
|
return workorders_values_str
|
||||||
|
|
||||||
def _json_workpiece_delivery_list(self, production):
|
def _json_workpiece_delivery_list(self):
|
||||||
up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc')
|
# 修改在装夹工单完成后,生成上产线的工件配送单
|
||||||
down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc')
|
|
||||||
|
# up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc')
|
||||||
|
# down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc')
|
||||||
return [
|
return [
|
||||||
[0, '',
|
[0, '',
|
||||||
{'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '上产线',
|
{
|
||||||
'route_id': up_route.id,
|
'production_id': self.production_id.id,
|
||||||
'feeder_station_start_id': up_route.start_site_id.id,
|
'production_line_id': self.production_id.production_line_id.id,
|
||||||
'feeder_station_destination_id': up_route.end_site_id.id}],
|
'type': '上产线',
|
||||||
[0, '',
|
'is_cnc_program_down': True,
|
||||||
{'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线',
|
'rfid_code': self.rfid_code
|
||||||
'route_id': down_route.id,
|
# 'route_id': up_route.id,
|
||||||
'feeder_station_start_id': down_route.start_site_id.id,
|
# 'feeder_station_start_id': agv_start_site_id,
|
||||||
'feeder_station_destination_id': down_route.end_site_id.id}]]
|
# 'feeder_station_destination_id': up_route.end_site_id.id
|
||||||
|
}
|
||||||
|
],
|
||||||
|
# [0, '',
|
||||||
|
# {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线',
|
||||||
|
# 'route_id': down_route.id,
|
||||||
|
# 'feeder_station_start_id': down_route.start_site_id.id,
|
||||||
|
# 'feeder_station_destination_id': down_route.end_site_id.id}]
|
||||||
|
]
|
||||||
|
|
||||||
# 拼接工单对象属性值(表面工艺)
|
# 拼接工单对象属性值(表面工艺)
|
||||||
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
|
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
|
||||||
@@ -1149,6 +1162,8 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
record.process_state = '待加工'
|
record.process_state = '待加工'
|
||||||
# record.write({'process_state': '待加工'})
|
# record.write({'process_state': '待加工'})
|
||||||
record.production_id.process_state = '待加工'
|
record.production_id.process_state = '待加工'
|
||||||
|
# 生成工件配送单
|
||||||
|
record.workpiece_delivery_ids = record._json_workpiece_delivery_list()
|
||||||
if record.routing_type == 'CNC加工':
|
if record.routing_type == 'CNC加工':
|
||||||
record.process_state = '待解除装夹'
|
record.process_state = '待解除装夹'
|
||||||
# record.write({'process_state': '待加工'})
|
# record.write({'process_state': '待加工'})
|
||||||
@@ -1236,6 +1251,19 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
record.production_id.button_mark_done1()
|
record.production_id.button_mark_done1()
|
||||||
# record.production_id.state = 'done'
|
# record.production_id.state = 'done'
|
||||||
|
|
||||||
|
# 解绑托盘
|
||||||
|
def unbind_tray(self):
|
||||||
|
self.write({
|
||||||
|
'rfid_code': False,
|
||||||
|
'tray_serial_number': False,
|
||||||
|
'tray_product_id': False,
|
||||||
|
'tray_brand_id': False,
|
||||||
|
'tray_type_id': False,
|
||||||
|
'tray_model_id': False,
|
||||||
|
'is_trayed': False
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
# 将FTP的检测报告文件下载到临时目录
|
# 将FTP的检测报告文件下载到临时目录
|
||||||
def download_reportfile_tmp(self, workorder, reportpath):
|
def download_reportfile_tmp(self, workorder, reportpath):
|
||||||
logging.info('reportpath/ftp地址:%s' % reportpath)
|
logging.info('reportpath/ftp地址:%s' % reportpath)
|
||||||
@@ -1275,6 +1303,66 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
else:
|
else:
|
||||||
raise UserError("无关联制造订单或关联序列号,无法打印。请检查!")
|
raise UserError("无关联制造订单或关联序列号,无法打印。请检查!")
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_views(self, views, options=None):
|
||||||
|
res = super().get_views(views, options)
|
||||||
|
if res['views'].get('list', {}) and self.env.context.get('search_default_workcenter_id'):
|
||||||
|
workcenter = self.env['mrp.workcenter'].browse(self.env.context.get('search_default_workcenter_id'))
|
||||||
|
tree_view = res['views']['list']
|
||||||
|
if workcenter.name == '工件拆卸中心':
|
||||||
|
arch = etree.fromstring(tree_view['arch'])
|
||||||
|
# 查找 tree 标签
|
||||||
|
tree_element = arch.xpath("//tree")[0]
|
||||||
|
|
||||||
|
# 查找或创建 header 标签
|
||||||
|
header_element = tree_element.find('header')
|
||||||
|
if header_element is None:
|
||||||
|
header_element = etree.Element('header')
|
||||||
|
tree_element.insert(0, header_element)
|
||||||
|
|
||||||
|
# 创建并添加按钮元素
|
||||||
|
button_element = etree.Element('button', {
|
||||||
|
'name': 'button_delivery',
|
||||||
|
'type': 'object',
|
||||||
|
'string': '解除装夹',
|
||||||
|
'class': 'btn-primary',
|
||||||
|
# 'className': 'btn-primary',
|
||||||
|
'modifiers': '{"force_show": 1}'
|
||||||
|
})
|
||||||
|
header_element.append(button_element)
|
||||||
|
|
||||||
|
# 更新 tree_view 的 arch
|
||||||
|
tree_view['arch'] = etree.tostring(arch, encoding='unicode')
|
||||||
|
return res
|
||||||
|
|
||||||
|
def button_delivery(self):
|
||||||
|
production_ids = []
|
||||||
|
workorder_ids = []
|
||||||
|
delivery_type = '运送空料架'
|
||||||
|
max_num = 4 # 最大配送数量
|
||||||
|
if len(self) > max_num:
|
||||||
|
raise UserError('仅限于拆卸1-4个制造订单,请重新选择')
|
||||||
|
for item in self:
|
||||||
|
if item.state != 'ready':
|
||||||
|
raise UserError('请选择状态为【就绪】的工单进行解除装夹')
|
||||||
|
|
||||||
|
production_ids.append(item.production_id.id)
|
||||||
|
workorder_ids.append(item.id)
|
||||||
|
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_delivery_type': delivery_type,
|
||||||
|
'default_workorder_ids': [(6, 0, workorder_ids)],
|
||||||
|
'default_workcenter_id': self.env.context.get('default_workcenter_id'),
|
||||||
|
'default_confirm_button': '确认解除'
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
class CNCprocessing(models.Model):
|
class CNCprocessing(models.Model):
|
||||||
_name = 'sf.cnc.processing'
|
_name = 'sf.cnc.processing'
|
||||||
@@ -1483,6 +1571,7 @@ class SfWorkOrderBarcodes(models.Model):
|
|||||||
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
|
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
|
||||||
self.process_state = '待检测'
|
self.process_state = '待检测'
|
||||||
self.date_start = datetime.now()
|
self.date_start = datetime.now()
|
||||||
|
self.is_trayed = True
|
||||||
else:
|
else:
|
||||||
raise UserError('没有找到Rfid为【%s】的托盘信息!!!' % barcode)
|
raise UserError('没有找到Rfid为【%s】的托盘信息!!!' % barcode)
|
||||||
# stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)])
|
# stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)])
|
||||||
@@ -1554,20 +1643,25 @@ class WorkPieceDelivery(models.Model):
|
|||||||
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
|
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
|
||||||
task_delivery_time = fields.Datetime('任务下发时间')
|
task_delivery_time = fields.Datetime('任务下发时间')
|
||||||
task_completion_time = fields.Datetime('任务完成时间')
|
task_completion_time = fields.Datetime('任务完成时间')
|
||||||
type = fields.Selection(
|
|
||||||
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
|
def _get_agv_route_type_selection(self):
|
||||||
|
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||||
|
|
||||||
|
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
|
||||||
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
|
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
|
||||||
status = fields.Selection(
|
status = fields.Selection(
|
||||||
[('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
|
[('待下发', '待下发'), ('已下发', '已下发'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
|
||||||
default='待下发',
|
default='待下发', tracking=True)
|
||||||
tracking=True)
|
|
||||||
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
|
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
|
||||||
is_manual_work = fields.Boolean('人工操作', default=False)
|
is_manual_work = fields.Boolean('人工操作', default=False)
|
||||||
active = fields.Boolean(string="有效", default=True)
|
active = fields.Boolean(string="有效", default=True)
|
||||||
|
|
||||||
|
agv_scheduling_id = fields.Many2one('sf.agv.scheduling', 'AGV任务调度')
|
||||||
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
if vals['route_id'] and vals.get('type') is None:
|
if vals.get('route_id') and vals.get('type') is None:
|
||||||
vals['type'] = '运送空料架'
|
vals['type'] = '运送空料架'
|
||||||
else:
|
else:
|
||||||
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
|
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
|
||||||
@@ -1615,84 +1709,45 @@ class WorkPieceDelivery(models.Model):
|
|||||||
def button_delivery(self):
|
def button_delivery(self):
|
||||||
delivery_ids = []
|
delivery_ids = []
|
||||||
production_ids = []
|
production_ids = []
|
||||||
|
workorder_ids = []
|
||||||
is_cnc_down = 0
|
is_cnc_down = 0
|
||||||
is_not_production_line = 0
|
is_not_production_line = 0
|
||||||
is_not_route = 0
|
|
||||||
same_production_line_id = None
|
same_production_line_id = None
|
||||||
same_route_id = None
|
delivery_type = '上产线'
|
||||||
down_status = '待下发'
|
max_num = 4 # 最大配送数量
|
||||||
production_type = None
|
if len(self) > max_num:
|
||||||
num = 0
|
raise UserError('仅限于配送1-4个制造订单,请重新选择')
|
||||||
for item in self:
|
for item in self:
|
||||||
num += 1
|
if item.status != '待下发':
|
||||||
if production_type is None:
|
raise UserError('请选择状态为【待下发】的制造订单进行配送')
|
||||||
production_type = item.type
|
if same_production_line_id is None:
|
||||||
if item.type == "运送空料架":
|
same_production_line_id = item.production_line_id.id
|
||||||
if num >= 2:
|
if item.production_line_id.id != same_production_line_id:
|
||||||
raise UserError('仅选择一条路线进行配送,请重新选择')
|
is_not_production_line += 1
|
||||||
else:
|
if item.is_cnc_program_down is False:
|
||||||
delivery_ids.append(item.id)
|
is_cnc_down += 1
|
||||||
else:
|
if is_cnc_down == 0 and is_not_production_line == 0:
|
||||||
if num > 4:
|
delivery_ids.append(item.id)
|
||||||
raise UserError('仅限于配送1-4个制造订单,请重新选择')
|
production_ids.append(item.production_id.id)
|
||||||
if item.status in ['待配送', '已配送']:
|
workorder_ids.append(item.workorder_id.id)
|
||||||
raise UserError('请选择状态为【待下发】的制造订单进行配送')
|
|
||||||
if item.route_id:
|
|
||||||
if same_route_id is None:
|
|
||||||
same_route_id = item.route_id.id
|
|
||||||
if item.route_id.id != same_route_id:
|
|
||||||
is_not_route += 1
|
|
||||||
# else:
|
|
||||||
# raise UserError('请选择【任务路线】再进行配送')
|
|
||||||
# if item.production_id.production_line_state == '已下产线' and item.state == '待下发' and item.type == '下产线':
|
|
||||||
# raise UserError('该制造订单已下产线,无需配送')
|
|
||||||
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:
|
|
||||||
is_not_production_line += 1
|
|
||||||
if item.is_cnc_program_down is False:
|
|
||||||
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:
|
if is_cnc_down >= 1:
|
||||||
raise UserError('您所选择制造订单的【CNC程序】暂未下发,请在程序下发后再进行配送')
|
raise UserError('您所选择制造订单的【CNC程序】暂未下发,请在程序下发后再进行配送')
|
||||||
if is_not_production_line >= 1:
|
if is_not_production_line >= 1:
|
||||||
raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认')
|
raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认')
|
||||||
if is_not_route >= 1:
|
return {
|
||||||
raise UserError('您所选择制造订单的【任务路线】不一致,请重新确认')
|
'name': _('确认'),
|
||||||
is_free = self._check_avgsite_state()
|
'type': 'ir.actions.act_window',
|
||||||
if is_free is True:
|
'view_mode': 'form',
|
||||||
if delivery_ids:
|
'res_model': 'sf.workpiece.delivery.wizard',
|
||||||
return {
|
'target': 'new',
|
||||||
'name': _('确认'),
|
'context': {
|
||||||
'type': 'ir.actions.act_window',
|
'default_delivery_ids': [(6, 0, delivery_ids)],
|
||||||
'view_mode': 'form',
|
'default_production_ids': [(6, 0, production_ids)],
|
||||||
'res_model': 'sf.workpiece.delivery.wizard',
|
'default_delivery_type': delivery_type,
|
||||||
'target': 'new',
|
'default_workorder_ids': [(6, 0, workorder_ids)],
|
||||||
'context': {
|
'default_confirm_button': '确认配送'
|
||||||
'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:
|
|
||||||
if production_type == '运送空料架':
|
|
||||||
raise UserError("您所选择的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时进行配送")
|
|
||||||
else:
|
|
||||||
raise UserError(
|
|
||||||
"您所选择制造订单的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时或选择其他路线进行配送")
|
|
||||||
|
|
||||||
# 验证agv站点是否可用
|
# 验证agv站点是否可用
|
||||||
def _check_avgsite_state(self):
|
def _check_avgsite_state(self):
|
||||||
@@ -1798,6 +1853,7 @@ class WorkPieceDelivery(models.Model):
|
|||||||
obj.delivery_duration = 0.0
|
obj.delivery_duration = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CMMprogram(models.Model):
|
class CMMprogram(models.Model):
|
||||||
_name = 'sf.cmm.program'
|
_name = 'sf.cmm.program'
|
||||||
_description = "CMM程序"
|
_description = "CMM程序"
|
||||||
|
|||||||
@@ -150,5 +150,12 @@ 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_scheduling_admin,sf_agv_scheduling_admin,model_sf_agv_scheduling,base.group_system,1,1,1,1
|
||||||
|
access_sf_agv_scheduling_group_sf_order_user,sf_agv_scheduling_group_sf_order_user,model_sf_agv_scheduling,sf_base.group_sf_order_user,1,1,1,0
|
||||||
|
access_sf_agv_scheduling_group_sf_mrp_manager,sf_agv_scheduling_group_sf_mrp_manager,model_sf_agv_scheduling,sf_base.group_sf_mrp_manager,1,1,1,0
|
||||||
|
access_sf_agv_scheduling_group_sf_equipment_user,sf_agv_scheduling_group_sf_equipment_user,model_sf_agv_scheduling,sf_base.group_sf_equipment_user,1,1,1,0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
@@ -1,15 +1,17 @@
|
|||||||
var RFID = ''
|
var RFID = ''
|
||||||
$(document).off('keydown')
|
$(document).off('keydown')
|
||||||
console.log(2222)
|
$(document).on('keydown', 'body.o_web_client', function (e) {
|
||||||
$(document).on('keydown', '.modal.d-block.o_technical_modal,body.o_web_client', function (e) {
|
|
||||||
const dom = $('.customRFID')
|
|
||||||
if(!dom.length) return
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
RFID = ''
|
RFID = ''
|
||||||
}, 200)
|
}, 200)
|
||||||
|
|
||||||
if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){
|
if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){
|
||||||
|
console.log(RFID)
|
||||||
if(!RFID || RFID.length <= 3) return;
|
if(!RFID || RFID.length <= 3) return;
|
||||||
dom.children('span').text(RFID)
|
$('[name="button_start"]').trigger('click')
|
||||||
|
setTimeout(() => {
|
||||||
|
$('.o_dialog .modal-footer .btn-primary').trigger('click')
|
||||||
|
}, 50)
|
||||||
RFID = ''
|
RFID = ''
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
|
||||||
|
const core = require('web.core');
|
||||||
|
const ajax = require('web.ajax');
|
||||||
|
const Dialog = require('web.Dialog');
|
||||||
|
var rpc = require('web.rpc');
|
||||||
|
var _t = core._t;
|
||||||
|
|
||||||
|
async function dispatch_confirm(parent, {params}) {
|
||||||
|
console.log(params, 'params')
|
||||||
|
console.log("<div>本次下发的工件数量为:" + params.workorder_count + ",是否确认?</div>", 'content')
|
||||||
|
const dialog = new Dialog(parent, {
|
||||||
|
title: "确认",
|
||||||
|
$content: $('<div>').append("请确认是否仅配送" + params.workorder_count + "个工件?"),
|
||||||
|
buttons: [
|
||||||
|
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
|
||||||
|
{ text: "取消", close: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
dialog.open();
|
||||||
|
|
||||||
|
|
||||||
|
async function dispatchConfirmed(parent, params) {
|
||||||
|
console.log(parent, 'parent')
|
||||||
|
rpc.query({
|
||||||
|
model: 'sf.workpiece.delivery.wizard',
|
||||||
|
method: 'confirm',
|
||||||
|
args: [params.active_id]
|
||||||
|
,
|
||||||
|
kwargs: {
|
||||||
|
context: params.context,
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res, 'res')
|
||||||
|
console.log(res.name, 'res')
|
||||||
|
parent.services.action.doAction({
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': '任务下发成功!AGV任务调度编号为【' + res.name + '】',
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': false,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.action_registry.add('dispatch_confirm', dispatch_confirm);
|
||||||
|
return dispatch_confirm;
|
||||||
|
});
|
||||||
57
sf_manufacturing/static/src/xml/button_show_on_tree.xml
Normal file
57
sf_manufacturing/static/src/xml/button_show_on_tree.xml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
<t t-name="sf_manufacturing.button_show" t-inherit="web.ListView.Buttons" t-inherit-mode="extension" owl="1">
|
||||||
|
<xpath expr="//div/t[@t-if='nbSelected']" position="after">
|
||||||
|
<t t-elif="!nbSelected">
|
||||||
|
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||||
|
<t t-if="button.modifiers.force_show">
|
||||||
|
<ListViewHeaderButton
|
||||||
|
list="model.root"
|
||||||
|
clickParams="button.clickParams"
|
||||||
|
defaultRank="button.defaultRank"
|
||||||
|
domain="props.domain"
|
||||||
|
icon="button.icon"
|
||||||
|
string="button.string"
|
||||||
|
title="button.title"
|
||||||
|
className="button.className+' ms-2'"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div/t[@t-if='nbSelected']" position="replace">
|
||||||
|
<t t-if="nbSelected">
|
||||||
|
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||||
|
<t t-if="!button.modifiers.force_show">
|
||||||
|
<ListViewHeaderButton
|
||||||
|
list="model.root"
|
||||||
|
clickParams="button.clickParams"
|
||||||
|
defaultRank="button.defaultRank"
|
||||||
|
domain="props.domain"
|
||||||
|
icon="button.icon"
|
||||||
|
string="button.string"
|
||||||
|
title="button.title"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
<t t-if="!env.isSmall">
|
||||||
|
<t t-call="web.ListView.Selection"/>
|
||||||
|
</t>
|
||||||
|
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
|
||||||
|
<t t-if="button.modifiers.force_show == 1">
|
||||||
|
<ListViewHeaderButton
|
||||||
|
list="model.root"
|
||||||
|
clickParams="button.clickParams"
|
||||||
|
defaultRank="button.defaultRank"
|
||||||
|
domain="props.domain"
|
||||||
|
icon="button.icon"
|
||||||
|
string="button.string"
|
||||||
|
title="button.title"
|
||||||
|
className="button.className+' ms-2'"
|
||||||
|
/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</xpath>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
79
sf_manufacturing/views/agv_scheduling_views.xml
Normal file
79
sf_manufacturing/views/agv_scheduling_views.xml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- agv站点 -->
|
||||||
|
<record id="view_agv_scheduling_tree" model="ir.ui.view">
|
||||||
|
<field name="name">agv调度</field>
|
||||||
|
<field name="model">sf.agv.scheduling</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="agv_route_type" invisible="1"/>
|
||||||
|
<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"/>
|
||||||
|
<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"/>
|
||||||
|
|
||||||
|
<button
|
||||||
|
name="button_cancel"
|
||||||
|
string="取消" type="object"
|
||||||
|
attrs="{'invisible': ['|', ('state', '!=', '待下发'), ('agv_route_type', '=', '运送空料架')]}"
|
||||||
|
icon="fa-times"
|
||||||
|
class="btn-danger"
|
||||||
|
confirm="你确定要取消这条记录吗?"
|
||||||
|
/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_agv_scheduling_search" model="ir.ui.view">
|
||||||
|
<field name="name">sf.agv.scheduling.search</field>
|
||||||
|
<field name="model">sf.agv.scheduling</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="AGV调度">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="agv_route_name"/>
|
||||||
|
<field name="start_site_id"/>
|
||||||
|
<field name="end_site_id"/>
|
||||||
|
<field name="delivery_workpieces"/>
|
||||||
|
<field name="state" string="状态"/>
|
||||||
|
<filter name="filter_to_be_issued" string="待下发" domain="[('state', 'in', ['待下发'])]"/>
|
||||||
|
<filter name="filter_delivering" string="配送中" domain="[('state', 'in', ['配送中'])]"/>
|
||||||
|
<filter name="filter_delivered" string="已配送" domain="[('state', 'in', ['已配送'])]"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_agv_scheduling_tree" model="ir.actions.act_window">
|
||||||
|
<field name="name">AGV调度</field>
|
||||||
|
<field name="res_model">sf.agv.scheduling</field>
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="context">
|
||||||
|
{
|
||||||
|
"search_default_filter_to_be_issued": 1,
|
||||||
|
"search_default_filter_delivering": 1,
|
||||||
|
}
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="menu_action_agv_scheduling"
|
||||||
|
name="AGV调度"
|
||||||
|
sequence="28"
|
||||||
|
action="action_agv_scheduling_tree"
|
||||||
|
parent="mrp.menu_mrp_manufacturing"
|
||||||
|
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"
|
||||||
|
/>
|
||||||
|
</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="workcenter_id" 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="workcenter_id"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
groups="sf_base.group_sf_mrp_user"/>
|
groups="sf_base.group_sf_mrp_user"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
|
<xpath expr="(//header//button[@name='button_scrap'])" position="replace">
|
||||||
<button name="button_scrap" invisible="0"/>
|
<button name="button_scrap" invisible="1"/>
|
||||||
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
|
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
|
||||||
confirm="是否确认更新程序"
|
confirm="是否确认更新程序"
|
||||||
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
|
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
|
||||||
|
|||||||
@@ -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_scheduling"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -40,10 +40,15 @@
|
|||||||
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
|
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//button[@name='button_start']" position="attributes">
|
<xpath expr="//button[@name='button_start']" position="attributes">
|
||||||
|
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
|
||||||
|
<!-- 'done',-->
|
||||||
|
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
|
||||||
|
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
|
||||||
|
<!-- </attribute>-->
|
||||||
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
|
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
|
||||||
'done',
|
'done',
|
||||||
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
|
||||||
('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}
|
('is_user_working', '!=', False),("user_permissions","=",False),("name","in",("CNC加工","解除装夹"))]}
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
|
||||||
@@ -113,6 +118,9 @@
|
|||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//form" position="inside">
|
||||||
|
<script src="sf_manufacturing/static/src/js/customRFID.js"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//header/field[@name='state']" position="replace">
|
<xpath expr="//header/field[@name='state']" position="replace">
|
||||||
<field name="state" widget="statusbar"
|
<field name="state" widget="statusbar"
|
||||||
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
|
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
|
||||||
@@ -141,6 +149,7 @@
|
|||||||
<field name='name' invisible="1"/>
|
<field name='name' invisible="1"/>
|
||||||
<field name='is_rework' invisible="1"/>
|
<field name='is_rework' invisible="1"/>
|
||||||
<field name='is_delivery' invisible="1"/>
|
<field name='is_delivery' invisible="1"/>
|
||||||
|
<field name="is_trayed" invisible="1"/>
|
||||||
<!-- <field name='is_send_program_again' invisible="1"/>-->
|
<!-- <field name='is_send_program_again' invisible="1"/>-->
|
||||||
<!-- 工单form页面的开始停工按钮等 -->
|
<!-- 工单form页面的开始停工按钮等 -->
|
||||||
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
|
<!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
|
||||||
@@ -153,8 +162,12 @@
|
|||||||
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
|
<!-- 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="是否确认完工"/> -->
|
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
|
||||||
|
|
||||||
|
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
|
||||||
|
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
|
||||||
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
|
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
|
||||||
attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
|
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
|
||||||
|
<button name="button_start" type="object" string="开始" class="btn-success"
|
||||||
|
attrs="{'invisible': ['|', '|', '|', '|', ('routing_type', '!=', '装夹预调'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
|
||||||
<button name="button_pending" type="object" string="暂停" class="btn-warning"
|
<button name="button_pending" type="object" string="暂停" class="btn-warning"
|
||||||
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
|
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
|
||||||
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
|
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
|
||||||
@@ -175,11 +188,14 @@
|
|||||||
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
|
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
|
||||||
<!-- groups="sf_base.group_sf_mrp_user" -->
|
<!-- groups="sf_base.group_sf_mrp_user" -->
|
||||||
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
|
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
|
||||||
<button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"
|
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
|
||||||
attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&',('rfid_code','in',['',False]),('state','=','done')]}"/>
|
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
|
||||||
<button name="button_rework_pre" type="object" string="返工"
|
<button name="button_rework_pre" type="object" string="返工"
|
||||||
class="btn-primary"
|
class="btn-primary"
|
||||||
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
|
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
|
||||||
|
<button name="unbind_tray" type="object" string="解绑托盘"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': ['|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False)]}"/>
|
||||||
<button name="print_method" type="object" string="打印二维码" class="btn-primary"
|
<button name="print_method" type="object" string="打印二维码" class="btn-primary"
|
||||||
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
|
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -642,27 +658,27 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="工件配送" class="center" create="0" delete="0">
|
<tree string="工件配送" class="center" create="0" delete="0">
|
||||||
<header>
|
<header>
|
||||||
<button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
|
<button name="button_delivery" type="object" string="工件配送" class="btn-primary" attrs="{'force_show':1}"/>
|
||||||
</header>
|
</header>
|
||||||
<field name="status" widget="badge"
|
<field name="status" widget="badge"
|
||||||
decoration-success="status == '已配送'"
|
decoration-success="status == '已配送'"
|
||||||
decoration-warning="status == '待下发'"
|
decoration-warning="status == '待下发'"
|
||||||
decoration-danger="status == '待配送'"
|
decoration-danger="status == '已下发'"
|
||||||
decoration-info="status == '已取消'"
|
decoration-info="status == '已取消'"
|
||||||
/>
|
/>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="production_id"/>
|
<field name="production_id"/>
|
||||||
<field name="type" readonly="1"/>
|
<field name="type" readonly="1"/>
|
||||||
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
|
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
|
||||||
<field name="route_id" options="{'no_create': True}"
|
<!-- <field name="route_id" options="{'no_create': True}"-->
|
||||||
domain="[('route_type','in',['上产线','下产线'])]"/>
|
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>-->
|
||||||
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
<field name="feeder_station_start_id" readonly="1" force_save="1"/>
|
||||||
<field name="feeder_station_destination_id" readonly="1" force_save="1"/>
|
<!-- <field name="feeder_station_destination_id" readonly="1" force_save="1"/>-->
|
||||||
<field name="is_cnc_program_down" readonly="1"/>
|
<field name="is_cnc_program_down" readonly="1"/>
|
||||||
<!-- <field name="rfid_code"/>-->
|
<!-- <field name="rfid_code"/>-->
|
||||||
<field name="task_delivery_time" readonly="1"/>
|
<!-- <field name="task_delivery_time" readonly="1"/>-->
|
||||||
<field name="task_completion_time" readonly="1"/>
|
<!-- <field name="task_completion_time" readonly="1"/>-->
|
||||||
<field name="delivery_duration" widget="float_time"/>
|
<!-- <field name="delivery_duration" widget="float_time"/>-->
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
@@ -717,7 +733,7 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="工件配送">
|
<search string="工件配送">
|
||||||
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
|
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
|
||||||
<filter name="filter_waiting_delivery" string="待配送" domain="[('status', 'in', ['待配送'])]"/>
|
<filter name="filter_issued" string="已下发" domain="[('status', 'in', ['已下发'])]"/>
|
||||||
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
|
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
|
||||||
<field name="rfid_code"/>
|
<field name="rfid_code"/>
|
||||||
<field name="production_id"/>
|
<field name="production_id"/>
|
||||||
@@ -741,7 +757,7 @@
|
|||||||
<field name="res_model">sf.workpiece.delivery</field>
|
<field name="res_model">sf.workpiece.delivery</field>
|
||||||
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
|
<field name="search_view_id" ref="sf_workpiece_delivery_search"/>
|
||||||
<field name="context">{'search_default_filter_to_be_issued': 1,
|
<field name="context">{'search_default_filter_to_be_issued': 1,
|
||||||
'search_default_filter_waiting_delivery': 1}
|
'search_default_filter_issued': 1}
|
||||||
</field>
|
</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
<field name="domain">
|
<field name="domain">
|
||||||
@@ -828,5 +844,11 @@
|
|||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
|
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="mrp.menu_mrp_manufacturing"
|
||||||
|
name="Operations"
|
||||||
|
sequence="10"
|
||||||
|
parent="mrp.menu_mrp_root"
|
||||||
|
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
||||||
|
|||||||
@@ -4,31 +4,28 @@
|
|||||||
<field name="name">sf.workpiece.delivery.wizard.form.view</field>
|
<field name="name">sf.workpiece.delivery.wizard.form.view</field>
|
||||||
<field name="model">sf.workpiece.delivery.wizard</field>
|
<field name="model">sf.workpiece.delivery.wizard</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form class="no_auto_focus">
|
||||||
<sheet>
|
<sheet>
|
||||||
<field name="delivery_ids" invisible="True"/>
|
<field name="delivery_ids" invisible="True"/>
|
||||||
<field name="workorder_id" invisible="True"/>
|
<field name="workorder_ids" invisible="True"/>
|
||||||
<field name="type" invisible="True"/>
|
<field name="delivery_type" invisible="True"/>
|
||||||
<group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
|
<field name="confirm_button" invisible="1"/>
|
||||||
|
<field name="_barcode_scanned" widget="barcode_handler"/>
|
||||||
|
<group col="1">
|
||||||
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
|
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
|
||||||
<div class="o_address_format">
|
<field name="delivery_type" readonly="1"/>
|
||||||
<lable for="rfid_code"></lable>
|
<field name="feeder_station_start_id" options="{'no_create': True}" required="1"/>
|
||||||
<field name="rfid_code" class="o_address_zip"/>
|
<field name="workcenter_id" options="{'no_create': True}"/>
|
||||||
<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>
|
||||||
<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>
|
<footer>
|
||||||
<button string="配送" name="confirm" type="object" class="oe_highlight"/>
|
<button string="确认配送" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button" attrs="{'invisible': [('confirm_button', '!=', '确认配送')]}"/>
|
||||||
|
<button string="确认解除" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button" attrs="{'invisible': [('confirm_button', '!=', '确认解除')]}"/>
|
||||||
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||||
|
<script>
|
||||||
|
setTimeout(function(){
|
||||||
|
$('#feeder_station_start_id').blur()
|
||||||
|
}, 200)
|
||||||
|
</script>
|
||||||
</footer>
|
</footer>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,88 +1,200 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
|
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
from odoo.exceptions import UserError, ValidationError
|
from odoo.exceptions import UserError, ValidationError
|
||||||
from datetime import datetime
|
from datetime import datetime, date
|
||||||
from odoo import models, api, fields, _
|
from odoo import models, api, fields
|
||||||
|
|
||||||
|
|
||||||
|
def convert_datetime(obj):
|
||||||
|
if isinstance(obj, (datetime, date)):
|
||||||
|
return obj.isoformat() # 将 datetime 或 date 对象转换为 ISO 8601 字符串格式
|
||||||
|
raise TypeError(f"Type {type(obj)} not serializable")
|
||||||
|
|
||||||
|
|
||||||
class WorkpieceDeliveryWizard(models.TransientModel):
|
class WorkpieceDeliveryWizard(models.TransientModel):
|
||||||
_name = 'sf.workpiece.delivery.wizard'
|
_name = 'sf.workpiece.delivery.wizard'
|
||||||
|
_inherit = ["barcodes.barcode_events_mixin"]
|
||||||
_description = '工件配送'
|
_description = '工件配送'
|
||||||
|
|
||||||
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送')
|
|
||||||
rfid_code = fields.Char('rfid码')
|
rfid_code = fields.Char('rfid码')
|
||||||
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送单')
|
||||||
|
workorder_ids = fields.Many2many('mrp.workorder', string='工单')
|
||||||
production_ids = fields.Many2many('mrp.production', string='制造订单号')
|
production_ids = fields.Many2many('mrp.production', string='制造订单号')
|
||||||
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
|
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
|
||||||
route_id = fields.Many2one('sf.agv.task.route', '任务路线', domain=[('route_type', 'in', ['上产线', '下产线'])])
|
route_id = fields.Many2one('sf.agv.task.route', '任务路线', domain=[('route_type', 'in', ['上产线', '下产线'])])
|
||||||
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
|
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
|
||||||
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
|
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
|
||||||
type = fields.Selection(
|
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True)
|
||||||
[('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
|
confirm_button = fields.Char('按钮名称')
|
||||||
|
|
||||||
|
@api.onchange('delivery_type')
|
||||||
|
def _onchange_type(self):
|
||||||
|
if self.delivery_type:
|
||||||
|
routes = self.env['sf.agv.task.route'].search([('route_type', '=', self.delivery_type)])
|
||||||
|
if self.workcenter_id:
|
||||||
|
routes = routes.filtered(lambda a: a.start_site_id.workcenter_id.id == self.workcenter_id.id)
|
||||||
|
start_site_ids = routes.mapped('start_site_id.id')
|
||||||
|
workcenter_ids = routes.mapped('end_site_id.workcenter_id.id')
|
||||||
|
if workcenter_ids:
|
||||||
|
self.workcenter_id = workcenter_ids[0]
|
||||||
|
return {
|
||||||
|
'domain':
|
||||||
|
{
|
||||||
|
'feeder_station_start_id': [('id', 'in', start_site_ids)],
|
||||||
|
'workcenter_id': [('id', 'in', workcenter_ids)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'domain':
|
||||||
|
{
|
||||||
|
'feeder_station_start_id': [],
|
||||||
|
'workcenter_id': [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_agv_route_type_selection(self):
|
||||||
|
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
|
||||||
|
|
||||||
|
delivery_type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
|
||||||
|
|
||||||
|
def dispatch_confirm(self):
|
||||||
|
if len(self.workorder_ids) < 4:
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'dispatch_confirm',
|
||||||
|
'params': {
|
||||||
|
'workorder_count': len(self.workorder_ids),
|
||||||
|
'active_id': self.id,
|
||||||
|
'context': self.env.context
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
scheduling = self.confirm()
|
||||||
|
# 显示通知
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': '任务下发成功!AGV任务调度编号为【%s】' % scheduling['name'],
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': False,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
}
|
||||||
def confirm(self):
|
def confirm(self):
|
||||||
if self.type != '运送空料架':
|
try:
|
||||||
if not self.route_id:
|
# if self.workorder_id:
|
||||||
raise UserError('请选择路线')
|
# self.workorder_id.workpiece_delivery_ids[0].agv_scheduling_id()
|
||||||
if self.workorder_id:
|
# else:
|
||||||
self.workorder_id.workpiece_delivery_ids[0]._delivery_avg()
|
# is_not_production_line = 0
|
||||||
else:
|
# same_production_line_id = None
|
||||||
is_not_production_line = 0
|
# notsame_production_line_arr = []
|
||||||
same_production_line_id = None
|
# for item in self.production_ids:
|
||||||
notsame_production_line_arr = []
|
# if same_production_line_id is None:
|
||||||
for item in self.production_ids:
|
# same_production_line_id = item.production_line_id.id
|
||||||
if same_production_line_id is None:
|
# if item.production_line_id.id != same_production_line_id:
|
||||||
same_production_line_id = item.production_line_id.id
|
# notsame_production_line_arr.append(item.name)
|
||||||
if item.production_line_id.id != same_production_line_id:
|
# notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
|
||||||
notsame_production_line_arr.append(item.name)
|
# if is_not_production_line >= 1:
|
||||||
notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
|
# raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
|
||||||
if is_not_production_line >= 1:
|
# else:
|
||||||
raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
|
scheduling = self.env['sf.agv.scheduling'].add_scheduling(
|
||||||
else:
|
agv_start_site_name=self.feeder_station_start_id.name,
|
||||||
self.delivery_ids._delivery_avg()
|
agv_route_type=self.delivery_type,
|
||||||
|
workorders=self.workorder_ids,
|
||||||
|
)
|
||||||
|
# 如果关联了工件配送单,则修改状态为已下发
|
||||||
|
if self.delivery_ids:
|
||||||
|
self.delivery_ids.write({
|
||||||
|
'status': '已下发',
|
||||||
|
'agv_scheduling_id': scheduling.id,
|
||||||
|
'feeder_station_start_id': scheduling.start_site_id.id,
|
||||||
|
})
|
||||||
|
|
||||||
def recognize_production(self):
|
# 如果是解除装夹工单,则需要处理工单逻辑
|
||||||
# production_ids = []
|
for item in self.workorder_ids:
|
||||||
# delivery_ids = []
|
if item.routing_type == '解除装夹' and item.state == 'ready':
|
||||||
# aa = self.production_ids.workorder_ids.filtered(
|
item.button_start()
|
||||||
# lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.filtered(
|
item.button_finish()
|
||||||
# lambda c: c.rfid_code == self.rfid_code)
|
|
||||||
# logging.info('aa:%s' % aa)
|
return scheduling.read()[0]
|
||||||
if len(self.production_ids) == 4:
|
except Exception as e:
|
||||||
raise UserError('只能配送四个制造订单')
|
logging.info('%s任务下发失败:%s' % (self.delivery_type, e))
|
||||||
else:
|
raise UserError('%s任务下发失败:%s' % (self.delivery_type, e))
|
||||||
if self.rfid_code:
|
|
||||||
wd = self.env['sf.workpiece.delivery'].search(
|
# def recognize_production(self):
|
||||||
[('type', '=', self.delivery_ids[0].type), ('rfid_code', '=', self.rfid_code),
|
# # production_ids = []
|
||||||
('status', '=', self.delivery_ids[0].status)])
|
# # delivery_ids = []
|
||||||
if wd:
|
# # aa = self.production_ids.workorder_ids.filtered(
|
||||||
if wd.production_line_id.id == self.delivery_ids[0].production_line_id.id:
|
# # lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.filtered(
|
||||||
# production_ids.append(wd.production_id)
|
# # lambda c: c.rfid_code == self.rfid_code)
|
||||||
# delivery_ids.append(wd.id)
|
# # logging.info('aa:%s' % aa)
|
||||||
# 将对象添加到对应的同模型且是多对多类型里
|
# if len(self.production_ids) == 4:
|
||||||
self.production_ids |= wd.production_id
|
# raise UserError('只能配送四个制造订单')
|
||||||
self.delivery_ids |= wd
|
# else:
|
||||||
self.rfid_code = False
|
# if self.rfid_code:
|
||||||
# self.production_ids = [(6, 0, production_ids)]
|
# wd = self.env['sf.workpiece.delivery'].search(
|
||||||
# self.delivery_ids = [(6, 0, delivery_ids)]
|
# [('type', '=', self.delivery_ids[0].type), ('rfid_code', '=', self.rfid_code),
|
||||||
else:
|
# ('status', '=', self.delivery_ids[0].status)])
|
||||||
raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % wd.production_id.name)
|
# if wd:
|
||||||
return {
|
# if wd.production_line_id.id == self.delivery_ids[0].production_line_id.id:
|
||||||
'name': _('确认'),
|
# # production_ids.append(wd.production_id)
|
||||||
'type': 'ir.actions.act_window',
|
# # delivery_ids.append(wd.id)
|
||||||
'view_mode': 'form',
|
# # 将对象添加到对应的同模型且是多对多类型里
|
||||||
'res_model': 'sf.workpiece.delivery.wizard',
|
# self.production_ids |= wd.production_id
|
||||||
'target': 'new',
|
# self.delivery_ids |= wd
|
||||||
'context': {
|
# self.rfid_code = False
|
||||||
'default_delivery_ids': [(6, 0, self.delivery_ids.ids)],
|
# # self.production_ids = [(6, 0, production_ids)]
|
||||||
'default_production_ids': [(6, 0, self.production_ids.ids)],
|
# # self.delivery_ids = [(6, 0, delivery_ids)]
|
||||||
'default_route_id': self.delivery_ids[0].route_id.id,
|
# else:
|
||||||
'default_type': self.delivery_ids[0].type
|
# 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')
|
@api.onchange('route_id')
|
||||||
def onchange_route(self):
|
def onchange_route(self):
|
||||||
if self.route_id:
|
if self.route_id:
|
||||||
self.feeder_station_start_id = self.route_id.start_site_id.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
|
self.feeder_station_destination_id = self.route_id.end_site_id.id
|
||||||
|
|
||||||
|
def on_barcode_scanned(self, barcode):
|
||||||
|
delivery_type = self.env.context.get('default_delivery_type')
|
||||||
|
if delivery_type == '上产线':
|
||||||
|
workorder = self.env['mrp.workorder'].search(
|
||||||
|
[('production_line_state', '=', '待上产线'), ('rfid_code', '=', barcode),
|
||||||
|
('state', '=', 'done')])
|
||||||
|
# 找到对应的配送单
|
||||||
|
delivery = self.env['sf.workpiece.delivery'].search(
|
||||||
|
[('type', '=', '上产线'), ('rfid_code', '=', barcode),
|
||||||
|
('status', '=', '待下发')])
|
||||||
|
if delivery:
|
||||||
|
self.delivery_ids |= delivery
|
||||||
|
elif delivery_type == '运送空料架':
|
||||||
|
workorder = self.env['mrp.workorder'].search(
|
||||||
|
[('routing_type', '=', '解除装夹'), ('rfid_code', '=', barcode),
|
||||||
|
('state', '=', 'ready')])
|
||||||
|
if workorder:
|
||||||
|
if (len(self.production_ids) > 0 and
|
||||||
|
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
|
||||||
|
raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % workorder.production_id.name)
|
||||||
|
|
||||||
|
# 将对象添加到对应的同模型且是多对多类型里
|
||||||
|
self.production_ids |= workorder.production_id
|
||||||
|
self.workorder_ids |= workorder
|
||||||
|
else:
|
||||||
|
raise UserError('该rfid码对应的工单不存在')
|
||||||
|
return
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -337,6 +337,7 @@
|
|||||||
name="空料架配送"
|
name="空料架配送"
|
||||||
sequence="11"
|
sequence="11"
|
||||||
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
|
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
|
||||||
|
groups="base.group_system"
|
||||||
parent="mrp.menu_mrp_manufacturing"
|
parent="mrp.menu_mrp_manufacturing"
|
||||||
/>
|
/>
|
||||||
<!-- <menuitem -->
|
<!-- <menuitem -->
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
'views/sale_order_view.xml',
|
'views/sale_order_view.xml',
|
||||||
'views/res_partner_view.xml',
|
'views/res_partner_view.xml',
|
||||||
'views/purchase_order_view.xml',
|
'views/purchase_order_view.xml',
|
||||||
'views/quick_easy_order_view.xml'
|
'views/quick_easy_order_view.xml',
|
||||||
|
'views/purchase_menu.xml'
|
||||||
],
|
],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ READONLY_FIELD_STATES = {
|
|||||||
class ReSaleOrder(models.Model):
|
class ReSaleOrder(models.Model):
|
||||||
_inherit = 'sale.order'
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
|
mrp_production_count = fields.Integer(
|
||||||
|
"Count of MO generated",
|
||||||
|
compute='_compute_mrp_production_ids',
|
||||||
|
groups='mrp.group_mrp_user,sf_base.group_sale_salemanager,sf_base.group_sale_director')
|
||||||
|
|
||||||
logistics_way = fields.Selection([('自提', '自提'), ('到付', '到付'), ('在线支付', '在线支付')], string='物流方式')
|
logistics_way = fields.Selection([('自提', '自提'), ('到付', '到付'), ('在线支付', '在线支付')], string='物流方式')
|
||||||
state = fields.Selection(
|
state = fields.Selection(
|
||||||
selection=[
|
selection=[
|
||||||
|
|||||||
@@ -96,5 +96,28 @@ access_product_supplierinfo_group_plan_director,product.supplierinfo user,produc
|
|||||||
access_product_category_group_plan_director,product.category user,product.model_product_category,sf_base.group_plan_director,1,1,1,0
|
access_product_category_group_plan_director,product.category user,product.model_product_category,sf_base.group_plan_director,1,1,1,0
|
||||||
|
|
||||||
|
|
||||||
|
access_purchase_report_sf_base_group_purchase,purchase_report_sf_base_group_purchase,purchase.model_purchase_report,sf_base.group_purchase,1,0,0,0
|
||||||
|
access_purchase_report_sf_base_group_purchase_director,purchase_report_sf_base_group_purchase_director,purchase.model_purchase_report,sf_base.group_purchase_director,1,0,0,0
|
||||||
|
access_sale_order_sf_base_group_purchase,sale_order_sf_base_group_purchase,model_sale_order,sf_base.group_purchase,1,0,0,0
|
||||||
|
access_sale_order_sf_base_group_purchase_director,sale_order_sf_base_group_purchase_director,model_sale_order,sf_base.group_purchase_director,1,0,0,0
|
||||||
|
|
||||||
|
|
||||||
|
access_quality_check_group_sale_salemanager,quality_check_group_sale_salemanager,quality.model_quality_check,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_quality_check_group_sale_director,quality_check_group_sale_director,quality.model_quality_check,sf_base.group_sale_director,1,0,0,0
|
||||||
|
access_stock_picking_group_sale_salemanager,stock_picking_group_sale_salemanager,stock.model_stock_picking,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_stock_picking_group_sale_director,stock_picking_group_sale_director,stock.model_stock_picking,sf_base.group_sale_director,1,0,0,0
|
||||||
|
access_mrp_workorder_group_sale_salemanager,mrp_workorder_group_sale_salemanager,mrp.model_mrp_workorder,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_mrp_workorder_group_sale_director,mrp_workorder_group_sale_director,mrp.model_mrp_workorder,sf_base.group_sale_director,1,0,0,0
|
||||||
|
access_mrp_unbuild_group_sale_salemanager,mrp_unbuild_group_sale_salemanager,mrp.model_mrp_unbuild,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_mrp_unbuild_group_sale_director,mrp_unbuild_group_sale_director,mrp.model_mrp_unbuild,sf_base.group_sale_director,1,0,0,0
|
||||||
|
access_mrp_workcenter_productivity_group_sale_salemanager,mrp_workcenter_productivity_group_sale_salemanager,mrp.model_mrp_workcenter_productivity,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_mrp_workcenter_productivity_group_sale_director,mrp_workcenter_productivity_group_sale_director,mrp.model_mrp_workcenter_productivity,sf_base.group_sale_director,1,0,0,0
|
||||||
|
access_sf_detection_result_group_sale_salemanager,sf_detection_result_group_sale_salemanager,sf_manufacturing.model_sf_detection_result,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_sf_detection_result_group_sale_director,sf_detection_result_group_sale_director,sf_manufacturing.model_sf_detection_result,sf_base.group_sale_director,1,0,0,0
|
||||||
|
access_stock_scrap_group_sale_salemanager,stock_scrap_group_sale_salemanager,stock.model_stock_scrap,sf_base.group_sale_salemanager,1,0,0,0
|
||||||
|
access_stock_scrap_group_sale_director,stock_scrap_group_sale_director,stock.model_stock_scrap,sf_base.group_sale_director,1,0,0,0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
23
sf_sale/views/purchase_menu.xml
Normal file
23
sf_sale/views/purchase_menu.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- 采购-产品 -->
|
||||||
|
<menuitem id="purchase.menu_purchase_products" name="Products" parent="purchase.menu_purchase_root"
|
||||||
|
groups="sf_base.group_purchase_director,sf_base.group_purchase"
|
||||||
|
sequence="5"/>
|
||||||
|
<!-- 采购-产品-产品 -->
|
||||||
|
<menuitem id="purchase.menu_procurement_partner_contact_form" name="Products"
|
||||||
|
action="purchase.product_normal_action_puchased" parent="purchase.menu_purchase_products"
|
||||||
|
groups="sf_base.group_purchase_director,sf_base.group_purchase"
|
||||||
|
sequence="20"/>
|
||||||
|
|
||||||
|
<!-- 采购-报表 -->
|
||||||
|
<menuitem id="purchase.purchase_report_main" name="Reporting" parent="purchase.menu_purchase_root" sequence="99"
|
||||||
|
groups="purchase.group_purchase_manager,sf_base.group_purchase_director,sf_base.group_purchase"/>
|
||||||
|
<!-- 采购-报表-采购 -->
|
||||||
|
<menuitem id="purchase.purchase_report" name="Purchase" parent="purchase.purchase_report_main" sequence="99"
|
||||||
|
groups="purchase.group_purchase_manager,sf_base.group_purchase_director,sf_base.group_purchase"
|
||||||
|
action="purchase.action_purchase_order_report_all"/>
|
||||||
|
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -86,6 +86,18 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//form/sheet/div[@name='button_box']/button[@name='action_view_picking']"
|
||||||
|
position="replace">
|
||||||
|
<button type="object"
|
||||||
|
name="action_view_picking"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-truck" attrs="{'invisible':[('incoming_picking_count','=', 0)]}"
|
||||||
|
groups="stock.group_stock_user,sf_base.group_purchase,sf_base.group_purchase_director">
|
||||||
|
<field name="incoming_picking_count" widget="statinfo" string="收货"
|
||||||
|
help="Incoming Shipments"/>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//field[@name='order_line']" position="attributes">
|
<xpath expr="//field[@name='order_line']" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
|
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
|
||||||
</attribute>
|
</attribute>
|
||||||
|
|||||||
@@ -6,6 +6,13 @@
|
|||||||
<field name="model">sale.order</field>
|
<field name="model">sale.order</field>
|
||||||
<field name="inherit_id" ref="sale.view_order_form"/>
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//button[@name='action_view_delivery']" position="attributes">
|
||||||
|
<attribute name="groups">sf_base.group_sale_salemanager,sf_base.group_sale_director</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//button[@name='action_view_mrp_production']" position="attributes">
|
||||||
|
<attribute name="groups">mrp.group_mrp_user,sf_base.group_sale_salemanager,sf_base.group_sale_director
|
||||||
|
</attribute>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//field[@name='user_id']" position="replace">
|
<xpath expr="//field[@name='user_id']" position="replace">
|
||||||
<field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"/>
|
<field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -35,12 +42,12 @@
|
|||||||
<xpath expr="//form/header/button[@name='action_confirm'][2]" position="replace">
|
<xpath expr="//form/header/button[@name='action_confirm'][2]" position="replace">
|
||||||
<button name="action_confirm" data-hotkey="v"
|
<button name="action_confirm" data-hotkey="v"
|
||||||
string="确认接单" type="object" context="{'validate_analytic': True}"
|
string="确认接单" type="object" context="{'validate_analytic': True}"
|
||||||
attrs="{'invisible': ['|','&',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&','&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]}"/>
|
attrs="{'invisible': ['|','&','|',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&','&',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False),('state', 'in', ['cancel'])]}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//form/header/button[@name='action_cancel']" position="attributes">
|
<xpath expr="//form/header/button[@name='action_cancel']" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': ['|','&',('check_status', '!=', 'approved'),('state',
|
<attribute name="attrs">{'invisible': ['|','&','|', ('check_status', '!=', 'approved'),('state',
|
||||||
'in', ['draft','cancel']),'&','&',('check_status', '=', 'approved'),('state', 'in',
|
'in', ['draft','cancel']),'&','&',('check_status', '=', 'approved'),('state', 'in',
|
||||||
['sale','cancel']),('delivery_status', '!=', False)]}
|
['sale','cancel']),('delivery_status', '!=', False), ('state', 'in', ['cancel'])]}
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//form/header/button[@name='action_draft']" position="attributes">
|
<xpath expr="//form/header/button[@name='action_draft']" position="attributes">
|
||||||
|
|||||||
@@ -94,47 +94,47 @@ class MachineTableToolChangingApply(models.Model):
|
|||||||
if len(records) > 1:
|
if len(records) > 1:
|
||||||
raise ValidationError('该刀位号已存在,请重新选择!!!')
|
raise ValidationError('该刀位号已存在,请重新选择!!!')
|
||||||
|
|
||||||
@api.constrains('functional_tool_status')
|
# @api.constrains('functional_tool_status')
|
||||||
def automation_apply_for_tool_change(self):
|
# def automation_apply_for_tool_change(self):
|
||||||
"""
|
# """
|
||||||
自动申请换刀
|
# 自动申请换刀
|
||||||
:return:
|
# :return:
|
||||||
"""
|
# """
|
||||||
# 更新数据到机台换刀申请界面
|
# # 更新数据到机台换刀申请界面
|
||||||
if self.functional_tool_status == '报警' and not self.sf_functional_tool_assembly_id:
|
# if self.functional_tool_status == '报警' and not self.sf_functional_tool_assembly_id:
|
||||||
machine_table_tool_changing_apply = self.env['sf.machine.table.tool.changing.apply'].search(
|
# machine_table_tool_changing_apply = self.env['sf.machine.table.tool.changing.apply'].search(
|
||||||
[('maintenance_equipment_id', '=', self.maintenance_equipment_id.id),
|
# [('maintenance_equipment_id', '=', self.maintenance_equipment_id.id),
|
||||||
('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id)
|
# ('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id)
|
||||||
])
|
# ])
|
||||||
|
#
|
||||||
# 创建功能刀具预警记录
|
# # 创建功能刀具预警记录
|
||||||
self.env['sf.functional.tool.warning'].create_tool_warning_record({'tool_changing_apply_id': self})
|
# self.env['sf.functional.tool.warning'].create_tool_warning_record({'tool_changing_apply_id': self})
|
||||||
|
#
|
||||||
# 新建组装任务
|
# # 新建组装任务
|
||||||
sf_functional_tool_assembly = self.env['sf.functional.tool.assembly'].create({
|
# sf_functional_tool_assembly = self.env['sf.functional.tool.assembly'].create({
|
||||||
'functional_tool_name': self.functional_tool_name,
|
# 'functional_tool_name': self.functional_tool_name,
|
||||||
'functional_tool_type_id': self.functional_tool_type_id.id,
|
# 'functional_tool_type_id': self.functional_tool_type_id.id,
|
||||||
'functional_tool_diameter': self.diameter,
|
# 'functional_tool_diameter': self.diameter,
|
||||||
'knife_tip_r_angle': self.knife_tip_r_angle,
|
# 'knife_tip_r_angle': self.knife_tip_r_angle,
|
||||||
'coarse_middle_thin': '3',
|
# 'coarse_middle_thin': '3',
|
||||||
'new_former': '0',
|
# 'new_former': '0',
|
||||||
'functional_tool_length': self.extension_length,
|
# 'functional_tool_length': self.extension_length,
|
||||||
'effective_length': self.effective_length,
|
# 'effective_length': self.effective_length,
|
||||||
'loading_task_source': '1',
|
# 'loading_task_source': '1',
|
||||||
'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
|
# 'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
|
||||||
'production_line_name_id': self.production_line_id.id,
|
# 'production_line_name_id': self.production_line_id.id,
|
||||||
'machine_tool_name_id': self.maintenance_equipment_id.id,
|
# 'machine_tool_name_id': self.maintenance_equipment_id.id,
|
||||||
'applicant': '系统自动',
|
# 'applicant': '系统自动',
|
||||||
'apply_time': fields.Datetime.now(),
|
# 'apply_time': fields.Datetime.now(),
|
||||||
'cutter_spacing_code_id': self.cutter_spacing_code_id.id,
|
# 'cutter_spacing_code_id': self.cutter_spacing_code_id.id,
|
||||||
'whether_standard_knife': self.whether_standard_knife,
|
# 'whether_standard_knife': self.whether_standard_knife,
|
||||||
'reason_for_applying': '机台报警自动换刀',
|
# 'reason_for_applying': '机台报警自动换刀',
|
||||||
'sf_machine_table_tool_changing_apply_id': self.id
|
# 'sf_machine_table_tool_changing_apply_id': self.id
|
||||||
})
|
# })
|
||||||
|
#
|
||||||
machine_table_tool_changing_apply.write(
|
# machine_table_tool_changing_apply.write(
|
||||||
{'status': '1',
|
# {'status': '1',
|
||||||
'sf_functional_tool_assembly_id': sf_functional_tool_assembly.id})
|
# 'sf_functional_tool_assembly_id': sf_functional_tool_assembly.id})
|
||||||
|
|
||||||
def revocation_1(self):
|
def revocation_1(self):
|
||||||
"""
|
"""
|
||||||
@@ -760,6 +760,15 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
|
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
|
||||||
domain=[('functional_tool_status', '!=', '已拆除'),
|
domain=[('functional_tool_status', '!=', '已拆除'),
|
||||||
('current_location', '=', '刀具房')])
|
('current_location', '=', '刀具房')])
|
||||||
|
|
||||||
|
@api.onchange('functional_tool_id')
|
||||||
|
def _onchange_functional_tool_id(self):
|
||||||
|
for item in self:
|
||||||
|
if item:
|
||||||
|
dismantle_id = self.search([('functional_tool_id', '=', item.functional_tool_id.id)])
|
||||||
|
if dismantle_id:
|
||||||
|
raise ValidationError(f'Rfid为【{item.rfid}】的功能刀具已经存在拆解单,单号是【{dismantle_id[0].code}】')
|
||||||
|
|
||||||
tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
|
tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
|
||||||
compute='_compute_functional_tool_num')
|
compute='_compute_functional_tool_num')
|
||||||
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_functional_tool_num', store=True)
|
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_functional_tool_num', store=True)
|
||||||
@@ -938,15 +947,23 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
if self.chuck_freight_id == self.pad_freight_id:
|
if self.chuck_freight_id == self.pad_freight_id:
|
||||||
raise ValidationError('【夹头】和【刀盘】的目标货位重复,请重新选择!')
|
raise ValidationError('【夹头】和【刀盘】的目标货位重复,请重新选择!')
|
||||||
|
|
||||||
|
def tool_scrap(self):
|
||||||
|
self.scrap_boolean = True
|
||||||
|
|
||||||
|
def tool_no_scrap(self):
|
||||||
|
self.scrap_boolean = False
|
||||||
|
|
||||||
def confirmation_disassembly(self):
|
def confirmation_disassembly(self):
|
||||||
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
|
logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
|
||||||
code = self.code
|
code = self.code
|
||||||
if self.functional_tool_id.functional_tool_status == '已拆除':
|
if self.functional_tool_id.functional_tool_status == '已拆除':
|
||||||
raise ValidationError('Rfid为【%s】的功能刀具已经拆解,请勿重复操作!' % self.functional_tool_id.rfid_dismantle)
|
raise ValidationError('Rfid为【%s】名称为【%s】的功能刀具已经拆解,请勿重复操作!' % (
|
||||||
|
self.functional_tool_id.rfid_dismantle, self.name))
|
||||||
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
|
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
|
||||||
if self.functional_tool_id.tool_room_num == 0:
|
elif self.functional_tool_id.functional_tool_status != '报警':
|
||||||
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
|
if self.functional_tool_id.tool_room_num == 0:
|
||||||
self.rfid, self.functional_tool_id.current_location))
|
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
|
||||||
|
self.rfid, self.functional_tool_id.current_location))
|
||||||
# 目标重复校验
|
# 目标重复校验
|
||||||
self.location_duplicate_check()
|
self.location_duplicate_check()
|
||||||
datas = {'scrap': [], 'picking': []}
|
datas = {'scrap': [], 'picking': []}
|
||||||
@@ -1005,6 +1022,14 @@ class FunctionalToolDismantle(models.Model):
|
|||||||
'rfid': '%s(已拆解)' % self.rfid,
|
'rfid': '%s(已拆解)' % self.rfid,
|
||||||
'state': '已拆解'
|
'state': '已拆解'
|
||||||
})
|
})
|
||||||
|
# ==================修改刀具预警信息的值============
|
||||||
|
warning_id = self.env['sf.functional.tool.warning'].sudo().search(
|
||||||
|
[('functional_tool_id', '=', self.functional_tool_id.id)])
|
||||||
|
if warning_id:
|
||||||
|
warning_id.sudo().write({
|
||||||
|
'dispose_user': self.env.user.name,
|
||||||
|
'dispose_time': fields.Datetime.now()
|
||||||
|
})
|
||||||
logging.info('【%s】刀具拆解成功!' % self.name)
|
logging.info('【%s】刀具拆解成功!' % self.name)
|
||||||
|
|
||||||
def create_tool_picking_scrap(self, datas):
|
def create_tool_picking_scrap(self, datas):
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from odoo.exceptions import ValidationError
|
|||||||
class FunctionalCuttingToolEntity(models.Model):
|
class FunctionalCuttingToolEntity(models.Model):
|
||||||
_name = 'sf.functional.cutting.tool.entity'
|
_name = 'sf.functional.cutting.tool.entity'
|
||||||
_description = '功能刀具列表'
|
_description = '功能刀具列表'
|
||||||
|
_order = 'functional_tool_status'
|
||||||
|
|
||||||
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True)
|
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True)
|
||||||
|
|
||||||
@@ -53,6 +54,22 @@ class FunctionalCuttingToolEntity(models.Model):
|
|||||||
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
|
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
|
||||||
string='功能刀具安全库存', readonly=True)
|
string='功能刀具安全库存', readonly=True)
|
||||||
|
|
||||||
|
@api.onchange('functional_tool_status')
|
||||||
|
def _onchange_functional_tool_status(self):
|
||||||
|
for item in self:
|
||||||
|
if item:
|
||||||
|
if item.functional_tool_status == '报警':
|
||||||
|
# 创建报警刀具拆解单
|
||||||
|
self.env['sf.functional.tool.dismantle'].sudo().create({
|
||||||
|
'functional_tool_id': item.ids[0],
|
||||||
|
'dismantle_cause': '寿命到期报废'
|
||||||
|
})
|
||||||
|
# 创建刀具报警记录
|
||||||
|
self.env['sf.functional.tool.warning'].sudo().create({
|
||||||
|
'rfid': item.rfid,
|
||||||
|
'functional_tool_id': item.ids[0]
|
||||||
|
})
|
||||||
|
|
||||||
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
|
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
|
||||||
'current_shelf_location_id')
|
'current_shelf_location_id')
|
||||||
def _compute_current_location_id(self):
|
def _compute_current_location_id(self):
|
||||||
@@ -101,27 +118,28 @@ class FunctionalCuttingToolEntity(models.Model):
|
|||||||
def tool_in_out_stock_location(self, location_id):
|
def tool_in_out_stock_location(self, location_id):
|
||||||
tool_room_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
tool_room_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
||||||
pre_manufacturing_id = self.env['stock.location'].search([('name', '=', '制造前')])
|
pre_manufacturing_id = self.env['stock.location'].search([('name', '=', '制造前')])
|
||||||
for item in self:
|
if self:
|
||||||
# 中控反馈该位置有刀
|
for item in self:
|
||||||
if item:
|
# 中控反馈该位置有刀
|
||||||
# 系统该位置有刀
|
if item:
|
||||||
if location_id.product_sn_id:
|
# 系统该位置有刀
|
||||||
# 中控反馈和系统中,该位置是同一把刀
|
if location_id.product_sn_id:
|
||||||
if item.barcode_id == location_id.product_sn_id:
|
# 中控反馈和系统中,该位置是同一把刀
|
||||||
return True
|
if item.barcode_id == location_id.product_sn_id:
|
||||||
# 中控反馈和系统中,该位置不是同一把刀
|
return True
|
||||||
else:
|
# 中控反馈和系统中,该位置不是同一把刀
|
||||||
# 原刀从线边出库
|
else:
|
||||||
item.tool_in_out_stock_location_1(location_id, tool_room_id)
|
# 原刀从线边出库
|
||||||
# 新刀入库到线边
|
item.tool_in_out_stock_location_1(location_id, tool_room_id)
|
||||||
item.create_stock_move(pre_manufacturing_id, location_id)
|
# 新刀入库到线边
|
||||||
item.current_shelf_location_id = location_id.id
|
item.create_stock_move(pre_manufacturing_id, location_id)
|
||||||
|
item.current_shelf_location_id = location_id.id
|
||||||
|
|
||||||
# 中控反馈该位置没有刀
|
# 中控反馈该位置没有刀
|
||||||
else:
|
else:
|
||||||
# 系统该位置有刀
|
# 系统该位置有刀
|
||||||
if location_id.product_sn_id:
|
if location_id.product_sn_id:
|
||||||
item.tool_in_out_stock_location_1(location_id, tool_room_id)
|
self.tool_in_out_stock_location_1(location_id, tool_room_id)
|
||||||
|
|
||||||
def tool_in_out_stock_location_1(self, location_id, tool_room_id):
|
def tool_in_out_stock_location_1(self, location_id, tool_room_id):
|
||||||
tool = self.env['sf.functional.cutting.tool.entity'].search(
|
tool = self.env['sf.functional.cutting.tool.entity'].search(
|
||||||
@@ -238,10 +256,39 @@ class FunctionalCuttingToolEntity(models.Model):
|
|||||||
functional_tool_model_ids.append(functional_tool_model.id)
|
functional_tool_model_ids.append(functional_tool_model.id)
|
||||||
return [(6, 0, functional_tool_model_ids)]
|
return [(6, 0, functional_tool_model_ids)]
|
||||||
|
|
||||||
|
dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', store=True)
|
||||||
|
dismantle_ids = fields.One2many('sf.functional.tool.dismantle', 'functional_tool_id', '拆解单')
|
||||||
|
|
||||||
|
@api.depends('dismantle_ids')
|
||||||
|
def _compute_dismantle_num(self):
|
||||||
|
for item in self:
|
||||||
|
if item:
|
||||||
|
item.dismantle_num = len(item.dismantle_ids)
|
||||||
|
|
||||||
|
def open_functional_tool_dismantle_form(self):
|
||||||
|
self.ensure_one()
|
||||||
|
dismantle_ids = self.env['sf.functional.tool.dismantle'].sudo().search([('functional_tool_id', '=', self.id)])
|
||||||
|
action = {
|
||||||
|
'res_model': 'sf.functional.tool.dismantle',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'name': '拆解单',
|
||||||
|
}
|
||||||
|
if len(dismantle_ids) == 1:
|
||||||
|
action.update({
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_id': dismantle_ids[0].id,
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
action.update({
|
||||||
|
'domain': [('id', 'in', dismantle_ids.ids)],
|
||||||
|
'view_mode': 'tree,form',
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
def open_functional_tool_warning(self):
|
def open_functional_tool_warning(self):
|
||||||
action = self.env.ref('sf_tool_management.action_sf_functional_tool_warning')
|
action = self.env.ref('sf_tool_management.action_sf_functional_tool_warning')
|
||||||
result = action.read()[0]
|
result = action.read()[0]
|
||||||
result['domain'] = [('functional_tool_name_id', '=', self.functional_tool_name_id.id)]
|
result['domain'] = [('functional_tool_id', '=', self.id)]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def open_stock_move_line(self):
|
def open_stock_move_line(self):
|
||||||
@@ -323,10 +370,10 @@ class FunctionalToolWarning(models.Model):
|
|||||||
_name = 'sf.functional.tool.warning'
|
_name = 'sf.functional.tool.warning'
|
||||||
_description = '功能刀具预警'
|
_description = '功能刀具预警'
|
||||||
|
|
||||||
code = fields.Char('编码', related='functional_tool_name_id.code')
|
code = fields.Char('编码', related='functional_tool_id.code')
|
||||||
rfid = fields.Char('Rfid', related='functional_tool_name_id.rfid')
|
rfid = fields.Char('Rfid', readonly=True)
|
||||||
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id')
|
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_id.tool_groups_id')
|
||||||
name = fields.Char('名称', invisible=True, readonly=True, related='functional_tool_name_id.name')
|
name = fields.Char('名称', invisible=True, readonly=True, related='functional_tool_id.name')
|
||||||
# 机床信息
|
# 机床信息
|
||||||
production_line_id = fields.Many2one('sf.production.line', string='生产线',
|
production_line_id = fields.Many2one('sf.production.line', string='生产线',
|
||||||
group_expand='_read_group_machine_table_name_ids')
|
group_expand='_read_group_machine_table_name_ids')
|
||||||
@@ -337,52 +384,77 @@ class FunctionalToolWarning(models.Model):
|
|||||||
cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号',
|
cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号',
|
||||||
domain="[('equipment_id', '=', maintenance_equipment_id)]")
|
domain="[('equipment_id', '=', maintenance_equipment_id)]")
|
||||||
# 功能刀具信息
|
# 功能刀具信息
|
||||||
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
|
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', string='功能刀具', readonly=True)
|
||||||
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', related='functional_tool_name_id.barcode_id')
|
barcode_id = fields.Many2one('stock.lot', string='序列号', related='functional_tool_id.barcode_id')
|
||||||
mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型')
|
mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
|
||||||
diameter = fields.Float(string='刀具直径(mm)')
|
related='functional_tool_id.sf_cutting_tool_type_id')
|
||||||
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)')
|
diameter = fields.Float(string='刀具直径(mm)', related='functional_tool_id.functional_tool_diameter')
|
||||||
|
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', related='functional_tool_id.knife_tip_r_angle')
|
||||||
# 其他信息
|
# 其他信息
|
||||||
install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time')
|
install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time')
|
||||||
on_board_time = fields.Datetime('上机装刀时间')
|
on_board_time = fields.Datetime('上机装刀时间')
|
||||||
max_lifetime_value = fields.Integer(string='最大寿命值(min)')
|
max_lifetime_value = fields.Integer(string='最大寿命值(min)', related='functional_tool_id.max_lifetime_value')
|
||||||
alarm_value = fields.Integer(string='报警值(min)')
|
alarm_value = fields.Integer(string='报警值(min)', related='functional_tool_id.alarm_value')
|
||||||
used_value = fields.Integer(string='已使用值(min)')
|
used_value = fields.Integer(string='已使用值(min)', related='functional_tool_id.used_value')
|
||||||
functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], string='状态')
|
functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], string='状态')
|
||||||
alarm_time = fields.Datetime('报警时间')
|
alarm_time = fields.Datetime('报警时间', default=lambda self: fields.Datetime.now(), readonly=True)
|
||||||
dispose_user = fields.Char('处理人')
|
dispose_user = fields.Char('处理人', readonly=True)
|
||||||
dispose_time = fields.Char('处理时间')
|
dispose_time = fields.Char('处理时间', readonly=True)
|
||||||
dispose_func = fields.Char('处理方法/措施', readonly=False)
|
dispose_func = fields.Char('处理方法/措施', readonly=True)
|
||||||
|
|
||||||
active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True)
|
||||||
|
|
||||||
|
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _read_group_machine_table_name_ids(self, categories, domain, order):
|
def _read_group_machine_table_name_ids(self, categories, domain, order):
|
||||||
machine_table_name_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)
|
machine_table_name_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)
|
||||||
return categories.browse(machine_table_name_ids)
|
return categories.browse(machine_table_name_ids)
|
||||||
|
|
||||||
def create_tool_warning_record(self, obj):
|
def action_open_dismantle(self):
|
||||||
"""
|
self.ensure_one()
|
||||||
机台换刀申请报警状态时,创建功能刀具预警记录
|
dismantle_ids = self.env['sf.functional.tool.dismantle'].sudo().search(
|
||||||
"""
|
[('functional_tool_id', '=', self.functional_tool_id.id)])
|
||||||
if obj:
|
action = {
|
||||||
for tool in obj.get('tool_changing_apply_id'):
|
'res_model': 'sf.functional.tool.dismantle',
|
||||||
self.env['sf.functional.tool.warning'].create({
|
'type': 'ir.actions.act_window',
|
||||||
'production_line_id': tool.production_line_id.id,
|
'name': '拆解单'
|
||||||
'maintenance_equipment_id': tool.maintenance_equipment_id.id,
|
}
|
||||||
'machine_tool_code': tool.machine_tool_code,
|
if len(dismantle_ids) == 1:
|
||||||
'machine_table_type_id': tool.machine_table_type_id.id,
|
action.update({
|
||||||
'cutter_spacing_code_id': tool.cutter_spacing_code_id.id,
|
'view_mode': 'form',
|
||||||
'functional_tool_name_id': tool.functional_tool_name_id.id,
|
'res_id': dismantle_ids.ids[0]
|
||||||
'barcode_id': tool.barcode_id.id,
|
})
|
||||||
'diameter': tool.diameter,
|
elif dismantle_ids:
|
||||||
'knife_tip_r_angle': tool.knife_tip_r_angle,
|
action.update({
|
||||||
'max_lifetime_value': tool.max_lifetime_value,
|
'view_mode': 'tree,form',
|
||||||
'alarm_value': tool.alarm_value,
|
'domain': [('id', 'in', dismantle_ids.ids)],
|
||||||
'used_value': tool.used_value,
|
})
|
||||||
'functional_tool_status': tool.functional_tool_status,
|
else:
|
||||||
'alarm_time': fields.Datetime.now(),
|
return False
|
||||||
})
|
return action
|
||||||
|
# def create_tool_warning_record(self, obj):
|
||||||
|
# """
|
||||||
|
# 机台换刀申请报警状态时,创建功能刀具预警记录
|
||||||
|
# """
|
||||||
|
# if obj:
|
||||||
|
# for tool in obj.get('tool_changing_apply_id'):
|
||||||
|
# self.env['sf.functional.tool.warning'].create({
|
||||||
|
# 'production_line_id': tool.production_line_id.id,
|
||||||
|
# 'maintenance_equipment_id': tool.maintenance_equipment_id.id,
|
||||||
|
# 'machine_tool_code': tool.machine_tool_code,
|
||||||
|
# 'machine_table_type_id': tool.machine_table_type_id.id,
|
||||||
|
# 'cutter_spacing_code_id': tool.cutter_spacing_code_id.id,
|
||||||
|
# 'functional_tool_name_id': tool.functional_tool_name_id.id,
|
||||||
|
# 'barcode_id': tool.barcode_id.id,
|
||||||
|
# 'diameter': tool.diameter,
|
||||||
|
# 'knife_tip_r_angle': tool.knife_tip_r_angle,
|
||||||
|
# 'max_lifetime_value': tool.max_lifetime_value,
|
||||||
|
# 'alarm_value': tool.alarm_value,
|
||||||
|
# 'used_value': tool.used_value,
|
||||||
|
# 'functional_tool_status': tool.functional_tool_status,
|
||||||
|
# 'alarm_time': fields.Datetime.now(),
|
||||||
|
# })
|
||||||
|
|
||||||
|
|
||||||
class StockMoveLine(models.Model):
|
class StockMoveLine(models.Model):
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class SfMaintenanceEquipment(models.Model):
|
|||||||
params = {"DeviceId": self.name}
|
params = {"DeviceId": self.name}
|
||||||
r = requests.get(crea_url, params=params, headers=headers)
|
r = requests.get(crea_url, params=params, headers=headers)
|
||||||
ret = r.json()
|
ret = r.json()
|
||||||
logging.info('register_equipment_tool:%s' % ret)
|
logging.info('机床刀库register_equipment_tool():%s' % ret)
|
||||||
datas = ret['Datas']
|
datas = ret['Datas']
|
||||||
self.write_maintenance_equipment_tool(datas)
|
self.write_maintenance_equipment_tool(datas)
|
||||||
if ret['Succeed']:
|
if ret['Succeed']:
|
||||||
|
|||||||
@@ -25,7 +25,10 @@
|
|||||||
<field name="max_lifetime_value"/>
|
<field name="max_lifetime_value"/>
|
||||||
<field name="alarm_value"/>
|
<field name="alarm_value"/>
|
||||||
<field name="used_value"/>
|
<field name="used_value"/>
|
||||||
<field name="functional_tool_status"/>
|
<field name="functional_tool_status" widget='badge'
|
||||||
|
decoration-success="functional_tool_status == '正常'"
|
||||||
|
decoration-muted="functional_tool_status == '已拆除'"
|
||||||
|
decoration-danger="functional_tool_status == '报警'"/>
|
||||||
<field name="current_location" string="当前位置"/>
|
<field name="current_location" string="当前位置"/>
|
||||||
|
|
||||||
<field name="current_location_id" invisible="1"/>
|
<field name="current_location_id" invisible="1"/>
|
||||||
@@ -48,6 +51,18 @@
|
|||||||
<div class="oe_button_box" name="button_box">
|
<div class="oe_button_box" name="button_box">
|
||||||
<!-- <button name="button_safe_inventory_id" string="更新功能刀具关联的安全库存记录"-->
|
<!-- <button name="button_safe_inventory_id" string="更新功能刀具关联的安全库存记录"-->
|
||||||
<!-- type="object" class="btn-primary"/>-->
|
<!-- type="object" class="btn-primary"/>-->
|
||||||
|
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
|
||||||
|
name="open_functional_tool_dismantle_form"
|
||||||
|
icon="fa-credit-card"
|
||||||
|
type="object"
|
||||||
|
attrs="{'invisible': [('dismantle_num', '=', 0)]}">
|
||||||
|
<div name="dismantle_num" class="o_field_widget o_readonly_modifier o_field_statinfo">
|
||||||
|
<span class="o_stat_info o_stat_value">
|
||||||
|
<field name="dismantle_num"/>
|
||||||
|
</span>
|
||||||
|
<span class="o_stat_text">拆解单</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
|
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
|
||||||
name="open_functional_tool_warning"
|
name="open_functional_tool_warning"
|
||||||
icon="fa-list-ul"
|
icon="fa-list-ul"
|
||||||
@@ -235,26 +250,29 @@
|
|||||||
<field name="name">sf.functional.tool.warning.tree</field>
|
<field name="name">sf.functional.tool.warning.tree</field>
|
||||||
<field name="model">sf.functional.tool.warning</field>
|
<field name="model">sf.functional.tool.warning</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="功能刀具预警" create="0" edit="0" delete="0" editable="bottom">
|
<tree string="功能刀具预警" create="0" edit="0" delete="0" editable="bottom" default_order="id desc"
|
||||||
<field name="production_line_id" optional="hide"/>
|
action="action_open_dismantle" type="object">
|
||||||
<field name="maintenance_equipment_id" optional="hide"/>
|
<field name="production_line_id" invisible="1"/>
|
||||||
<field name="machine_tool_code"/>
|
<field name="maintenance_equipment_id" invisible="1"/>
|
||||||
<field name="cutter_spacing_code_id"/>
|
<field name="machine_tool_code" invisible="1"/>
|
||||||
<field name="barcode_id" invisible="1"/>
|
<field name="cutter_spacing_code_id" invisible="1"/>
|
||||||
|
<field name="on_board_time" invisible="1"/>
|
||||||
|
<field name="functional_tool_status" invisible="1"/>
|
||||||
|
<field name="functional_tool_name_id" invisible="1"/>
|
||||||
|
|
||||||
<field name="rfid"/>
|
<field name="rfid"/>
|
||||||
<field name="functional_tool_name_id"/>
|
<field name="functional_tool_id"/>
|
||||||
<field name="diameter"/>
|
<field name="barcode_id" optional="hide"/>
|
||||||
<field name="knife_tip_r_angle"/>
|
<field name="diameter" optional="hide"/>
|
||||||
|
<field name="knife_tip_r_angle" optional="hide"/>
|
||||||
<field name="install_tool_time" optional="hide"/>
|
<field name="install_tool_time" optional="hide"/>
|
||||||
<field name="on_board_time" optional="hide"/>
|
|
||||||
<field name="max_lifetime_value"/>
|
<field name="max_lifetime_value"/>
|
||||||
<field name="alarm_value"/>
|
<field name="alarm_value"/>
|
||||||
<field name="used_value"/>
|
<field name="used_value"/>
|
||||||
<field name="functional_tool_status"/>
|
|
||||||
<field name="alarm_time"/>
|
<field name="alarm_time"/>
|
||||||
<field name="dispose_user"/>
|
<field name="dispose_user"/>
|
||||||
<field name="dispose_time"/>
|
<field name="dispose_time"/>
|
||||||
<field name="dispose_func"/>
|
<field name="dispose_func" optional="hide"/>
|
||||||
<!-- <button name="enroll_functional_tool_warning" string="刀具预警注册" type="object"-->
|
<!-- <button name="enroll_functional_tool_warning" string="刀具预警注册" type="object"-->
|
||||||
<!-- class="btn-primary"/>-->
|
<!-- class="btn-primary"/>-->
|
||||||
</tree>
|
</tree>
|
||||||
@@ -266,31 +284,19 @@
|
|||||||
<field name="model">sf.functional.tool.warning</field>
|
<field name="model">sf.functional.tool.warning</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search string="功能刀具预警">
|
<search string="功能刀具预警">
|
||||||
<field name="machine_tool_code"/>
|
|
||||||
<field name="cutter_spacing_code_id"/>
|
|
||||||
<field name="barcode_id"/>
|
<field name="barcode_id"/>
|
||||||
<field name="rfid"/>
|
<field name="rfid"/>
|
||||||
<field name="functional_tool_name_id"/>
|
<field name="functional_tool_name_id"/>
|
||||||
<field name="diameter"/>
|
|
||||||
<field name="knife_tip_r_angle"/>
|
|
||||||
<field name="install_tool_time" optional="hide"/>
|
|
||||||
<field name="on_board_time" optional="hide"/>
|
|
||||||
<field name="max_lifetime_value"/>
|
|
||||||
<field name="alarm_value"/>
|
|
||||||
<field name="used_value"/>
|
|
||||||
<field name="functional_tool_status"/>
|
|
||||||
<field name="alarm_time"/>
|
|
||||||
<field name="dispose_user"/>
|
|
||||||
<field name="dispose_time"/>
|
|
||||||
<field name="dispose_func"/>
|
|
||||||
<field name="production_line_id" invisible="True"/>
|
|
||||||
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
|
|
||||||
<searchpanel>
|
<searchpanel>
|
||||||
<field name="production_line_id" icon="fa-building" enable_counters="1"/>
|
<field name="production_line_id" icon="fa-building" enable_counters="1"/>
|
||||||
<field name="maintenance_equipment_id" icon="fa-building" enable_counters="1"/>
|
<field name="maintenance_equipment_id" icon="fa-building" enable_counters="1"/>
|
||||||
<field name="cutter_spacing_code_id" icon="fa-building" enable_counters="1"/>
|
<field name="cutter_spacing_code_id" icon="fa-building" enable_counters="1"/>
|
||||||
<field name="functional_tool_status" icon="fa-building" enable_counters="1"/>
|
<field name="functional_tool_status" icon="fa-building" enable_counters="1"/>
|
||||||
</searchpanel>
|
</searchpanel>
|
||||||
|
<group expand="0">
|
||||||
|
<filter string="报警时间" name="alarm_time" domain="[]"
|
||||||
|
context="{'group_by': 'alarm_time'}"/>
|
||||||
|
</group>
|
||||||
</search>
|
</search>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|||||||
@@ -704,10 +704,10 @@
|
|||||||
<field name="model">sf.functional.tool.assembly</field>
|
<field name="model">sf.functional.tool.assembly</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search>
|
<search>
|
||||||
|
<field name="functional_tool_name"/>
|
||||||
<field name="assembly_order_code"/>
|
<field name="assembly_order_code"/>
|
||||||
<field name="code" string="功能刀具编码"/>
|
<field name="code" string="功能刀具编码"/>
|
||||||
<field name="barcode_id"/>
|
<field name="barcode_id"/>
|
||||||
<field name="functional_tool_name"/>
|
|
||||||
<field name="functional_tool_type_id"/>
|
<field name="functional_tool_type_id"/>
|
||||||
<field name="tool_groups_id"/>
|
<field name="tool_groups_id"/>
|
||||||
<field name="loading_task_source" string="任务来源"/>
|
<field name="loading_task_source" string="任务来源"/>
|
||||||
@@ -800,10 +800,16 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<field name="_barcode_scanned" widget="barcode_handler"/>
|
<field name="_barcode_scanned" widget="barcode_handler"/>
|
||||||
|
<script>
|
||||||
|
setTimeout(function(){
|
||||||
|
$('#functional_tool_id').blur()
|
||||||
|
}, 100)
|
||||||
|
</script>
|
||||||
<group>
|
<group>
|
||||||
<group>
|
<group>
|
||||||
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
|
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
|
||||||
options="{'no_create': True}" attrs="{'readonly': [('state', '=', '已拆解')]}"/>
|
options="{'no_create': True}"
|
||||||
|
attrs="{'readonly': ['|',('state', '=', '已拆解'),('id', '!=', False)]}"/>
|
||||||
<field name="rfid" attrs="{'invisible': [('rfid', '=', '')]}"/>
|
<field name="rfid" attrs="{'invisible': [('rfid', '=', '')]}"/>
|
||||||
<field name="rfid_dismantle" attrs="{'invisible': [('rfid_dismantle', '=', False)]}"/>
|
<field name="rfid_dismantle" attrs="{'invisible': [('rfid_dismantle', '=', False)]}"/>
|
||||||
<field name="tool_type_id"/>
|
<field name="tool_type_id"/>
|
||||||
@@ -833,10 +839,26 @@
|
|||||||
<notebook>
|
<notebook>
|
||||||
<page string="物料组装信息">
|
<page string="物料组装信息">
|
||||||
<group>
|
<group>
|
||||||
<group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}">
|
<group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}"
|
||||||
|
col="1">
|
||||||
|
<group attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
|
||||||
|
<group col="3">
|
||||||
|
<group>
|
||||||
|
<field name="scrap_boolean" string="是否报废"/>
|
||||||
|
</group>
|
||||||
|
<group></group>
|
||||||
|
<group>
|
||||||
|
<button string="报废" name="tool_scrap" type="object"
|
||||||
|
class="btn-primary" confirm="是否确认报废刀柄"
|
||||||
|
attrs="{'invisible': [('scrap_boolean', '=', True)]}"/>
|
||||||
|
<button string="取消" name="tool_no_scrap" type="object"
|
||||||
|
class="btn-primary" confirm="是否取消报废刀柄"
|
||||||
|
attrs="{'invisible': [('scrap_boolean', '=', False)]}"/>
|
||||||
|
<group></group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
<group>
|
<group>
|
||||||
<field name="scrap_boolean" string="是否报废"
|
|
||||||
attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])], 'readonly': [('state', '=', '已拆解')]}"/>
|
|
||||||
<field name="handle_rfid" string="Rfid"/>
|
<field name="handle_rfid" string="Rfid"/>
|
||||||
<field name="handle_lot_id" string="序列号"/>
|
<field name="handle_lot_id" string="序列号"/>
|
||||||
<field name="handle_product_id" string="名称"/>
|
<field name="handle_product_id" string="名称"/>
|
||||||
@@ -911,7 +933,7 @@
|
|||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</page>
|
</page>
|
||||||
<page string="报废"
|
<page string="报废单"
|
||||||
attrs="{'invisible':[('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
|
attrs="{'invisible':[('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
|
||||||
<field name="scrap_ids">
|
<field name="scrap_ids">
|
||||||
<tree>
|
<tree>
|
||||||
@@ -952,8 +974,9 @@
|
|||||||
<field name="model">sf.functional.tool.dismantle</field>
|
<field name="model">sf.functional.tool.dismantle</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<search>
|
<search>
|
||||||
|
<field name="rfid"/>
|
||||||
<field name="functional_tool_id"/>
|
<field name="functional_tool_id"/>
|
||||||
<field name="code" string="拆解单编码"/>
|
<field name="code" string="拆解单号"/>
|
||||||
<filter name="no_dismantle_state" string="未拆解" domain="[('state','!=','已拆解')]"/>
|
<filter name="no_dismantle_state" string="未拆解" domain="[('state','!=','已拆解')]"/>
|
||||||
<filter name="dismantle_state" string="已拆解" domain="[('state','=','已拆解')]"/>
|
<filter name="dismantle_state" string="已拆解" domain="[('state','=','已拆解')]"/>
|
||||||
<separator/>
|
<separator/>
|
||||||
|
|||||||
@@ -842,6 +842,8 @@ class StockPicking(models.Model):
|
|||||||
stock_move_id = self.env['stock.move']
|
stock_move_id = self.env['stock.move']
|
||||||
datas = {'data': [], 'picking_id': picking_id}
|
datas = {'data': [], 'picking_id': picking_id}
|
||||||
if obj.handle_code_id:
|
if obj.handle_code_id:
|
||||||
|
# 修改刀柄序列号状态为【在用】
|
||||||
|
obj.handle_code_id.sudo().write({'tool_material_status': '在用'})
|
||||||
datas['data'].append(
|
datas['data'].append(
|
||||||
{'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
|
{'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
|
||||||
if obj.integral_product_id:
|
if obj.integral_product_id:
|
||||||
|
|||||||
@@ -201,6 +201,11 @@
|
|||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
<script>
|
||||||
|
setTimeout(function(){
|
||||||
|
$('#handle_code_id').blur()
|
||||||
|
}, 100)
|
||||||
|
</script>
|
||||||
<group string="组装物料信息" col="1">
|
<group string="组装物料信息" col="1">
|
||||||
<field name="_barcode_scanned" widget="barcode_handler"/>
|
<field name="_barcode_scanned" widget="barcode_handler"/>
|
||||||
<group col="1">
|
<group col="1">
|
||||||
@@ -368,7 +373,7 @@
|
|||||||
<group>
|
<group>
|
||||||
<field name="obtain_measurement_status" invisible="1"/>
|
<field name="obtain_measurement_status" invisible="1"/>
|
||||||
<button name="get_tool_preset_parameter" string="获取测量值" type="object"
|
<button name="get_tool_preset_parameter" string="获取测量值" type="object"
|
||||||
attrs="{'invisible': [('enable_tool_presetter', '=', False)]}"
|
attrs="{'invisible': [('enable_tool_presetter', '=', False)]}"
|
||||||
class="btn-primary"/>
|
class="btn-primary"/>
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -507,13 +507,13 @@ class ShelfLocation(models.Model):
|
|||||||
print('eeeeeee空闲', e)
|
print('eeeeeee空闲', e)
|
||||||
|
|
||||||
# 调取获取货位信息接口
|
# 调取获取货位信息接口
|
||||||
def get_sf_shelf_location_info(self):
|
def get_sf_shelf_location_info(self, device_id='Cabinet-AL'):
|
||||||
|
|
||||||
config = self.env['res.config.settings'].get_values()
|
config = self.env['res.config.settings'].get_values()
|
||||||
headers = {'Authorization': config['center_control_Authorization']}
|
headers = {'Authorization': config['center_control_Authorization']}
|
||||||
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos"
|
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos"
|
||||||
|
|
||||||
params = {'DeviceId': 'Cabinet-AL'}
|
params = {'DeviceId': device_id}
|
||||||
r = requests.get(crea_url, params=params, headers=headers)
|
r = requests.get(crea_url, params=params, headers=headers)
|
||||||
|
|
||||||
ret = r.json()
|
ret = r.json()
|
||||||
|
|||||||
@@ -13,88 +13,92 @@ class MrsShelfLocationDataSync(models.Model):
|
|||||||
_name = 'sf.shelf.location.datasync'
|
_name = 'sf.shelf.location.datasync'
|
||||||
_description = '同步库存信息'
|
_description = '同步库存信息'
|
||||||
|
|
||||||
|
def get_total_data(self):
|
||||||
|
|
||||||
|
# 建立对应关系的函数
|
||||||
|
def align_data(my_data, their_data):
|
||||||
|
paired_data = list(zip(my_data, their_data))
|
||||||
|
return paired_data
|
||||||
|
|
||||||
|
logging.info('============================get_total_data()======================')
|
||||||
|
shelf_1_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边刀架')], limit=1)
|
||||||
|
tool_location_objs_1 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_1_obj.id)], order='id')
|
||||||
|
|
||||||
|
location_codes_1 = [location.barcode for location in tool_location_objs_1]
|
||||||
|
print(location_codes_1)
|
||||||
|
# 对方的数据列表
|
||||||
|
their_data_1 = [f"ToolCab1-{i:02}" for i in range(1, 73)]
|
||||||
|
|
||||||
|
# 执行对齐
|
||||||
|
aligned_data_1 = align_data(location_codes_1, their_data_1)
|
||||||
|
|
||||||
|
# 2
|
||||||
|
shelf_2_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边刀架')], limit=1)
|
||||||
|
tool_location_objs_2 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_2_obj.id)], order='id')
|
||||||
|
|
||||||
|
location_codes_2 = [location.barcode for location in tool_location_objs_2]
|
||||||
|
print(location_codes_2)
|
||||||
|
# 对方的数据列表
|
||||||
|
their_data_2 = [f"ToolCab2-{i:02}" for i in range(1, 73)]
|
||||||
|
|
||||||
|
# 执行对齐
|
||||||
|
aligned_data_2 = align_data(location_codes_2, their_data_2)
|
||||||
|
|
||||||
|
# 4
|
||||||
|
shelf_4_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边料架')], limit=1)
|
||||||
|
tool_location_objs_4 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_4_obj.id)], order='id')
|
||||||
|
|
||||||
|
location_codes_4 = [location.barcode for location in tool_location_objs_4]
|
||||||
|
print(location_codes_4)
|
||||||
|
# 对方的数据列表
|
||||||
|
their_data_4 = [f"PartCab4-{i:02}" for i in range(1, 17)]
|
||||||
|
|
||||||
|
# 执行对齐
|
||||||
|
aligned_data_4 = align_data(location_codes_4, their_data_4)
|
||||||
|
|
||||||
|
# 3
|
||||||
|
shelf_3_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边料架')], limit=1)
|
||||||
|
tool_location_objs_3 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_3_obj.id)], order='id')
|
||||||
|
|
||||||
|
location_codes_3 = [location.barcode for location in tool_location_objs_3]
|
||||||
|
print(location_codes_3)
|
||||||
|
# 对方的数据列表
|
||||||
|
their_data_3 = [f"PartCab3-{i:02}" for i in range(1, 13)]
|
||||||
|
|
||||||
|
# 执行对齐
|
||||||
|
aligned_data_3 = align_data(location_codes_3, their_data_3)
|
||||||
|
|
||||||
|
# 5
|
||||||
|
shelf_5_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-三号线边料架')], limit=1)
|
||||||
|
tool_location_objs_5 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_5_obj.id)], order='id')
|
||||||
|
|
||||||
|
location_codes_5 = [location.barcode for location in tool_location_objs_5]
|
||||||
|
print(location_codes_5)
|
||||||
|
# 对方的数据列表
|
||||||
|
their_data_5 = [f"PartCab5-{i:02}" for i in range(1, 13)]
|
||||||
|
|
||||||
|
# 执行对齐
|
||||||
|
aligned_data_5 = align_data(location_codes_5, their_data_5)
|
||||||
|
|
||||||
|
total_data = aligned_data_1 + aligned_data_2 + aligned_data_3 + aligned_data_4 + aligned_data_5
|
||||||
|
print(total_data)
|
||||||
|
logging.info(f"total_data: {total_data}")
|
||||||
|
return total_data
|
||||||
|
|
||||||
|
def find_our_code(self, total_data, their_code):
|
||||||
|
for code_pair in total_data:
|
||||||
|
if code_pair[1] == their_code:
|
||||||
|
return code_pair[0]
|
||||||
|
return None # 如果没有找到对应的值,返回None或适当的默认值
|
||||||
|
|
||||||
def _cron_shelf_location_datasync(self):
|
def _cron_shelf_location_datasync(self):
|
||||||
try:
|
try:
|
||||||
# 建立对应关系的函数
|
|
||||||
def align_data(my_data, their_data):
|
|
||||||
paired_data = list(zip(my_data, their_data))
|
|
||||||
return paired_data
|
|
||||||
|
|
||||||
shelf_1_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边刀架')], limit=1)
|
|
||||||
tool_location_objs_1 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_1_obj.id)], order='id')
|
|
||||||
|
|
||||||
location_codes_1 = [location.barcode for location in tool_location_objs_1]
|
|
||||||
print(location_codes_1)
|
|
||||||
# 对方的数据列表
|
|
||||||
their_data_1 = [f"ToolCab1-{i:02}" for i in range(1, 73)]
|
|
||||||
|
|
||||||
# 执行对齐
|
|
||||||
aligned_data_1 = align_data(location_codes_1, their_data_1)
|
|
||||||
|
|
||||||
# 2
|
|
||||||
shelf_2_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边刀架')], limit=1)
|
|
||||||
tool_location_objs_2 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_2_obj.id)], order='id')
|
|
||||||
|
|
||||||
location_codes_2 = [location.barcode for location in tool_location_objs_2]
|
|
||||||
print(location_codes_2)
|
|
||||||
# 对方的数据列表
|
|
||||||
their_data_2 = [f"ToolCab2-{i:02}" for i in range(1, 73)]
|
|
||||||
|
|
||||||
# 执行对齐
|
|
||||||
aligned_data_2 = align_data(location_codes_2, their_data_2)
|
|
||||||
|
|
||||||
# 4
|
|
||||||
shelf_4_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边料架')], limit=1)
|
|
||||||
tool_location_objs_4 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_4_obj.id)], order='id')
|
|
||||||
|
|
||||||
location_codes_4 = [location.barcode for location in tool_location_objs_4]
|
|
||||||
print(location_codes_4)
|
|
||||||
# 对方的数据列表
|
|
||||||
their_data_4 = [f"PartCab4-{i:02}" for i in range(1, 17)]
|
|
||||||
|
|
||||||
# 执行对齐
|
|
||||||
aligned_data_4 = align_data(location_codes_4, their_data_4)
|
|
||||||
|
|
||||||
# 3
|
|
||||||
shelf_3_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边料架')], limit=1)
|
|
||||||
tool_location_objs_3 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_3_obj.id)], order='id')
|
|
||||||
|
|
||||||
location_codes_3 = [location.barcode for location in tool_location_objs_3]
|
|
||||||
print(location_codes_3)
|
|
||||||
# 对方的数据列表
|
|
||||||
their_data_3 = [f"PartCab3-{i:02}" for i in range(1, 13)]
|
|
||||||
|
|
||||||
# 执行对齐
|
|
||||||
aligned_data_3 = align_data(location_codes_3, their_data_3)
|
|
||||||
|
|
||||||
# 5
|
|
||||||
shelf_5_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-三号线边料架')], limit=1)
|
|
||||||
tool_location_objs_5 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_5_obj.id)], order='id')
|
|
||||||
|
|
||||||
location_codes_5 = [location.barcode for location in tool_location_objs_5]
|
|
||||||
print(location_codes_5)
|
|
||||||
# 对方的数据列表
|
|
||||||
their_data_5 = [f"PartCab5-{i:02}" for i in range(1, 13)]
|
|
||||||
|
|
||||||
# 执行对齐
|
|
||||||
aligned_data_5 = align_data(location_codes_5, their_data_5)
|
|
||||||
|
|
||||||
total_data = aligned_data_1 + aligned_data_2 + aligned_data_3 + aligned_data_4 + aligned_data_5
|
|
||||||
print(total_data)
|
|
||||||
logging.info(f"total_data: {total_data}")
|
|
||||||
|
|
||||||
def find_their_code(my_code, aligned_data):
|
def find_their_code(my_code, aligned_data):
|
||||||
for code_pair in aligned_data:
|
for code_pair in aligned_data:
|
||||||
if code_pair[0] == my_code:
|
if code_pair[0] == my_code:
|
||||||
return code_pair[1]
|
return code_pair[1]
|
||||||
return None # 如果没有找到对应的值,返回None或适当的默认值
|
return None # 如果没有找到对应的值,返回None或适当的默认值
|
||||||
|
|
||||||
def find_our_code(their_code, aligned_data):
|
|
||||||
for code_pair in aligned_data:
|
|
||||||
if code_pair[1] == their_code:
|
|
||||||
return code_pair[0]
|
|
||||||
return None # 如果没有找到对应的值,返回None或适当的默认值
|
|
||||||
|
|
||||||
# 定时更新所有设备机床刀库信息
|
# 定时更新所有设备机床刀库信息
|
||||||
equipment_ids = self.env['maintenance.equipment'].search(
|
equipment_ids = self.env['maintenance.equipment'].search(
|
||||||
[('equipment_type', '=', '机床'), ('function_type', '!=', False)])
|
[('equipment_type', '=', '机床'), ('function_type', '!=', False)])
|
||||||
@@ -103,9 +107,10 @@ class MrsShelfLocationDataSync(models.Model):
|
|||||||
equipment_id.register_equipment_tool()
|
equipment_id.register_equipment_tool()
|
||||||
|
|
||||||
shelfinfo = self.env['sf.shelf.location'].get_sf_shelf_location_info()
|
shelfinfo = self.env['sf.shelf.location'].get_sf_shelf_location_info()
|
||||||
|
total_data = self.get_total_data()
|
||||||
print('shelfinfo:', shelfinfo)
|
print('shelfinfo:', shelfinfo)
|
||||||
for item in shelfinfo:
|
for item in shelfinfo:
|
||||||
shelf_barcode = find_our_code(item['Postion'], total_data)
|
shelf_barcode = self.find_our_code(total_data, item['Postion'])
|
||||||
location_id = self.env['sf.shelf.location'].search([('barcode', '=', shelf_barcode)], limit=1)
|
location_id = self.env['sf.shelf.location'].search([('barcode', '=', shelf_barcode)], limit=1)
|
||||||
if location_id:
|
if location_id:
|
||||||
# 如果是线边刀库信息,则对功能刀具移动生成记录
|
# 如果是线边刀库信息,则对功能刀具移动生成记录
|
||||||
@@ -115,6 +120,13 @@ class MrsShelfLocationDataSync(models.Model):
|
|||||||
tool.tool_in_out_stock_location(location_id)
|
tool.tool_in_out_stock_location(location_id)
|
||||||
if tool:
|
if tool:
|
||||||
location_id.product_sn_id = tool.barcode_id.id
|
location_id.product_sn_id = tool.barcode_id.id
|
||||||
|
# 修改功能刀具状态
|
||||||
|
tool_state = {'Nomal': '正常', 'Warning': '报警'}
|
||||||
|
if tool_state.get(item.get('State')):
|
||||||
|
if tool_state.get(item.get('State')) != tool.functional_tool_status:
|
||||||
|
tool.write({
|
||||||
|
'functional_tool_status': tool_state.get(item['State'])
|
||||||
|
})
|
||||||
else:
|
else:
|
||||||
location_id.product_sn_id = False
|
location_id.product_sn_id = False
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user