Merge branch 'feature/优化最新版返工' into develop
This commit is contained in:
@@ -14,9 +14,11 @@
|
|||||||
'data': [
|
'data': [
|
||||||
'data/stock_data.xml',
|
'data/stock_data.xml',
|
||||||
'data/empty_racks_data.xml',
|
'data/empty_racks_data.xml',
|
||||||
|
'data/panel_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',
|
||||||
'views/mrp_views_menus.xml',
|
'views/mrp_views_menus.xml',
|
||||||
'views/stock_lot_views.xml',
|
'views/stock_lot_views.xml',
|
||||||
'views/mrp_production_addional_change.xml',
|
'views/mrp_production_addional_change.xml',
|
||||||
|
|||||||
@@ -220,21 +220,22 @@ class Manufacturing_Connect(http.Controller):
|
|||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
# workorder.write({'date_finished': datetime.now()})
|
# workorder.write({'date_finished': datetime.now()})
|
||||||
if ret['IsComplete'] is True:
|
if ret['IsComplete'] is True:
|
||||||
workorder.button_finish()
|
workorder.write({'date_finished': datetime.now()})
|
||||||
# workorder.process_state = '待解除装夹'
|
|
||||||
# workorder.sudo().production_id.process_state = '待解除装夹'
|
|
||||||
|
|
||||||
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
|
# workorder.process_state = '待解除装夹'
|
||||||
# if workorder.date_finished:
|
# workorder.sudo().production_id.process_state = '待解除装夹'
|
||||||
# request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
|
|
||||||
# {'actual_end_time': workorder.date_finished,
|
# 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态
|
||||||
# 'state': 'finished'})
|
# if workorder.date_finished:
|
||||||
# production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
|
# request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write(
|
||||||
# if production_obj:
|
# {'actual_end_time': workorder.date_finished,
|
||||||
# production_obj.sudo().work_order_state = '已完成'
|
# 'state': 'finished'})
|
||||||
# production_obj.write({'state': 'done'})
|
# production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)])
|
||||||
# request.env['sale.order'].sudo().search(
|
# if production_obj:
|
||||||
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
|
# production_obj.sudo().work_order_state = '已完成'
|
||||||
|
# production_obj.write({'state': 'done'})
|
||||||
|
# request.env['sale.order'].sudo().search(
|
||||||
|
# [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
@@ -540,6 +541,7 @@ 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': '已下产线'})
|
||||||
|
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', '=', '下产线'),
|
||||||
|
|||||||
24
sf_manufacturing/data/panel_data.xml
Normal file
24
sf_manufacturing/data/panel_data.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="panel_zm" model="sf.processing.panel">
|
||||||
|
<field name="name">ZM</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="panel_fm" model="sf.processing.panel">
|
||||||
|
<field name="name">FM</field>
|
||||||
|
</record>
|
||||||
|
<record id="panel_yc" model="sf.processing.panel">
|
||||||
|
<field name="name">YC</field>
|
||||||
|
</record>
|
||||||
|
<record id="panel_zc" model="sf.processing.panel">
|
||||||
|
<field name="name">ZC</field>
|
||||||
|
</record>
|
||||||
|
<record id="panel_qc" model="sf.processing.panel">
|
||||||
|
<field name="name">QC</field>
|
||||||
|
</record>
|
||||||
|
<record id="panel_hc" model="sf.processing.panel">
|
||||||
|
<field name="name">HC</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="1">
|
<data noupdate="1">
|
||||||
|
<record model="ir.cron" id="ir_cron_mrp_production">
|
||||||
|
<field name="name">返工且编程中的制造订单定时获取Cloud编程单状态</field>
|
||||||
|
<field name="model_id" ref="model_mrp_production"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">model._cron_get_programming_state()</field>
|
||||||
|
<field name="interval_number">3</field>
|
||||||
|
<field name="interval_type">minutes</field>
|
||||||
|
<field name="numbercall">-1</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
<record id="sequence_routing_workcenter" model="ir.sequence">
|
<record id="sequence_routing_workcenter" model="ir.sequence">
|
||||||
<field name="name">工序编码规则</field>
|
<field name="name">工序编码规则</field>
|
||||||
<field name="code">mrp.routing.workcenter</field>
|
<field name="code">mrp.routing.workcenter</field>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
@@ -24,6 +26,7 @@ class MrpProduction(models.Model):
|
|||||||
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
|
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
|
||||||
string='工单状态', default='未排')
|
string='工单状态', default='未排')
|
||||||
|
|
||||||
|
detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告')
|
||||||
# state = fields.Selection(selection_add=[
|
# state = fields.Selection(selection_add=[
|
||||||
# ('pending_scheduling', '待排程'),
|
# ('pending_scheduling', '待排程'),
|
||||||
# ('pending_processing', '待加工'),
|
# ('pending_processing', '待加工'),
|
||||||
@@ -34,9 +37,10 @@ class MrpProduction(models.Model):
|
|||||||
('confirmed', '待排程'),
|
('confirmed', '待排程'),
|
||||||
('pending_cam', '待加工'),
|
('pending_cam', '待加工'),
|
||||||
('progress', '加工中'),
|
('progress', '加工中'),
|
||||||
|
('rework', '返工'),
|
||||||
('to_close', 'To Close'),
|
('to_close', 'To Close'),
|
||||||
('done', 'Done'),
|
('done', 'Done'),
|
||||||
('cancel', 'Cancelled')], string='State',
|
('cancel', '报废')], string='State',
|
||||||
compute='_compute_state', copy=False, index=True, readonly=True,
|
compute='_compute_state', copy=False, index=True, readonly=True,
|
||||||
store=True, tracking=True,
|
store=True, tracking=True,
|
||||||
help=" * Draft: The MO is not confirmed yet.\n"
|
help=" * Draft: The MO is not confirmed yet.\n"
|
||||||
@@ -49,12 +53,15 @@ class MrpProduction(models.Model):
|
|||||||
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
|
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
|
||||||
active = fields.Boolean(string='已归档', default=True)
|
active = fields.Boolean(string='已归档', default=True)
|
||||||
programming_no = fields.Char('编程单号')
|
programming_no = fields.Char('编程单号')
|
||||||
|
reprogramming_num = fields.Integer('重新编程次数', default=0)
|
||||||
work_state = fields.Char('业务状态')
|
work_state = fields.Char('业务状态')
|
||||||
programming_state = fields.Selection(
|
programming_state = fields.Selection(
|
||||||
[('编程中', '编程中'), ('已编程', '已编程')], string='编程状态', tracking=True)
|
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发')], string='编程状态',
|
||||||
|
tracking=True)
|
||||||
glb_file = fields.Binary("glb模型文件")
|
glb_file = fields.Binary("glb模型文件")
|
||||||
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
|
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
|
||||||
plan_start_processing_time = fields.Datetime('计划开始加工时间')
|
plan_start_processing_time = fields.Datetime('计划开始加工时间')
|
||||||
|
is_rework = fields.Boolean(string='是否返工', default=False)
|
||||||
# production_line_state = fields.Selection(
|
# production_line_state = fields.Selection(
|
||||||
# [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
|
# [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
|
||||||
# string='上/下产线', default='待上产线', tracking=True)
|
# string='上/下产线', default='待上产线', tracking=True)
|
||||||
@@ -125,6 +132,15 @@ class MrpProduction(models.Model):
|
|||||||
# if production.state == 'pending_cam':
|
# if production.state == 'pending_cam':
|
||||||
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
||||||
# production.state = 'done'
|
# production.state = 'done'
|
||||||
|
if any(
|
||||||
|
(
|
||||||
|
wo.test_results == '返工' and wo.state == 'done' and production.programming_state == '已编程') or (
|
||||||
|
wo.state == 'rework' and production.programming_state == '编程中') or (
|
||||||
|
wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中',
|
||||||
|
'已编程'])
|
||||||
|
for wo in
|
||||||
|
production.workorder_ids):
|
||||||
|
production.state = 'rework'
|
||||||
|
|
||||||
def action_check(self):
|
def action_check(self):
|
||||||
"""
|
"""
|
||||||
@@ -155,27 +171,63 @@ class MrpProduction(models.Model):
|
|||||||
for production in self:
|
for production in self:
|
||||||
production.maintenance_count = len(production.request_ids)
|
production.maintenance_count = len(production.request_ids)
|
||||||
|
|
||||||
# 制造订单报废:编程单更新
|
# 获取cloud编程单的状态
|
||||||
def updateCNC(self):
|
def _cron_get_programming_state(self):
|
||||||
try:
|
try:
|
||||||
res = {'production_no': self.name, 'programming_no': self.programming_no,
|
if not self:
|
||||||
'order_no': self.origin}
|
reproduction = self.env['mrp.production'].search(
|
||||||
|
[('state', '=', 'rework'), ('programming_state', '=', '编程中')])
|
||||||
|
else:
|
||||||
|
reproduction = self
|
||||||
|
if reproduction:
|
||||||
|
programming_no = [item.programming_no for item in reproduction]
|
||||||
|
programming_no_str = ','.join(programming_no)
|
||||||
|
res = {'programming_no': programming_no_str}
|
||||||
|
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/get_state'
|
||||||
|
config_url = configsettings['sf_url'] + url
|
||||||
|
ret = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||||
|
ret = ret.json()
|
||||||
|
result = json.loads(ret['result'])
|
||||||
|
logging.info('cron_get_programming_state-ret:%s' % result)
|
||||||
|
if result['status'] == 1:
|
||||||
|
for item in result['programming_list']:
|
||||||
|
if not self:
|
||||||
|
production = self.env['mrp.production'].search(
|
||||||
|
[('state', '=', 'rework'), ('programming_no', '=', item['programming_no'])])
|
||||||
|
if production:
|
||||||
|
production.write({'programming_state': '已编程未下发' if item[
|
||||||
|
'programming_state'] == '已编程' else '编程中'})
|
||||||
|
else:
|
||||||
|
return item['programming_state']
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise UserError(ret['message'])
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('cron_get_programming_state error:%s' % e)
|
||||||
|
|
||||||
|
# 编程单更新
|
||||||
|
def update_programming_state(self):
|
||||||
|
try:
|
||||||
|
res = {'programming_no': self.programming_no, 'manufacturing_type': 'rework'}
|
||||||
logging.info('res=%s:' % res)
|
logging.info('res=%s:' % res)
|
||||||
configsettings = self.env['res.config.settings'].get_values()
|
configsettings = self.env['res.config.settings'].get_values()
|
||||||
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
url = '/api/intelligent_programming/update_intelligent_programmings'
|
url = '/api/intelligent_programming/reset_state_again'
|
||||||
config_url = configsettings['sf_url'] + url
|
config_url = configsettings['sf_url'] + url
|
||||||
res['token'] = configsettings['token']
|
ret = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||||
ret = requests.post(config_url, json={}, data=res, headers=config_header)
|
|
||||||
ret = ret.json()
|
ret = ret.json()
|
||||||
logging.info('updateCNC-ret:%s' % ret)
|
result = json.loads(ret['result'])
|
||||||
if ret['status'] == 1:
|
logging.info('update_programming_state-ret:%s' % result)
|
||||||
self.write({'work_state': '已编程'})
|
if result['status'] == 1:
|
||||||
|
self.write({'is_rework': True})
|
||||||
else:
|
else:
|
||||||
raise UserError(ret['message'])
|
raise UserError(ret['message'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.info('updateCNC error:%s' % e)
|
logging.info('update_programming_state error:%s' % e)
|
||||||
raise UserError("更新程单失败,请联系管理员")
|
raise UserError("更新编程单状态失败,请联系管理员")
|
||||||
|
|
||||||
# cnc程序获取
|
# cnc程序获取
|
||||||
def fetchCNC(self, production_names):
|
def fetchCNC(self, production_names):
|
||||||
@@ -201,7 +253,7 @@ class MrpProduction(models.Model):
|
|||||||
'material_type_code': self.env['sf.materials.model'].search(
|
'material_type_code': self.env['sf.materials.model'].search(
|
||||||
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
|
[('id', '=', cnc.product_id.materials_type_id.id)]).materials_no,
|
||||||
'machining_processing_panel': cnc.product_id.model_processing_panel,
|
'machining_processing_panel': cnc.product_id.model_processing_panel,
|
||||||
'machining_precision': cnc.product_id.model_machining_precision,
|
'machining_precision': '',
|
||||||
'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length,
|
'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_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,
|
'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width,
|
||||||
@@ -524,70 +576,90 @@ class MrpProduction(models.Model):
|
|||||||
|
|
||||||
def _reset_work_order_sequence(self):
|
def _reset_work_order_sequence(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
sequence_list = {}
|
workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework'))
|
||||||
# 产品模型类型
|
# 产品模型类型
|
||||||
model_type_id = rec.product_id.product_model_type_id
|
model_type_id = rec.product_id.product_model_type_id
|
||||||
# 产品加工面板
|
# 产品加工面板
|
||||||
model_processing_panel = rec.product_id.model_processing_panel
|
model_processing_panel = rec.product_id.model_processing_panel
|
||||||
if model_type_id:
|
if not workorder_ids:
|
||||||
if model_processing_panel:
|
sequence_list = {}
|
||||||
tmpl_num = 1
|
if model_type_id:
|
||||||
panel_list = model_processing_panel.split(',')
|
if model_processing_panel:
|
||||||
for panel in panel_list:
|
tmpl_num = 1
|
||||||
panel_sequence_list = {}
|
panel_list = model_processing_panel.split(',')
|
||||||
# 成品工序
|
for panel in panel_list:
|
||||||
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
panel_sequence_list = {}
|
||||||
if product_routing_tmpl_ids:
|
# 成品工序
|
||||||
for tmpl_id in product_routing_tmpl_ids:
|
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
||||||
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
if product_routing_tmpl_ids:
|
||||||
tmpl_num += 1
|
for tmpl_id in product_routing_tmpl_ids:
|
||||||
sequence_list.update({panel: panel_sequence_list})
|
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||||
# 表面工艺工序
|
tmpl_num += 1
|
||||||
# 模型类型的表面工艺工序模版
|
sequence_list.update({panel: panel_sequence_list})
|
||||||
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
|
# 表面工艺工序
|
||||||
# 产品选择的表面工艺
|
# 模型类型的表面工艺工序模版
|
||||||
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
|
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
|
||||||
process_dict = {}
|
# 产品选择的表面工艺
|
||||||
if model_process_parameters_ids:
|
model_process_parameters_ids = rec.product_id.model_process_parameters_ids
|
||||||
for process_parameters_id in model_process_parameters_ids:
|
process_dict = {}
|
||||||
process_id = process_parameters_id.process_id
|
if model_process_parameters_ids:
|
||||||
for surface_tmpl_id in surface_tmpl_ids:
|
for process_parameters_id in model_process_parameters_ids:
|
||||||
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
|
process_id = process_parameters_id.process_id
|
||||||
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
|
for surface_tmpl_id in surface_tmpl_ids:
|
||||||
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
|
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
|
||||||
surface_tmpl_name, process_parameters_id.name)})
|
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
|
||||||
process_list = sorted(process_dict.keys())
|
process_dict.update({int(process_id.category_id.code): '%s-%s' % (
|
||||||
for process_num in process_list:
|
surface_tmpl_name, process_parameters_id.name)})
|
||||||
sequence_list.update({process_dict.get(process_num): tmpl_num})
|
process_list = sorted(process_dict.keys())
|
||||||
tmpl_num += 1
|
for process_num in process_list:
|
||||||
# 坯料工序
|
sequence_list.update({process_dict.get(process_num): tmpl_num})
|
||||||
tmpl_num = 1
|
|
||||||
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
|
|
||||||
if embryo_routing_tmpl_ids:
|
|
||||||
for tmpl_id in embryo_routing_tmpl_ids:
|
|
||||||
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
|
||||||
tmpl_num += 1
|
tmpl_num += 1
|
||||||
|
# 坯料工序
|
||||||
|
tmpl_num = 1
|
||||||
|
embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids
|
||||||
|
if embryo_routing_tmpl_ids:
|
||||||
|
for tmpl_id in embryo_routing_tmpl_ids:
|
||||||
|
sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num})
|
||||||
|
tmpl_num += 1
|
||||||
|
else:
|
||||||
|
raise ValidationError('该产品【加工面板】为空!')
|
||||||
else:
|
else:
|
||||||
raise ValidationError('该产品【加工面板】为空!')
|
raise ValidationError('该产品没有选择【模版类型】!')
|
||||||
|
|
||||||
else:
|
for work in rec.workorder_ids:
|
||||||
raise ValidationError('该产品没有选择【模版类型】!')
|
if sequence_list.get(work.name):
|
||||||
|
work.sequence = sequence_list[work.name]
|
||||||
for work in rec.workorder_ids:
|
elif sequence_list.get(work.processing_panel):
|
||||||
if sequence_list.get(work.name):
|
processing_panel = sequence_list.get(work.processing_panel)
|
||||||
work.sequence = sequence_list[work.name]
|
if processing_panel.get(work.name):
|
||||||
elif sequence_list.get(work.processing_panel):
|
work.sequence = processing_panel[work.name]
|
||||||
processing_panel = sequence_list.get(work.processing_panel)
|
else:
|
||||||
if processing_panel.get(work.name):
|
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
||||||
work.sequence = processing_panel[work.name]
|
|
||||||
else:
|
else:
|
||||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
||||||
else:
|
# 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排
|
||||||
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
|
elif rec.workorder_ids.filtered(lambda item: item.sequence == 0):
|
||||||
# if work.name == '获取CNC加工程序':
|
# 获取新增的返工工单
|
||||||
# work.button_start()
|
work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0)
|
||||||
# #work.fetchCNC()
|
# 获取当前返工面最后一个工单工序
|
||||||
# work.button_finish()
|
sequence_max = sorted(
|
||||||
|
rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel),
|
||||||
|
key=lambda item: item.sequence, reverse=True)[0].sequence
|
||||||
|
# 对当前返工工单之后的工单工序进行重排
|
||||||
|
work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max)
|
||||||
|
for work_id in work_order_ids:
|
||||||
|
work_id.sequence = work_id.sequence + 3
|
||||||
|
# 生成新增的返工工单的工序
|
||||||
|
# 成品工序
|
||||||
|
panel_sequence_list = {}
|
||||||
|
product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids
|
||||||
|
if product_routing_tmpl_ids:
|
||||||
|
for tmpl_id in product_routing_tmpl_ids:
|
||||||
|
sequence_max += 1
|
||||||
|
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max})
|
||||||
|
for work_id in work_ids:
|
||||||
|
if panel_sequence_list.get(work_id.name):
|
||||||
|
work_id.sequence = panel_sequence_list[work_id.name]
|
||||||
|
|
||||||
# 创建工单并进行排序
|
# 创建工单并进行排序
|
||||||
def _create_workorder(self, item):
|
def _create_workorder(self, item):
|
||||||
@@ -688,3 +760,180 @@ class MrpProduction(models.Model):
|
|||||||
'view_mode': 'tree,form',
|
'view_mode': 'tree,form',
|
||||||
})
|
})
|
||||||
return action
|
return action
|
||||||
|
|
||||||
|
# 返工
|
||||||
|
def button_rework(self):
|
||||||
|
cloud_programming_state = None
|
||||||
|
if self.programming_state != '已编程' and self.reprogramming_num >= 1:
|
||||||
|
cloud_programming_state = self._cron_get_programming_state()
|
||||||
|
logging.info('cloud_programming_state:%s' % cloud_programming_state)
|
||||||
|
logging.info('programming_state:%s' % self.programming_state)
|
||||||
|
return {
|
||||||
|
'name': _('返工'),
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_model': 'sf.rework.wizard',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_production_id': self.id,
|
||||||
|
'default_product_id': self.product_id.id,
|
||||||
|
'default_programming_state': self.programming_state if cloud_programming_state is not None else cloud_programming_state,
|
||||||
|
'default_is_reprogramming': False if (cloud_programming_state in ['编程中',
|
||||||
|
'待编程'] and self.programming_state in [
|
||||||
|
'编程中'])
|
||||||
|
or (cloud_programming_state in [
|
||||||
|
'已编程'] and self.programming_state in ['已编程未下发']) else True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 报废
|
||||||
|
def button_scrap_new(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 更新程序
|
||||||
|
def do_update_program(self):
|
||||||
|
program_production = self
|
||||||
|
if len(program_production) >= 1:
|
||||||
|
same_product_id = None
|
||||||
|
is_not_same_product = 0
|
||||||
|
for item in program_production:
|
||||||
|
if same_product_id is None:
|
||||||
|
same_product_id = item.product_id
|
||||||
|
if item.product_id != same_product_id:
|
||||||
|
is_not_same_product += 1
|
||||||
|
if item.state != "rework" and item.programming_state != "已编程未下发":
|
||||||
|
raise UserError("请选择状态为返工且已编程未下发的制造订单")
|
||||||
|
if is_not_same_product >= 1:
|
||||||
|
raise UserError("您选择的记录中含有其他产品的制造订单,请选择同一产品的制造订单")
|
||||||
|
grouped_program_ids = {k: list(g) for k, g in groupby(program_production, key=lambda x: x.programming_no)}
|
||||||
|
program_to_production_names = {}
|
||||||
|
for programming_no, program_production in grouped_program_ids.items():
|
||||||
|
program_to_production_names[programming_no] = [production.name for production in program_production]
|
||||||
|
for production in self:
|
||||||
|
if production.programming_no in program_to_production_names:
|
||||||
|
rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework')
|
||||||
|
if rework_workorder:
|
||||||
|
new_pancel_workorder = production.workorder_ids.filtered(
|
||||||
|
lambda m1: m1.state != 'rework' and m1.processing_panel == rework_workorder[0].processing_panel)
|
||||||
|
if not new_pancel_workorder.cnc_ids:
|
||||||
|
production.get_new_program(rework_workorder[0].processing_panel)
|
||||||
|
production.write({'state': 'progress', 'programming_state': '已下发'})
|
||||||
|
|
||||||
|
# 从cloud获取重新编程过的最新程序
|
||||||
|
def get_new_program(self, processing_panel):
|
||||||
|
try:
|
||||||
|
res = {'programming_no': self.programming_no, 'processing_panel': processing_panel}
|
||||||
|
configsettings = self.env['res.config.settings'].get_values()
|
||||||
|
config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||||
|
url = '/api/intelligent_programming/get_new_program'
|
||||||
|
config_url = configsettings['sf_url'] + url
|
||||||
|
r = requests.post(config_url, json=res, data=None, headers=config_header)
|
||||||
|
r = r.json()
|
||||||
|
result = json.loads(r['result'])
|
||||||
|
if result['status'] == 1:
|
||||||
|
# program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel)
|
||||||
|
# if os.path.exists(program_path_tmp_panel):
|
||||||
|
# files_r = os.listdir(program_path_tmp_panel)
|
||||||
|
# if files_r:
|
||||||
|
# for file_name in files_r:
|
||||||
|
# file_path = os.path.join(program_path_tmp_panel, file_name)
|
||||||
|
# os.remove(file_path)
|
||||||
|
# download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'],
|
||||||
|
# processing_panel)
|
||||||
|
# if download_state is False:
|
||||||
|
# raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no))
|
||||||
|
productions = self.env['mrp.production'].search(
|
||||||
|
[('programming_no', '=', self.programming_no), ('state', 'not in', ['cancel,done'])])
|
||||||
|
if productions:
|
||||||
|
panel_workorder = productions.workorder_ids.filtered(
|
||||||
|
lambda
|
||||||
|
ac: ac.processing_panel == processing_panel and ac.routing_type == 'CNC加工' and ac.state != 'rework')
|
||||||
|
if panel_workorder:
|
||||||
|
if panel_workorder.cnc_ids:
|
||||||
|
panel_workorder.cmm_ids.sudo().unlink()
|
||||||
|
panel_workorder.cnc_ids.sudo().unlink()
|
||||||
|
self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
|
||||||
|
productions)
|
||||||
|
program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
||||||
|
processing_panel)
|
||||||
|
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
|
||||||
|
files_panel = os.listdir(program_path_tmp_panel)
|
||||||
|
if files_panel:
|
||||||
|
for file in files_panel:
|
||||||
|
file_extension = os.path.splitext(file)[1]
|
||||||
|
if file_extension.lower() == '.pdf':
|
||||||
|
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
||||||
|
logging.info('panel_file_path:%s' % panel_file_path)
|
||||||
|
panel_workorder.write(
|
||||||
|
{'cnc_ids': panel_workorder.cnc_ids.sudo()._json_cnc_processing(processing_panel, result),
|
||||||
|
'cmm_ids': panel_workorder.cmm_ids.sudo()._json_cmm_program(processing_panel, result),
|
||||||
|
'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
|
pre_workorder = productions.workorder_ids.filtered(lambda
|
||||||
|
ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state != 'rework')
|
||||||
|
if pre_workorder:
|
||||||
|
pre_workorder.write(
|
||||||
|
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
|
else:
|
||||||
|
raise UserError(result['message'])
|
||||||
|
except Exception as e:
|
||||||
|
logging.info('get_new_program error:%s' % e)
|
||||||
|
raise UserError("从云平台获取最新程序失败,请联系管理员")
|
||||||
|
|
||||||
|
|
||||||
|
class sf_detection_result(models.Model):
|
||||||
|
_name = 'sf.detection.result'
|
||||||
|
_description = "检测结果"
|
||||||
|
|
||||||
|
production_id = fields.Many2one('mrp.production')
|
||||||
|
processing_panel = fields.Char('加工面')
|
||||||
|
routing_type = fields.Selection([
|
||||||
|
('装夹预调', '装夹预调'),
|
||||||
|
('CNC加工', 'CNC加工')], string="工序类型")
|
||||||
|
|
||||||
|
rework_reason = fields.Selection(
|
||||||
|
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
|
||||||
|
("operate computer", "操机"),
|
||||||
|
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
|
||||||
|
detailed_reason = fields.Text('详细原因')
|
||||||
|
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")],
|
||||||
|
string="检测结果", tracking=True)
|
||||||
|
test_report = fields.Binary('检测报告', readonly=True)
|
||||||
|
handle_result = fields.Selection([("待处理", "待处理"), ("已处理", "已处理")], default='', string="处理结果",
|
||||||
|
tracking=True)
|
||||||
|
|
||||||
|
# 查看检测报告
|
||||||
|
def button_look_test_report(self):
|
||||||
|
return {
|
||||||
|
'res_model': 'sf.detection.result',
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_id': self.id,
|
||||||
|
'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')],
|
||||||
|
# 'view_mode': 'form',
|
||||||
|
# 'context': {
|
||||||
|
# 'default_id': self.id
|
||||||
|
# },
|
||||||
|
'target': 'new'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class sf_processing_panel(models.Model):
|
||||||
|
_name = 'sf.processing.panel'
|
||||||
|
_description = "加工面"
|
||||||
|
|
||||||
|
name = fields.Char('加工面')
|
||||||
|
active = fields.Boolean('有效', default=True)
|
||||||
|
|
||||||
|
# @api.model
|
||||||
|
# def name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||||
|
# if self.env.user.has_group('sf_base.group_sf_order_user'):
|
||||||
|
# if self._context.get('product_id'):
|
||||||
|
# product = self.env['product.product'].search([('id', '=', self._context.get('product_id'))])
|
||||||
|
# if product:
|
||||||
|
# panel = self.env['sf.processing.panel'].search([('name', 'ilike', 'ZM')])
|
||||||
|
# if panel:
|
||||||
|
# ids = [t.id for t in panel]
|
||||||
|
# domain = [('id', 'in', ids)]
|
||||||
|
# else:
|
||||||
|
# domain = [('id', '=', False)]
|
||||||
|
# return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||||
|
# return super()._name_search(name, args, operator, limit, name_get_uid)
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
('切割', '切割'), ('表面工艺', '表面工艺')
|
('切割', '切割'), ('表面工艺', '表面工艺')
|
||||||
], string="工序类型")
|
], string="工序类型")
|
||||||
results = fields.Char('结果')
|
results = fields.Char('结果')
|
||||||
|
state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
|
||||||
|
|
||||||
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
||||||
|
|
||||||
@@ -197,13 +198,14 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
production_line_state = fields.Selection(
|
production_line_state = fields.Selection(
|
||||||
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
|
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
|
||||||
string='上/下产线', default='待上产线', tracking=True)
|
string='上/下产线', default='待上产线', tracking=True)
|
||||||
detection_report = fields.Binary('检测报告', readonly=True)
|
detection_report = fields.Binary('检测报告', readonly=False)
|
||||||
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False)
|
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False)
|
||||||
is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False)
|
is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False)
|
||||||
reason = fields.Selection(
|
reason = fields.Selection(
|
||||||
[("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"),
|
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
|
||||||
("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True)
|
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
|
||||||
detailed_reason = fields.Text('详细原因')
|
detailed_reason = fields.Text('详细原因')
|
||||||
|
is_rework = fields.Boolean(string='是否返工', default=False)
|
||||||
|
|
||||||
# is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False)
|
# is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False)
|
||||||
|
|
||||||
@@ -431,6 +433,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
work.compensation_value_y = eval(self.material_center_point)[1]
|
work.compensation_value_y = eval(self.material_center_point)[1]
|
||||||
# work.process_state = '待加工'
|
# work.process_state = '待加工'
|
||||||
# self.sudo().production_id.process_state = '待加工'
|
# self.sudo().production_id.process_state = '待加工'
|
||||||
|
# self.sudo().production_id.process_state = '待加工'
|
||||||
self.date_finished = datetime.now()
|
self.date_finished = datetime.now()
|
||||||
workorder.button_finish()
|
workorder.button_finish()
|
||||||
|
|
||||||
@@ -461,6 +464,19 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
else:
|
else:
|
||||||
raise UserError(_("该工单暂未完成,无法进行工件配送"))
|
raise UserError(_("该工单暂未完成,无法进行工件配送"))
|
||||||
|
|
||||||
|
def button_rework_pre(self):
|
||||||
|
return {
|
||||||
|
'name': _('返工'),
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'view_mode': 'form',
|
||||||
|
'res_model': 'sf.rework.wizard',
|
||||||
|
'target': 'new',
|
||||||
|
'context': {
|
||||||
|
'default_workorder_id': self.id,
|
||||||
|
'default_production_id': self.production_id.id,
|
||||||
|
'default_routing_type': self.routing_type
|
||||||
|
}}
|
||||||
|
|
||||||
# 拼接工单对象属性值
|
# 拼接工单对象属性值
|
||||||
def json_workorder_str(self, k, production, route, item):
|
def json_workorder_str(self, k, production, route, item):
|
||||||
# 计算预计时长duration_expected
|
# 计算预计时长duration_expected
|
||||||
@@ -804,28 +820,67 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
}]
|
}]
|
||||||
return workorders_values_str
|
return workorders_values_str
|
||||||
|
|
||||||
# @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
|
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state')
|
||||||
# def _compute_state(self):
|
def _compute_state(self):
|
||||||
# super(ResMrpWorkOrder, self)._compute_state()
|
super()._compute_state()
|
||||||
# for item in self:
|
for workorder in self:
|
||||||
# print(item.name)
|
re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
|
||||||
# print(item.state)
|
('processing_panel', '=', workorder.processing_panel),
|
||||||
# print(item.is_remanufacture)
|
('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
|
||||||
# scrap_workorder = self.env['mrp.workorder'].search(
|
logging.info('re_work:%s' % re_work.state)
|
||||||
# [('production_id', '=', item.production_id.id), ('routing_type', '=', 'CNC加工'),
|
if workorder.state not in ['cancel', 'progress', 'rework']:
|
||||||
# ('state', '=', 'done'), ('test_results', 'in', ['返工', '报废'])])
|
logging.info('工序:%s' % workorder.routing_type)
|
||||||
# print(scrap_workorder)
|
logging.info('状态:%s' % workorder.state)
|
||||||
# # if item.routing_type == 'CNC加工' and item.state in ['done'] and item.test_results in ['返工', '报废']:
|
logging.info('is_rework:%s' % workorder.is_rework)
|
||||||
# if item.routing_type == '解除装夹':
|
logging.info('面:%s' % workorder.processing_panel)
|
||||||
# if scrap_workorder and item.state not in ['cancel']:
|
logging.info('编程状态:%s' % workorder.production_id.programming_state)
|
||||||
# item.state = 'cancel'
|
logging.info('制造状态:%s' % workorder.production_id.state)
|
||||||
# elif item.routing_type == '表面工艺':
|
# if workorder.state =='done' and workorder.is_rework is True:
|
||||||
# if scrap_workorder:
|
# workorder.state = 'rework'
|
||||||
# stock_move = self.env['stock.move'].search(
|
# else:
|
||||||
# [('origin', '=', item.production_id.name)])
|
# if re_work:
|
||||||
# stock_move.write({'state': 'cancel'})
|
# workorder.state = 'rework'
|
||||||
# item.picking_ids.write({'state': 'cancel'})
|
if workorder.production_id.state == 'rework':
|
||||||
# item.state = 'cancel'
|
if (workorder.routing_type == 'CNC加工' and workorder.state == 'done') or (
|
||||||
|
workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '编程中' and re_work):
|
||||||
|
logging.info('面111:%s' % workorder.processing_panel)
|
||||||
|
workorder.state = 'waiting'
|
||||||
|
elif workorder.production_id.state == 'progress':
|
||||||
|
logging.info('面222:%s' % workorder.processing_panel)
|
||||||
|
if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已下发' and re_work:
|
||||||
|
workorder.state = 'ready'
|
||||||
|
# else:
|
||||||
|
# if workorder.state not in ['cancel', 'rework']:
|
||||||
|
# workorder.state = 'rework'
|
||||||
|
# elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress',
|
||||||
|
# 'rework']:
|
||||||
|
# per_work = self.env['mrp.workorder'].search(
|
||||||
|
# [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id),
|
||||||
|
# ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)])
|
||||||
|
# if per_work:
|
||||||
|
# workorder.state = 'waiting'
|
||||||
|
# if workorder.routing_type == 'CNC加工' and workorder.state == 'progress':
|
||||||
|
# workorder.state = 'to be detected'
|
||||||
|
# for workorder in self:
|
||||||
|
# if workorder.is_rework is True and workorder.state == 'done':
|
||||||
|
# cnc_work = self.env['mrp.workorder'].search([('routing_type','=','CNC加工'),('production_id','=',workorder.production_id.id)])
|
||||||
|
# if cnc_work:
|
||||||
|
# cnc_work.state = 'waiting'
|
||||||
|
# if workorder.state == 'pending':
|
||||||
|
# if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
|
||||||
|
# workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting'
|
||||||
|
# continue
|
||||||
|
# if workorder.state not in ('waiting', 'ready'):
|
||||||
|
# continue
|
||||||
|
# if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]):
|
||||||
|
# workorder.state = 'pending'
|
||||||
|
# continue
|
||||||
|
# if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'):
|
||||||
|
# continue
|
||||||
|
# if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting':
|
||||||
|
# workorder.state = 'ready'
|
||||||
|
# elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready':
|
||||||
|
# workorder.state = 'waiting'
|
||||||
|
|
||||||
# 重写工单开始按钮方法
|
# 重写工单开始按钮方法
|
||||||
def button_start(self):
|
def button_start(self):
|
||||||
@@ -942,6 +997,15 @@ 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 = '待解除装夹'
|
||||||
|
if record.test_results in ['返工']:
|
||||||
|
record.production_id.write({'detection_result_ids': [(0, 0, {
|
||||||
|
'rework_reason': record.reason,
|
||||||
|
'detailed_reason': record.detailed_reason,
|
||||||
|
'processing_panel': record.processing_panel,
|
||||||
|
'routing_type': record.routing_type,
|
||||||
|
'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '',
|
||||||
|
'test_results': record.test_results,
|
||||||
|
'test_report': record.detection_report})]})
|
||||||
if record.routing_type == '解除装夹':
|
if record.routing_type == '解除装夹':
|
||||||
'''
|
'''
|
||||||
记录结束时间
|
记录结束时间
|
||||||
@@ -990,10 +1054,7 @@ class ResMrpWorkOrder(models.Model):
|
|||||||
record.write({
|
record.write({
|
||||||
'date_planned_finished': tem_date_planned_finished # 保持原值
|
'date_planned_finished': tem_date_planned_finished # 保持原值
|
||||||
})
|
})
|
||||||
# if record.routing_type == 'CNC加工':
|
|
||||||
# record.write({
|
|
||||||
# 'date_finished': tem_date_finished # 保持原值
|
|
||||||
# })
|
|
||||||
# if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']:
|
# if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']:
|
||||||
# record.production_id.action_cancel()
|
# record.production_id.action_cancel()
|
||||||
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
|
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
|
||||||
@@ -1163,24 +1224,25 @@ class CNCprocessing(models.Model):
|
|||||||
|
|
||||||
def _json_cnc_processing(self, panel, ret):
|
def _json_cnc_processing(self, panel, ret):
|
||||||
cnc_processing = []
|
cnc_processing = []
|
||||||
for item in ret['programming_list']:
|
if ret is not False:
|
||||||
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
|
for item in ret['programming_list']:
|
||||||
cnc_processing.append((0, 0, {
|
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
|
||||||
'sequence_number': item['sequence_number'],
|
cnc_processing.append((0, 0, {
|
||||||
'program_name': item['program_name'],
|
'sequence_number': item['sequence_number'],
|
||||||
'cutting_tool_name': item['cutting_tool_name'],
|
'program_name': item['program_name'],
|
||||||
'cutting_tool_no': item['cutting_tool_no'],
|
'cutting_tool_name': item['cutting_tool_name'],
|
||||||
'processing_type': item['processing_type'],
|
'cutting_tool_no': item['cutting_tool_no'],
|
||||||
'margin_x_y': item['margin_x_y'],
|
'processing_type': item['processing_type'],
|
||||||
'margin_z': item['margin_z'],
|
'margin_x_y': item['margin_x_y'],
|
||||||
'depth_of_processing_z': item['depth_of_processing_z'],
|
'margin_z': item['margin_z'],
|
||||||
'cutting_tool_extension_length': item['cutting_tool_extension_length'],
|
'depth_of_processing_z': item['depth_of_processing_z'],
|
||||||
'cutting_tool_handle_type': item['cutting_tool_handle_type'],
|
'cutting_tool_extension_length': item['cutting_tool_extension_length'],
|
||||||
'estimated_processing_time': item['estimated_processing_time'],
|
'cutting_tool_handle_type': item['cutting_tool_handle_type'],
|
||||||
'program_path': item['ftp_path'],
|
'estimated_processing_time': item['estimated_processing_time'],
|
||||||
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
|
'program_path': item['ftp_path'],
|
||||||
'remark': item['remark']
|
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
|
||||||
}))
|
'remark': item['remark']
|
||||||
|
}))
|
||||||
return cnc_processing
|
return cnc_processing
|
||||||
|
|
||||||
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
|
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
|
||||||
@@ -1636,12 +1698,13 @@ class CMMprogram(models.Model):
|
|||||||
|
|
||||||
def _json_cmm_program(self, panel, ret):
|
def _json_cmm_program(self, panel, ret):
|
||||||
cmm_program = []
|
cmm_program = []
|
||||||
for item in ret['programming_list']:
|
if ret is not False:
|
||||||
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
|
for item in ret['programming_list']:
|
||||||
cmm_program.append((0, 0, {
|
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
|
||||||
'sequence_number': 1,
|
cmm_program.append((0, 0, {
|
||||||
'program_name': item['program_name'],
|
'sequence_number': 1,
|
||||||
'program_path': item['ftp_path'],
|
'program_name': item['program_name'],
|
||||||
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
|
'program_path': item['ftp_path'],
|
||||||
}))
|
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
|
||||||
|
}))
|
||||||
return cmm_program
|
return cmm_program
|
||||||
|
|||||||
@@ -141,3 +141,8 @@ access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,m
|
|||||||
access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0
|
access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0
|
||||||
access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0
|
access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0
|
||||||
|
|
||||||
|
access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||||
|
access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0
|
||||||
|
access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_order_user,model_sf_processing_panel,sf_base.group_sf_order_user,1,1,1,0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
@@ -1,11 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<record id="custom_mrp_production_tree_view" model="ir.ui.view">
|
<record id="custom_mrp_production_tree_view" model="ir.ui.view">
|
||||||
<field name="name">custom.mrp.production.tree</field>
|
<field name="name">custom.mrp.production.tree</field>
|
||||||
<field name="model">mrp.production</field>
|
<field name="model">mrp.production</field>
|
||||||
<field name="inherit_id" ref="mrp.mrp_production_tree_view"/>
|
<field name="inherit_id" ref="mrp.mrp_production_tree_view"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//button[@name='do_unreserve']" position="after">
|
||||||
|
<button name="do_update_program" type="object" string="更新程序"
|
||||||
|
groups="sf_base.group_sf_mrp_user"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//field[@name='product_id']" position="replace"/>
|
<xpath expr="//field[@name='product_id']" position="replace"/>
|
||||||
<xpath expr="//field[@name='product_qty']" position="replace"/>
|
<xpath expr="//field[@name='product_qty']" position="replace"/>
|
||||||
<xpath expr="//field[@name='product_uom_id']" position="replace"/>
|
<xpath expr="//field[@name='product_uom_id']" position="replace"/>
|
||||||
@@ -77,8 +82,10 @@
|
|||||||
<xpath expr="//sheet//group//group//div[3]" position="after">
|
<xpath expr="//sheet//group//group//div[3]" position="after">
|
||||||
<field name="manual_quotation" readonly="1"/>
|
<field name="manual_quotation" readonly="1"/>
|
||||||
<field name="programming_no" readonly="1"/>
|
<field name="programming_no" readonly="1"/>
|
||||||
<field name="programming_state" readonly="1" decoration-success="programming_state == '已编程'"
|
<field name="programming_state" readonly="1"
|
||||||
decoration-warning="programming_state =='编程中'"/>
|
decoration-success="programming_state == '已编程'"
|
||||||
|
decoration-warning="programming_state =='编程中'"
|
||||||
|
decoration-danger="programming_state =='已编程未下发'"/>
|
||||||
<field name="work_state" invisible="1"/>
|
<field name="work_state" invisible="1"/>
|
||||||
<field name="schedule_state" invisible='1'/>
|
<field name="schedule_state" invisible='1'/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -87,7 +94,7 @@
|
|||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='user_id']" position="after">
|
<xpath expr="//field[@name='user_id']" position="after">
|
||||||
<field name="production_line_id" readonly="1"/>
|
<field name="production_line_id" readonly="1"/>
|
||||||
<!-- <field name="production_line_state" readonly="1"/>-->
|
<!-- <field name="production_line_state" readonly="1"/>-->
|
||||||
<field name="part_number" string="成品的零件图号"/>
|
<field name="part_number" string="成品的零件图号"/>
|
||||||
<field name="part_drawing"/>
|
<field name="part_drawing"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -106,9 +113,19 @@
|
|||||||
<xpath expr="(//header//button[@name='button_mark_done'])[2]" position="replace">
|
<xpath expr="(//header//button[@name='button_mark_done'])[2]" position="replace">
|
||||||
<button name="button_mark_done"
|
<button name="button_mark_done"
|
||||||
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '=', [])]}"
|
attrs="{'invisible': ['|', '|', ('state', 'in', ('draft', 'cancel', 'done', 'to_close')), ('qty_producing', '=', 0), ('move_raw_ids', '=', [])]}"
|
||||||
string="验证" type="object" class="oe_highlight" data-hotkey="g"
|
string="验证" type="object" data-hotkey="g"
|
||||||
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">
|
||||||
|
<button name="button_scrap" invisible="1"/>
|
||||||
|
<button name="do_update_program" string="更新程序" type="object" groups="sf_base.group_sf_mrp_user"
|
||||||
|
confirm="是否确认更新程序"
|
||||||
|
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
|
||||||
|
<button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user"
|
||||||
|
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程')]}"/>
|
||||||
|
<button name="button_scrap_new" string="报废" type="object" groups="sf_base.group_sf_mrp_user"
|
||||||
|
confirm="是否确认报废" attrs="{'invisible': [('state', '!=', 'cancel')]}"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace">
|
<xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace">
|
||||||
<button name="button_mark_done" attrs="{'invisible': [
|
<button name="button_mark_done" attrs="{'invisible': [
|
||||||
'|',
|
'|',
|
||||||
@@ -138,11 +155,11 @@
|
|||||||
attrs="{'invisible': ['|', ('is_planned', '=', False), ('state', '=', 'cancel')]}"
|
attrs="{'invisible': ['|', ('is_planned', '=', False), ('state', '=', 'cancel')]}"
|
||||||
data-hotkey="x" groups="sf_base.group_sf_mrp_user"/>
|
data-hotkey="x" 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" type="object" string="报废"
|
<!-- <button name="button_scrap" type="object" string="报废"-->
|
||||||
attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="y"
|
<!-- attrs="{'invisible': [('state', 'in', ('cancel', 'draft'))]}" data-hotkey="y"-->
|
||||||
groups="sf_base.group_sf_mrp_user"/>
|
<!-- groups="sf_base.group_sf_mrp_user"/>-->
|
||||||
</xpath>
|
<!-- </xpath>-->
|
||||||
|
|
||||||
|
|
||||||
<xpath expr="//header//button[@name='action_confirm']" position="replace">
|
<xpath expr="//header//button[@name='action_confirm']" position="replace">
|
||||||
@@ -259,6 +276,32 @@
|
|||||||
[])]}
|
[])]}
|
||||||
</attribute>
|
</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//sheet//notebook//page[@name='operations']" position="after">
|
||||||
|
<page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}">
|
||||||
|
<field name="detection_result_ids" string="" readonly="0">
|
||||||
|
<tree sample="1">
|
||||||
|
<field name="production_id" invisible="1"/>
|
||||||
|
<field name="processing_panel"/>
|
||||||
|
<field name="routing_type"/>
|
||||||
|
<field name="rework_reason"/>
|
||||||
|
<field name="detailed_reason"/>
|
||||||
|
<field name="test_results"/>
|
||||||
|
<field name="handle_result"/>
|
||||||
|
<field name="test_report" invisible="1"/>
|
||||||
|
<button name="button_look_test_report" string="查看测试报告" type="object"
|
||||||
|
attrs="{'invisible': [('test_report', '=', False)]}"
|
||||||
|
class="btn btn-primary me-1"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//sheet//notebook//page[@name='components']" position="attributes">
|
||||||
|
<attribute name="string">投料</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='components_availability']" position="attributes">
|
||||||
|
<attribute name="string">投料状态</attribute>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
@@ -280,6 +323,16 @@
|
|||||||
<field name="model">mrp.workorder</field>
|
<field name="model">mrp.workorder</field>
|
||||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
|
<field name="inherit_id" ref="mrp.mrp_production_workorder_tree_editable_view"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//tree" position="attributes">
|
||||||
|
<attribute name="default_order">sequence</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='state']" position="replace">
|
||||||
|
<field name="state" widget="badge" decoration-warning="state == 'progress'"
|
||||||
|
decoration-success="state == 'done'" decoration-danger="state in ('cancel','rework')"
|
||||||
|
decoration-muted="state == 'to be detected'"
|
||||||
|
decoration-info="state not in ('progress', 'done', 'cancel','rework','to be detected')"
|
||||||
|
attrs="{'invisible': [('production_state', '=', 'draft')], 'column_invisible': [('parent.state', '=', 'draft')]}"/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//tree//button[@name='button_start']" position="replace">
|
<xpath expr="//tree//button[@name='button_start']" position="replace">
|
||||||
<field name="routing_type" invisible="True"/>
|
<field name="routing_type" invisible="True"/>
|
||||||
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
|
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始?"
|
||||||
@@ -397,6 +450,19 @@
|
|||||||
<field name="model">mrp.production</field>
|
<field name="model">mrp.production</field>
|
||||||
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
|
<field name="inherit_id" ref="mrp.view_mrp_production_filter"/>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//filter[@name='filter_in_progress']" position="before">
|
||||||
|
<filter string="返工" name="filter_rework" domain="[('state', '=', 'rework')]"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//filter[@name='planning_issues']" position="before">
|
||||||
|
<separator/>
|
||||||
|
<filter name="filter_programming" string="编程中"
|
||||||
|
domain="[('programming_state', '=', '编程中')]"/>
|
||||||
|
<filter name="filter_programmed" string="已编程"
|
||||||
|
domain="[('programming_state', '=', '已编程')]"/>
|
||||||
|
<filter name="filter_programmed_not_delivered" string="已编程未下发"
|
||||||
|
domain="[('programming_state', '=', '已编程未下发')]"/>
|
||||||
|
<separator/>
|
||||||
|
</xpath>
|
||||||
<xpath expr="//search" position="inside">
|
<xpath expr="//search" position="inside">
|
||||||
<searchpanel class="account_root">
|
<searchpanel class="account_root">
|
||||||
<field name="state" icon="fa-filter" enable_counters="1"/>
|
<field name="state" icon="fa-filter" enable_counters="1"/>
|
||||||
@@ -482,5 +548,72 @@
|
|||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="sf_test_report_form">
|
||||||
|
<field name="name">sf.detection.result</field>
|
||||||
|
<field name="model">sf.detection.result</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<group>
|
||||||
|
<field name="handle_result"/>
|
||||||
|
<field name="test_report" readonly="1" widget="pdf_viewer"/>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="action_test_report_form" model="ir.actions.act_window">
|
||||||
|
<field name="name">检测报告</field>
|
||||||
|
<field name="res_model">sf.detection.result</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
</record>
|
||||||
|
<!-- <menuitem action="mrp.mrp_production_action"-->
|
||||||
|
<!-- id="mrp_production_action"-->
|
||||||
|
<!-- sequence="1" active="False"/>-->
|
||||||
|
|
||||||
|
<!-- <!– <record id="mrp_production_action_sf" model="ir.actions.act_window">–>-->
|
||||||
|
<!-- <!– <field name="name">Manufacturing Orders</field>–>-->
|
||||||
|
<!-- <!– <field name="type">ir.actions.act_window</field>–>-->
|
||||||
|
<!-- <!– <field name="res_model">mrp.production</field>–>-->
|
||||||
|
<!-- <!– <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>–>-->
|
||||||
|
<!-- <!– <field name="search_view_id" ref="mrp.view_mrp_production_filter"/>–>-->
|
||||||
|
<!-- <!– <!– <field name="context">{'search_default_todo': True, 'default_company_id': allowed_company_ids[0]}</field>–>–>-->
|
||||||
|
<!-- <!– <field name="domain">[('picking_type_id.active', '=', True)]</field>–>-->
|
||||||
|
<!-- <!– </record>–>-->
|
||||||
|
<!-- <!– <menuitem action="mrp_production_action_sf"–>-->
|
||||||
|
<!-- <!– id="menu_mrp_production_action_sf"–>-->
|
||||||
|
<!-- <!– parent="mrp.menu_mrp_manufacturing"–>-->
|
||||||
|
<!-- <!– sequence="2"–>-->
|
||||||
|
<!-- <!– string="制造订单"/>–>-->
|
||||||
|
<!-- <record id="mrp_production_action_sf" model="ir.actions.act_window">-->
|
||||||
|
<!-- <field name="name">Manufacturing Orders</field>-->
|
||||||
|
<!-- <field name="type">ir.actions.act_window</field>-->
|
||||||
|
<!-- <field name="res_model">mrp.production</field>-->
|
||||||
|
<!-- <field name="view_mode">tree,kanban,form,calendar,pivot,graph</field>-->
|
||||||
|
<!-- <field name="view_id" eval="False"/>-->
|
||||||
|
<!-- <field name="search_view_id" ref="mrp.view_mrp_production_filter"/>-->
|
||||||
|
<!-- <field name="context">{'search_default_todo': True, 'default_company_id':-->
|
||||||
|
<!-- allowed_company_ids[0],'search_default_filter_rework':1,'search_default_filter_programmed':1}-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- <field name="domain">[('picking_type_id.active', '=', True)]</field>-->
|
||||||
|
<!-- <!– <!– <field name="help" type="html">–>,'search_default_filter_rework': 1,–>-->
|
||||||
|
<!-- <!– 'search_default_filter_programming': 1–>-->
|
||||||
|
<!-- <!– <p class="o_view_nocontent_smiling_face">–>-->
|
||||||
|
<!-- <!– No manufacturing order found. Let's create one.–>-->
|
||||||
|
<!-- <!– </p>–>-->
|
||||||
|
<!-- <!– <p>–>-->
|
||||||
|
<!-- <!– Consume <a name="%(product.product_template_action)d" type='action' tabindex="-1">components</a> and–>-->
|
||||||
|
<!-- <!– build finished products using–>-->
|
||||||
|
<!-- <!– <a name="%(mrp_bom_form_action)d" type='action' tabindex="-1">bills of materials</a>–>-->
|
||||||
|
<!-- <!– </p>–>-->
|
||||||
|
<!-- <!– </field>–>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
|
||||||
|
<!-- <menuitem action="sf_manufacturing.mrp_production_action_sf"-->
|
||||||
|
<!-- id="mrp_production_action"-->
|
||||||
|
<!-- parent="mrp.menu_mrp_manufacturing"-->
|
||||||
|
<!-- sequence="1"/>-->
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -7,11 +7,10 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<field name="name" position="replace">
|
<field name="name" position="replace">
|
||||||
<field name="is_subcontract" invisible="1"/>
|
<field name="is_subcontract" invisible="1"/>
|
||||||
|
|
||||||
<field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/>
|
<field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="name" position="before">
|
<field name="name" position="before">
|
||||||
<field name="sequence" optional="hide"/>
|
<field name="sequence"/>
|
||||||
<field name='user_permissions' invisible="1"/>
|
<field name='user_permissions' invisible="1"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="name" position="after">
|
<field name="name" position="after">
|
||||||
@@ -123,6 +122,7 @@
|
|||||||
<xpath expr="//field[@name='state']" position="before">
|
<xpath expr="//field[@name='state']" position="before">
|
||||||
<field name='user_permissions' invisible="1"/>
|
<field name='user_permissions' invisible="1"/>
|
||||||
<field name='name' invisible="1"/>
|
<field name='name' invisible="1"/>
|
||||||
|
<field name='is_rework' invisible="1"/>
|
||||||
<field name='is_delivery' invisible="1"/>
|
<field name='is_delivery' invisible="1"/>
|
||||||
<!-- <field name='is_send_program_again' invisible="1"/>-->
|
<!-- <field name='is_send_program_again' invisible="1"/>-->
|
||||||
<!-- 工单form页面的开始停工按钮等 -->
|
<!-- 工单form页面的开始停工按钮等 -->
|
||||||
@@ -160,11 +160,9 @@
|
|||||||
<!-- 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')]}"/>
|
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done')]}"/>
|
||||||
<!-- <button name="button_send_program_again" type="object" string="重新下发NC程序" class="btn-primary"-->
|
<button name="button_rework_pre" type="object" string="返工"
|
||||||
<!-- confirm="是否确认重新下发NC程序?"-->
|
class="btn-primary"
|
||||||
<!-- groups="sf_base.group_sf_order_user,sf_base.group_sf_equipment_user"-->
|
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
|
||||||
<!-- attrs="{'invisible': ['|', '|', '|',('routing_type','!=','装夹预调'),('state','in',['done', 'cancel',-->
|
|
||||||
<!-- 'progress']),('cnc_worksheet','=',False),('is_send_program_again','=',True)]}"/>-->
|
|
||||||
<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>
|
||||||
@@ -187,7 +185,7 @@
|
|||||||
<xpath expr="//label[1]" position="before">
|
<xpath expr="//label[1]" position="before">
|
||||||
<field name='routing_type' readonly="1"/>
|
<field name='routing_type' readonly="1"/>
|
||||||
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
|
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
|
||||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
<field name="rfid_code" cache="True"
|
||||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
|
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
|
||||||
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
@@ -220,11 +218,11 @@
|
|||||||
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
|
attrs="{'invisible': [('routing_type', '!=', 'CNC加工')]}"/>
|
||||||
<field name="processing_panel" readonly="1"
|
<field name="processing_panel" readonly="1"
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||||
<field name="equipment_id"
|
<field name="equipment_id" readonly="1"
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||||
<field name="production_line_id"
|
<field name="production_line_id"
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割"))]}'/>
|
||||||
<field name="production_line_state"
|
<field name="production_line_state" readonly="1"
|
||||||
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/>
|
||||||
<!-- <field name="functional_fixture_id" -->
|
<!-- <field name="functional_fixture_id" -->
|
||||||
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
|
<!-- attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/> -->
|
||||||
@@ -487,10 +485,11 @@
|
|||||||
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
|
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
|
||||||
<!-- <field name="is_fetchcnc"-->
|
<!-- <field name="is_fetchcnc"-->
|
||||||
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
|
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
|
||||||
<!-- <field name="reason"-->
|
<field name="reason"
|
||||||
<!-- attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>-->
|
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
|
||||||
<!-- <field name="detailed_reason" attrs='{"invisible":[("test_results","=","合格")]}'/>-->
|
<field name="detailed_reason"
|
||||||
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
|
||||||
|
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
||||||
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
|
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
|
||||||
widget="pdf_viewer"/>
|
widget="pdf_viewer"/>
|
||||||
</group>
|
</group>
|
||||||
@@ -589,6 +588,9 @@
|
|||||||
<field name="product_id" position="after">
|
<field name="product_id" position="after">
|
||||||
<field name="part_number" string="成品零件图号"/>
|
<field name="part_number" string="成品零件图号"/>
|
||||||
</field>
|
</field>
|
||||||
|
<xpath expr="//filter[@name='progress']" position="after">
|
||||||
|
<filter string="待检测" name="state" domain="[('state','=','to be detected')]"/>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
from . import workpiece_delivery_wizard
|
from . import workpiece_delivery_wizard
|
||||||
|
from . import rework_wizard
|
||||||
|
|||||||
91
sf_manufacturing/wizard/rework_wizard.py
Normal file
91
sf_manufacturing/wizard/rework_wizard.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of YiZuo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import logging
|
||||||
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
from datetime import datetime
|
||||||
|
from odoo import models, api, fields, _
|
||||||
|
|
||||||
|
|
||||||
|
class ReworkWizard(models.TransientModel):
|
||||||
|
_name = 'sf.rework.wizard'
|
||||||
|
_description = '返工向导'
|
||||||
|
|
||||||
|
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
||||||
|
product_id = fields.Many2one('product.product')
|
||||||
|
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
||||||
|
rework_reason = fields.Selection(
|
||||||
|
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"),
|
||||||
|
("operate computer", "操机"),
|
||||||
|
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因")
|
||||||
|
detailed_reason = fields.Text('详细原因')
|
||||||
|
routing_type = fields.Selection([
|
||||||
|
('装夹预调', '装夹预调'),
|
||||||
|
('CNC加工', 'CNC加工')], string="工序类型")
|
||||||
|
# 根据工单的加工面来显示
|
||||||
|
processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面")
|
||||||
|
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
|
||||||
|
reprogramming_num = fields.Integer('重新编程次数', default=0)
|
||||||
|
programming_state = fields.Selection(
|
||||||
|
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发')],
|
||||||
|
string='编程状态')
|
||||||
|
|
||||||
|
def confirm(self):
|
||||||
|
if self.routing_type in ['装夹预调', 'CNC加工']:
|
||||||
|
self.workorder_id.is_rework = True
|
||||||
|
self.production_id.write({'detection_result_ids': [(0, 0, {
|
||||||
|
'rework_reason': self.rework_reason,
|
||||||
|
'detailed_reason': self.detailed_reason,
|
||||||
|
'processing_panel': self.workorder_id.processing_panel,
|
||||||
|
'routing_type': self.workorder_id.routing_type,
|
||||||
|
'handle_result': '待处理' if self.workorder_id.test_results == '返工' or self.workorder_id.is_rework is True else '',
|
||||||
|
'test_results': '返工' if not self.routing_type == 'CNC加工' else self.workorder_id.test_results,
|
||||||
|
'test_report': self.workorder_id.detection_report})]})
|
||||||
|
self.workorder_id.button_finish()
|
||||||
|
else:
|
||||||
|
if self.production_id.workorder_ids:
|
||||||
|
for panel in self.processing_panel_id:
|
||||||
|
panel_workorder = self.production_id.workorder_ids.filtered(
|
||||||
|
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
|
||||||
|
if panel_workorder:
|
||||||
|
panel_workorder.write({'state': 'rework'})
|
||||||
|
product_routing_workcenter = self.env['sf.product.model.type.routing.sort'].search(
|
||||||
|
[('product_model_type_id', '=', self.production_id.product_id.product_model_type_id.id)],
|
||||||
|
order='sequence asc'
|
||||||
|
)
|
||||||
|
workorders_values = []
|
||||||
|
for route in product_routing_workcenter:
|
||||||
|
if route.is_repeat is True:
|
||||||
|
workorders_values.append(
|
||||||
|
self.env['mrp.workorder'].json_workorder_str(panel.name,
|
||||||
|
self.production_id, route, False))
|
||||||
|
if workorders_values:
|
||||||
|
self.production_id.write(
|
||||||
|
{'workorder_ids': workorders_values, 'programming_state': '编程中', 'work_state': '编程中',
|
||||||
|
'reprogramming_num': self.production_id.reprogramming_num + 1})
|
||||||
|
self.production_id._reset_work_order_sequence()
|
||||||
|
self.production_id.detection_result_ids.filtered(
|
||||||
|
lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
|
||||||
|
{'handle_result': '已处理'})
|
||||||
|
if self.is_reprogramming is True:
|
||||||
|
self.production_id.update_programming_state()
|
||||||
|
else:
|
||||||
|
self.production_id.do_update_program()
|
||||||
|
if self.production_id.state == 'progress':
|
||||||
|
self.production_id.write({'programming_state': '已编程'})
|
||||||
|
|
||||||
|
@api.onchange('production_id')
|
||||||
|
def onchange_processing_panel_id(self):
|
||||||
|
for item in self:
|
||||||
|
domain = [('id', '=', False)]
|
||||||
|
production_id = item.production_id
|
||||||
|
if production_id:
|
||||||
|
if self.env.user.has_group('sf_base.group_sf_order_user'):
|
||||||
|
panel_ids = []
|
||||||
|
for p in production_id.detection_result_ids.filtered(
|
||||||
|
lambda ap1: ap1.handle_result == '待处理'):
|
||||||
|
panel = self.env['sf.processing.panel'].search(
|
||||||
|
[('name', 'ilike', p.processing_panel)])
|
||||||
|
if panel:
|
||||||
|
panel_ids.append(panel.id)
|
||||||
|
domain = {'processing_panel_id': [('id', 'in', panel_ids)]}
|
||||||
|
return {'domain': domain}
|
||||||
51
sf_manufacturing/wizard/rework_wizard_views.xml
Normal file
51
sf_manufacturing/wizard/rework_wizard_views.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record model="ir.ui.view" id="sf_rework_wizard_form_view">
|
||||||
|
<field name="name">sf.rework.wizard.form.view</field>
|
||||||
|
<field name="model">sf.rework.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<field name="production_id" invisible="True"/>
|
||||||
|
<field name="workorder_id" invisible="True"/>
|
||||||
|
<field name="product_id" invisible="True"/>
|
||||||
|
<field name="routing_type" invisible="True"/>
|
||||||
|
<group>
|
||||||
|
<field name="processing_panel_id" options="{'no_create': True}"
|
||||||
|
attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/>
|
||||||
|
</group>
|
||||||
|
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'>
|
||||||
|
注意*: 该制造订单的产品已重复编程过<field name="reprogramming_num" string=""
|
||||||
|
readonly="1"
|
||||||
|
style='color:red;'/>次,且当前编程状态为
|
||||||
|
<field name="programming_state" string=""
|
||||||
|
decoration-info="programming_state == '待编程'"
|
||||||
|
decoration-success="programming_state == '已编程'"
|
||||||
|
decoration-warning="programming_state =='编程中'"
|
||||||
|
decoration-danger="programming_state =='已编程未下发'" readonly="1"/>
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<field name="is_reprogramming"
|
||||||
|
attrs='{"invisible": [("routing_type","in",["装夹预调","CNC加工"])],"readonly": [("programming_state","in",["编程中","已编程未下发"])]}'/>
|
||||||
|
<field name="rework_reason"
|
||||||
|
attrs='{"invisible": [("routing_type","not in",["装夹预调","CNC加工"])],"required": [("routing_type","in",["装夹预调"])]}'/>
|
||||||
|
<field name="detailed_reason"
|
||||||
|
attrs='{"invisible": [("routing_type","not in",["装夹预调","CNC加工"])],"required": [("routing_type","in",["装夹预调"])]}'/>
|
||||||
|
</group>
|
||||||
|
<footer>
|
||||||
|
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认返工"/>
|
||||||
|
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||||
|
</footer>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_sf_rework_wizard" model="ir.actions.act_window">
|
||||||
|
<field name="name">返工</field>
|
||||||
|
<field name="res_model">sf.rework.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -82,37 +82,38 @@ class Sf_Mrs_Connect(http.Controller):
|
|||||||
# if pre_workorder:
|
# if pre_workorder:
|
||||||
# pre_workorder.write(
|
# pre_workorder.write(
|
||||||
# {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
# {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
for panel in ret['processing_panel'].split(','):
|
# for panel in ret['processing_panel'].split(','):
|
||||||
# 查询状态为进行中且工序类型为CNC加工的工单
|
# # 查询状态为进行中且工序类型为CNC加工的工单
|
||||||
cnc_workorder = productions.workorder_ids.filtered(
|
# cnc_workorder = productions.workorder_ids.filtered(
|
||||||
lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
|
# lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
|
||||||
'cancel'] and ac.processing_panel == panel)
|
# 'cancel'] and ac.processing_panel == panel)
|
||||||
if cnc_workorder:
|
# if cnc_workorder:
|
||||||
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
# # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
|
||||||
# panel)
|
# # panel)
|
||||||
program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
|
# program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
|
||||||
logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
|
# logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
|
||||||
files_panel = os.listdir(program_path_tmp_panel)
|
# files_panel = os.listdir(program_path_tmp_panel)
|
||||||
if files_panel:
|
# if files_panel:
|
||||||
for file in files_panel:
|
# for file in files_panel:
|
||||||
file_extension = os.path.splitext(file)[1]
|
# file_extension = os.path.splitext(file)[1]
|
||||||
logging.info('file_extension:%s' % file_extension)
|
# logging.info('file_extension:%s' % file_extension)
|
||||||
if file_extension.lower() == '.pdf':
|
# if file_extension.lower() == '.pdf':
|
||||||
panel_file_path = os.path.join(program_path_tmp_panel, file)
|
# panel_file_path = os.path.join(program_path_tmp_panel, file)
|
||||||
logging.info('panel_file_path:%s' % panel_file_path)
|
# logging.info('panel_file_path:%s' % panel_file_path)
|
||||||
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
# cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
pre_workorder = productions.workorder_ids.filtered(
|
# pre_workorder = productions.workorder_ids.filtered(
|
||||||
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
|
# lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
|
||||||
'cancel'] and ap.processing_panel == panel)
|
# 'cancel'] and ap.processing_panel == panel)
|
||||||
if pre_workorder:
|
# if pre_workorder:
|
||||||
pre_workorder.write(
|
# pre_workorder.write(
|
||||||
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
# {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
|
||||||
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
productions.write({'programming_state': '已编程', 'work_state': '已编程'})
|
||||||
cnc_program_ids = [item.id for item in productions]
|
cnc_program_ids = [item.id for item in productions]
|
||||||
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
|
||||||
[('production_id', 'in', cnc_program_ids)])
|
[('production_id', 'in', cnc_program_ids)])
|
||||||
if workpiece_delivery:
|
if workpiece_delivery:
|
||||||
workpiece_delivery.write({'is_cnc_program_down': True})
|
workpiece_delivery.write(
|
||||||
|
{'is_cnc_program_down': True, 'production_line_id': productions.production_line_id.id})
|
||||||
return json.JSONEncoder().encode(res)
|
return json.JSONEncoder().encode(res)
|
||||||
else:
|
else:
|
||||||
res = {'status': 0, 'message': '该制造订单暂未开始'}
|
res = {'status': 0, 'message': '该制造订单暂未开始'}
|
||||||
|
|||||||
@@ -102,11 +102,11 @@
|
|||||||
attrs="{'invisible': ['|', '|', '|', ('picking_type_code', '=', 'incoming'), ('immediate_transfer', '=', True), '&', ('state', '!=', 'assigned'), ('move_type', '!=', 'one'), '&', ('state', 'not in', ('assigned', 'confirmed')), ('move_type', '=', 'one')]}"
|
attrs="{'invisible': ['|', '|', '|', ('picking_type_code', '=', 'incoming'), ('immediate_transfer', '=', True), '&', ('state', '!=', 'assigned'), ('move_type', '!=', 'one'), '&', ('state', 'not in', ('assigned', 'confirmed')), ('move_type', '=', 'one')]}"
|
||||||
data-hotkey="w"/>
|
data-hotkey="w"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//form//header//button[@name='button_scrap']" position="replace">
|
<!-- <xpath expr="//form//header//button[@name='button_scrap']" position="replace">-->
|
||||||
<button name="button_scrap" groups="sf_base.group_sf_stock_user" type="object" string="报废"
|
<!-- <button name="button_scrap" groups="sf_base.group_sf_stock_user" type="object" string="报废"-->
|
||||||
attrs="{'invisible': ['|', '&', ('picking_type_code', '=', 'incoming'), ('state', '!=', 'done'), '&', ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done')]}"
|
<!-- attrs="{'invisible': ['|', '&', ('picking_type_code', '=', 'incoming'), ('state', '!=', 'done'), '&', ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done')]}"-->
|
||||||
data-hotkey="y"/>
|
<!-- data-hotkey="y"/>-->
|
||||||
</xpath>
|
<!-- </xpath>-->
|
||||||
<xpath expr="//form//header//button[@name='action_assign']" position="replace">
|
<xpath expr="//form//header//button[@name='action_assign']" position="replace">
|
||||||
<button name="action_assign" attrs="{'invisible': [('show_check_availability', '=', False)]}"
|
<button name="action_assign" attrs="{'invisible': [('show_check_availability', '=', False)]}"
|
||||||
string="检查可用量" type="object" class="oe_highlight"
|
string="检查可用量" type="object" class="oe_highlight"
|
||||||
|
|||||||
Reference in New Issue
Block a user