优化sf获取cnc程序和ftp

This commit is contained in:
jinling.yang
2023-01-09 19:54:02 +08:00
parent c023a13e3a
commit b7723f82d0
24 changed files with 473 additions and 224 deletions

View File

@@ -10,15 +10,15 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['mrp', 'sf_base', 'maintenance'],
'depends': ['mrp', 'sf_base', 'maintenance', 'sf_mrs_connect'],
'data': [
'security/group_security.xml',
'security/ir.model.access.csv',
'report/tray_report.xml',
'views/mrp_maintenance_views.xml',
'views/mrp_routing_workcenter_view.xml',
'views/mrp_workorder_view.xml',
'views/mrp_workcenter_views.xml',
'views/mrp_workorder_view.xml',
'views/tray_view.xml',
'views/model_type_view.xml',

View File

@@ -6,6 +6,9 @@ from . import mrp_routing_workcenter
from . import mrp_workorder
from . import model_type
from . import stock
from . import res_user

View File

@@ -6,7 +6,7 @@ class ModelType(models.Model):
_description = '模型类型'
name = fields.Char('名称')
embryo_tolerance = fields.Integer('胚料的容余量')
embryo_tolerance = fields.Boolean('胚料的容余量', default=False)
routing_tmpl_ids = fields.One2many('sf.model.type.routing.sort', 'model_type_id', '工序模板')

View File

@@ -1,5 +1,11 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo import api, fields, models,_
class ResProducTemplate_Production(models.Model):
_inherit = 'product.template'
model_file = fields.Binary('模型文件')
class MrpProduction(models.Model):
@@ -9,8 +15,7 @@ class MrpProduction(models.Model):
tray_ids = fields.One2many('sf.tray', 'production_id', string="托盘")
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
request_ids = fields.One2many('maintenance.request', 'production_id')
# model_file = fields.Binary('模型文件', related='product_id.model_file')
# model_information = fields.Char('模型信息', related='product_id.model_information')
model_file = fields.Binary('模型文件', related='product_id.model_file')
@api.depends('request_ids')
def _compute_maintenance_count(self):
@@ -131,9 +136,6 @@ class MrpProduction(models.Model):
'location_src_id': production.location_src_id.id,
'location_dest_id': production.location_dest_id.id,
'bom_id': production.bom_id.id,
'model_file': base64.b64decode(production.product_id.model_file),
'model_information': '%s/%s' % (
production.product_id.model_processing_panel, production.product_id.materials_id.name),
'date_deadline': production.date_deadline,
'date_planned_start': production.date_planned_start,
'date_planned_finished': production.date_planned_finished,

View File

@@ -7,16 +7,18 @@ class ResWorkcenter(models.Model):
_inherit = "mrp.workcenter"
machine_tool_id = fields.Many2one('sf.machine_tool', '机床')
@api.onchange('machine_tool_id')
def get_machine_tool_is_binding(self):
print('1111111')
for item in self:
item.machine_tool_id.is_binding = False
users_ids = fields.Many2many("res.users", 'users_workcenter')
equipment_ids = fields.One2many(
'maintenance.equipment', 'workcenter_id', string="Maintenance Equipment",
check_company=True)
@api.onchange('machine_tool_id')
def update_machine_tool_is_binding(self):
self.machine_tool_id.is_binding = True
def action_work_order(self):
if not self.env.context.get('desktop_list_view', False):
action = self.env["ir.actions.actions"]._for_xml_id("sf_manufacturing.mrp_workorder_action_tablet")

View File

@@ -4,6 +4,7 @@ import math
import requests
import logging
import base64
import hashlib
# import subprocess
from datetime import datetime
from dateutil.relativedelta import relativedelta
@@ -19,6 +20,7 @@ class ResMrpWorkOrder(models.Model):
_order = 'sequence'
workcenter_id = fields.Many2one('mrp.workcenter', required=False)
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
processing_panel = fields.Char('加工面')
sequence = fields.Integer(string='工序')
routing_type = fields.Selection([
@@ -29,8 +31,26 @@ class ResMrpWorkOrder(models.Model):
('后置三元质量检测', '后置三元质量检测'),
('解除装夹', '解除装夹'),
], string="工序类型")
@api.onchange('users_ids')
def get_user_permissions(self):
uid = self.env.uid
for workorder in self:
if workorder.users_ids:
list_user_id = []
for item in workorder.users_ids:
list_user_id.append(item.id)
if uid in list_user_id:
workorder.user_permissions = True
else:
workorder.user_permissions = False
else:
workorder.user_permissions = False
user_permissions = fields.Boolean('用户权限', compute='get_user_permissions')
programming_no = fields.Char('编程单号')
is_programming = fields.Boolean('是否编程', default=False)
work_state = fields.Char('业务状态')
programming_state = fields.Char('编程状态')
cnc_worksheet = fields.Binary(
'工作指令', readonly=True)
material_center_point = fields.Char(string='配料中心点')
@@ -106,7 +126,11 @@ class ResMrpWorkOrder(models.Model):
print("(%.2f,%.2f)" % (x, y))
self.material_center_point = ("(%.2f,%.2f,%.2f)" % (x, y, z))
self.X_deviation_angle = jdz
return self.material_center_point
# 将补偿值写入CNC加工工单
workorder = self.env['mrp.workorder'].browse(self.ids)
work = workorder.production_id.workorder_ids
work.compensation_value_x = eval(self.material_center_point)[0]
work.compensation_value_y = eval(self.material_center_point)[1]
def json_workorder_str(self, k, production, route):
workorders_values_str = [0, '', {
@@ -116,6 +140,7 @@ class ResMrpWorkOrder(models.Model):
'name': route.route_workcenter_id.name,
'processing_panel': k,
'routing_type': route.routing_type,
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids),
'date_planned_start': False,
'date_planned_finished': False,
@@ -233,35 +258,72 @@ class ResMrpWorkOrder(models.Model):
else:
return True
# def fetchCNCing(self):
# return None
# cnc程序获取
def fetchCNC(self):
cnc = self.env['mrp.workorder'].search(
[('routing_type', '=', 'CNC加工'), ('production_id', '=', self.production_id.id)], limit=1)
res = {'model_code': cnc.product_id.barcode, 'production_no': self.production_id.name,
'machine_tool_code': cnc.workcenter_id.machine_tool_id.code,
'material_code': cnc.env['sf.production.materials'].search(
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
'material_type_code': cnc.env['sf.materials.model'].search(
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
'machining_precision': cnc.product_id.model_machining_precision,
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
'order_no': cnc.production_id.origin,
'user': self.env.user.name,
'model_file': base64.b64encode(cnc.product_id.model_file).decode('utf-8')
}
logging.info('res:%s' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/create'
config_url = configsettings['sf_url'] + url
# res_str = json.dumps(res)
ret = requests.post(config_url, json={}, data=res, headers=config_header)
ret = ret.json()
result = json.loads(ret['result'])
if result['status'] == 1:
return self.write({'programming_no': result['programming_no'], 'is_programming': True})
try:
cnc = self.env['mrp.workorder'].search(
[('routing_type', '=', 'CNC加工'), ('production_id', '=', self.production_id.id)], limit=1)
logging.info('fetchCNC-cnc:%s' % cnc)
# if cnc.product_id.upload_model_file:
# logging.info('fetchCNC-upload_model_file:%s' % cnc.product_id.upload_model_file)
# attachments = cnc.product_id.upload_model_file[0]
# logging.info('fetchCNC-attachment1:%s' % attachments)
# logging.info('fetchCNC-attachment1:%s' % cnc.product_id.upload_model_file[0])
# logging.info('fetchCNC-attachment2:%s' % cnc.product_id.upload_model_file[0].datas)
# logging.info('fetchCNC-attachment:%s' % attachments.datas)
# base64_data = base64.b64encode(attachments.datas)
# logging.info('fetchCNC-attachment1:%s' % attachments)
# base64_datas = base64_data.decode('utf-8')
# model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# logging.info('fetchCNC-model_code:%s' % model_code)
logging.info('fetchCNC-model_code1:%s' % cnc.product_id.model_code)
res = {'model_code': '' if not cnc.product_id.model_code else cnc.product_id.model_code,
'production_no': self.production_id.name,
'machine_tool_code': cnc.workcenter_id.machine_tool_id.code,
'material_code': cnc.env['sf.production.materials'].search(
[('id', '=', cnc.product_id.materials_id.id)]).materials_no,
'material_type_code': cnc.env['sf.materials.model'].search(
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
'machining_processing_panel': cnc.product_id.model_processing_panel,
'machining_precision': cnc.product_id.model_machining_precision,
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height,
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
'order_no': cnc.production_id.origin,
'user': self.env.user.name,
'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
cnc.product_id.model_file).decode('utf-8')
}
logging.info('res:%s' % res)
configsettings = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
url = '/api/intelligent_programming/create'
config_url = configsettings['sf_url'] + url
# res_str = json.dumps(res)
ret = requests.post(config_url, json={}, data=res, headers=config_header)
ret = ret.json()
logging.info('fetchCNC-ret:%s' % ret)
if ret['status'] == 1:
self.write(
{'programming_no': ret['programming_no'], 'programming_state': '编程中', 'work_state': '编程中'})
else:
logging.info('fetchCNC-error:%s' % cnc)
raise UserError('行业资源库解析失败')
except Exception as e:
logging.info('fetchCNC error:%s' % e)
raise UserError(e)
# return {
# 'name': _("工单"),
# 'view_mode': 'form',
# 'res_model': 'mrp.workorder',
# 'res_id': self.id,
# 'type': 'ir.actions.act_window',
# 'target': 'new'
# }
def json_workorder_str1(self, k, production, route):
workorders_values_str = [0, '', {
@@ -271,6 +333,7 @@ class ResMrpWorkOrder(models.Model):
'name': route.route_workcenter_id.name,
'processing_panel': k,
'routing_type': route.routing_type,
'work_state': '' if not route.routing_type == '获取CNC加工程序' else '待发起',
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids),
'date_planned_start': False,
'date_planned_finished': False,
@@ -349,44 +412,73 @@ class CNCprocessing(models.Model):
workorder_id = fields.Many2one('mrp.workorder', string="工单")
# mrs下发编程单创建CNC加工
def cnc_processing_create(self, obj):
workorder = self.env['mrp.workorder'].search([('production_id.name', '=', obj['production_order_no']),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
vals = {
'workorder_id': workorder.id,
'sequence_number': obj['sequence_number'],
'program_name': obj['program_name'],
'cutting_tool_name': obj['cutting_tool_name'],
'cutting_tool_no': obj['cutting_tool_no'],
'processing_type': obj['processing_type'],
'margin_x_y': obj['margin_x_y'],
'margin_z': obj['margin_z'],
'depth_of_processing_z': obj['depth_of_processing_z'],
'cutting_tool_extension_length': obj['cutting_tool_extension_length'],
'cutting_tool_handle_type': obj['cutting_tool_handle_type'],
'estimated_processing_time': obj['estimated_processing_time'],
'remark': obj['remark']
}
return self.env['sf.cnc.processing'].create(vals)
def cnc_processing_create(self, cnc_workorder, ret):
for obj in ret['programming_list']:
workorder = self.env['mrp.workorder'].search([('production_id.name', '=', ret['production_order_no']),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
cnc_processing = self.env['sf.cnc.processing'].create({
'workorder_id': workorder.id,
'sequence_number': obj['sequence_number'],
'program_name': obj['program_name'],
'cutting_tool_name': obj['cutting_tool_name'],
'cutting_tool_no': obj['cutting_tool_no'],
'processing_type': obj['processing_type'],
'margin_x_y': obj['margin_x_y'],
'margin_z': obj['margin_z'],
'depth_of_processing_z': obj['depth_of_processing_z'],
'cutting_tool_extension_length': obj['cutting_tool_extension_length'],
'cutting_tool_handle_type': obj['cutting_tool_handle_type'],
'estimated_processing_time': obj['estimated_processing_time'],
'remark': obj['remark']
})
self.get_cnc_processing_file(ret['folder_name'], cnc_processing)
cnc_workorder.state = 'done'
cnc_workorder.work_state = '已编程'
cnc_workorder.programming_state = '已编程'
cnc_workorder.time_ids.date_end = datetime.now()
def get_cnc_processing_file(self, folder_name, cnc_processing):
logging.info('folder_name:%s' % folder_name)
serverdir = os.path.join('/', folder_name, 'return', cnc_processing.processing_panel)
logging.info('serverdir:%s' % serverdir)
for root, dirs, files in os.walk(server_dir):
for f in files:
logging.info('f:%s' % f)
if os.path.splitext(f)[1] == ".pdf":
full_path = os.path.join(server_dir, root, f)
logging.info('pdf:%s' % full_path)
if full_path != False:
if not cnc_processing.workorder_id.cnc_worksheet:
cnc_processing.workorder_id.cnc_worksheet = base64.b64encode(
open(full_path, 'rb').read())
else:
if cnc_processing.program_name == f.split('.')[0]:
cnc_file_path = os.path.join(server_dir, root, f)
logging.info('cnc_file_path:%s' % cnc_file_path)
cnc_processing.with_user(request.env.ref("base.user_admin")).write_file(cnc_file_path,
cnc_processing)
# 创建附件(nc文件)
def attachment_create(self, name, data):
attachment = self.env['ir.attachment'].create({
'datas': base64.b64encode(data),
'type': 'binary',
'public': True,
'description': '程序文件',
'name': name
})
return attachment
# 将FTP的nc文件下载到临时目录
def download_file_tmp(self, model_code, processing_panel):
remotepath = os.path.join('/', model_code, 'return', processing_panel)
serverdir = os.path.join('/tmp', model_code, 'return', processing_panel)
ftp = FtpController()
ftp.download_file_tree(remotepath, serverdir)
return serverdir
def download_file_tmp(self, production_no, processing_panel):
remotepath = os.path.join('/', production_no, 'return', processing_panel)
serverdir = os.path.join('/tmp', production_no, 'return', processing_panel)
ftp_resconfig = self.env['res.config.settings'].get_values()
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'],
ftp_resconfig['ftp_password'])
download_state = ftp.download_file_tree(remotepath, serverdir)
return download_state
# 将nc文件存到attach的datas里
def write_file(self, nc_file_path, cnc):

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from odoo import SUPERUSER_ID, _, api, fields, models, registry
class Users(models.Model):
_inherit = 'res.users'
workcenter_ids = fields.Many2many("mrp.workcenter", 'users_workcenter')

View File

@@ -66,25 +66,24 @@ class StockRule(models.Model):
list2 = []
for item in procurements:
num = int(item[0].product_qty)
product = self.env['product.template'].search(
["&", ("name", '=', item[0].product_id.display_name), ('single_manufacturing', '!=', False)])
if product:
product = self.env['product.product'].search(
[("id", '=', item[0].product_id.id)])
product_tmpl = self.env['product.template'].search(
["&", ("id", '=', product.product_tmpl_id.id), ('single_manufacturing', "!=", False)])
if product_tmpl:
if num > 1:
for no in range(1, num + 1):
Procurement = namedtuple('Procurement', ['product_id', 'product_qty',
'product_uom', 'location_id', 'name', 'origin',
'company_id',
'values', 'model_file',
'model_information'])
'values'])
s = Procurement(product_id=item[0].product_id, product_qty=1.0, product_uom=item[0].product_uom,
location_id=item[0].location_id,
name=item[0].name,
origin=item[0].origin,
company_id=item[0].company_id,
values=item[0].values,
model_file=base64.b64decode(item[0].product_id.model_file),
model_information='%s/%s' % (item[0].product_id.model_processing_panel,
item[0].product_id.materials_id.name),
)
item1 = list(item)
item1[0] = s

View File

@@ -3,7 +3,7 @@
import base64
from io import BytesIO
from odoo import api, fields, models, SUPERUSER_ID, _
# from pystrich.code128 import Code128Encoder
#from pystrich.code128 import Code128Encoder
class Tray(models.Model):

View File

@@ -31,7 +31,7 @@
<form string="模型类型">
<group>
<field name="name" required="1"/>
<field name="embryo_tolerance" string="胚料容余(mm)"/>
<field name="embryo_tolerance" required="1"/>
</group>
<group>
<field name='routing_tmpl_ids'>

View File

@@ -24,14 +24,16 @@
</field>
</record>
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
<field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>
<field name="model">mrp.workcenter</field>
<field name="inherit_id" ref="mrp.mrp_workcenter_kanban"/>
<field name="arch" type="xml">
<!-- Desktop view -->
<xpath expr="//div[@name='o_wo']" position="inside">
<button class="btn btn-secondary fa fa-desktop" name="action_work_order" type="object" context="{'search_default_ready': 1, 'search_default_progress': 1, 'search_default_pending': 1, 'desktop_list_view': 1, 'search_default_workcenter_id': active_id}" title="Work orders" aria-label="Work orders"/>
<button class="btn btn-secondary fa fa-desktop" name="action_work_order" type="object"
context="{'search_default_ready': 1, 'search_default_progress': 1, 'search_default_pending': 1, 'desktop_list_view': 1, 'search_default_workcenter_id': active_id}"
title="Work orders" aria-label="Work orders"/>
</xpath>
</field>
</record>
@@ -39,25 +41,46 @@
<!-- override to change the no content image -->
<record id="mrp.action_work_orders" model="ir.actions.act_window">
<field name="help" type="html">
<p class="o_view_nocontent_workorder">
No work orders to do!
</p><p>
Work orders are operations to do as part of a manufacturing order.
Operations are defined in the bill of materials or added in the manufacturing order directly.
</p><p>
Use the table work center control panel to register operations in the shop floor directly.
The tablet provides worksheets for your workers and allow them to scrap products, track time,
launch a maintenance request, perform quality tests, etc.
</p>
<p class="o_view_nocontent_workorder">
No work orders to do!
</p>
<p>
Work orders are operations to do as part of a manufacturing order.
Operations are defined in the bill of materials or added in the manufacturing order directly.
</p>
<p>
Use the table work center control panel to register operations in the shop floor directly.
The tablet provides worksheets for your workers and allow them to scrap products, track time,
launch a maintenance request, perform quality tests, etc.
</p>
</field>
</record>
<record id="mrp_workcenter_kanban_action1" model="ir.actions.act_window">
<field name="name">Work Centers Overview</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.workcenter</field>
<field name="view_mode">kanban,form</field>
<field name="view_id" ref="mrp.mrp_workcenter_kanban"/>
<field name="search_view_id" ref="mrp.view_mrp_workcenter_search"/>
<field name="domain">[('users_ids','in',uid)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new work center
</p>
<p>
Manufacturing operations are processed at Work Centers. A Work Center can be composed of
workers and/or machines, they are used for costing, scheduling, capacity planning, etc.
They can be defined via the configuration menu.
</p>
</field>
</record>
<menuitem id="menu_mrp_dashboard"
name="工作中心概述"
action="mrp.mrp_workcenter_kanban_action"
groups="mrp.group_mrp_routings"
parent="mrp.menu_mrp_root"
sequence="5"/>
name="工作中心概述"
action="mrp_workcenter_kanban_action1"
groups="mrp.group_mrp_routings"
parent="mrp.menu_mrp_root"
sequence="5"/>
<!-- MRP.WORKCENTER -->
<record model="ir.ui.view" id="view_mrp_workcenter_form_inherit_sf">
@@ -79,6 +102,9 @@
</field>
</page>
</xpath>
<xpath expr="//field[@name='company_id']" position="after">
<field name="users_ids" widget="many2many_tags" string="可操作用户"/>
</xpath>
<xpath expr="//field[@name='alternative_workcenter_ids']" position="after">
<field name="machine_tool_id" domain="[('is_binding', '=', False)]"/>

View File

@@ -7,10 +7,30 @@
<field name="arch" type="xml">
<field name="name" position="before">
<field name="sequence"/>
<field name='user_permissions'/>
</field>
<field name="name" position="after">
<field name="processing_panel"/>
</field>
<field name="state" position="after">
<field name="work_state"/>
</field>
<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)]}
</attribute>
</xpath>
<!-- <button name="button_start" type="object" string="Start" class="btn-success"-->
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), ('is_user_working', '!=', False)]}"/>-->
<!-- <button name="button_pending" type="object" string="Pause" class="btn-warning"-->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>-->
<!-- <button name="button_finish" type="object" string="Done" class="btn-success"-->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>-->
<tree position="attributes">
<attribute name="multi_edit"></attribute>
<attribute name="editable"></attribute>
</tree>
</field>
</record>
@@ -57,15 +77,27 @@
<field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
<field name="arch" type="xml">
<field name="production_id" position="after">
<field name="programming_no" readonly="1"
attrs='{"invisible": [("routing_type","!=","获取CNC加工程序")]}'/>
</field>
<xpath expr="field[@name='is_user_working']" position="before">
<field name='user_permissions' invisible="1"/>
</xpath>
<xpath expr="//page[last()]" position="after">
<page string="获取CNC加工程序" attrs='{"invisible": [("routing_type","!=","获取CNC加工程序")]}'>
<div class="col-12 col-lg-6 o_setting_box">
<button type="object" class="oe_highlight" name="fetchCNC" string="获取CNC程序代码"/>
</div>
<group>
<field name="programming_no" readonly="1"
attrs='{"invisible": [("programming_no","=",False)]}'/>
<field name="programming_state" readonly="1"
attrs='{"invisible": [("programming_no","=",False)]}'/>
</group>
<group>
<div class="col-12 col-lg-6 o_setting_box" style="white-space: nowrap">
<button type="object" class="oe_highlight" name="fetchCNC" string="获取CNC程序代码"
attrs='{"invisible": [("user_permissions","=",False)]}'/>
<button type="object" class="oe_highlight disabled" name="fetchCNC" string="获取CNC程序代码"
attrs='{"invisible": ["|",("programming_no","=",False),("programming_no","=",False)]}'/>
</div>
</group>
</page>
</xpath>
@@ -189,7 +221,8 @@
</div>
<div class="col-12 col-lg-6 o_setting_box">
<button type="object" class="oe_highlight" name="getcenter" string="计算定位"/>
<button type="object" class="oe_highlight" name="getcenter" string="计算定位"
attrs='{"invisible": [("user_permissions","=",False)]}'/>
</div>
<group>
@@ -231,7 +264,7 @@
</group>
<div class="col-12 col-lg-6 o_setting_box">
<button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"
string="检测确认"/>
string="检测确认" attrs='{"invisible": [("user_permissions","=",False)]}'/>
</div>
</page>
</xpath>
@@ -239,11 +272,12 @@
<page string="解除装夹" attrs='{"invisible": [("routing_type","!=","解除装夹")]}'>
<div class="col-12 col-lg-6 o_setting_box">
<button type="object" class="oe_highlight" name="unbindtray" string="解除装夹"/>
<button type="object" class="oe_highlight" name="unbindtray" string="解除装夹"
attrs='{"invisible": [("user_permissions","=",False)]}'/>
</div>
<div class="col-12 col-lg-6 o_setting_box">
<button type="action" class="oe_highlight" name="sf_manufacturing.label_sf_tray_code1"
string="打印标签"/>
string="打印标签" attrs='{"invisible": [("user_permissions","=",False)]}'/>
</div>
</page>