Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/优化制造功能
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
<field name="program_name"/>
|
||||
<field name="functional_tool_type_id"/>
|
||||
<field name="cutting_tool_name"/>
|
||||
<field name="tool_state"/>
|
||||
<field name="cutting_tool_no"/>
|
||||
<field name="processing_type"/>
|
||||
<field name="margin_x_y"/>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
'security/ir.model.access.csv',
|
||||
'wizard/workpiece_delivery_views.xml',
|
||||
'wizard/rework_wizard_views.xml',
|
||||
'wizard/production_wizard_views.xml',
|
||||
'views/mrp_views_menus.xml',
|
||||
'views/stock_lot_views.xml',
|
||||
'views/mrp_production_addional_change.xml',
|
||||
|
||||
@@ -242,7 +242,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('button_Work_End error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def PartQualityInspect(self, **kw):
|
||||
"""
|
||||
|
||||
@@ -27,6 +27,43 @@ class MrpProduction(models.Model):
|
||||
string='工单状态', default='未排')
|
||||
|
||||
detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告')
|
||||
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
|
||||
store=True, compute='_compute_tool_state')
|
||||
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', readonly=True)
|
||||
tool_state_remark2 = fields.Text(string='功能刀具状态备注(无效刀)', readonly=True)
|
||||
|
||||
@api.depends('workorder_ids.tool_state')
|
||||
def _compute_tool_state(self):
|
||||
for item in self:
|
||||
if item:
|
||||
workorder_ids = item.workorder_ids.filtered(lambda a: a.state not in ('rework', '返工'))
|
||||
if workorder_ids.filtered(lambda a: a.tool_state == '2'):
|
||||
item.tool_state = '2'
|
||||
elif workorder_ids.filtered(lambda a: a.tool_state == '1'):
|
||||
tool_state_remark = ''
|
||||
data = {}
|
||||
# 获取所有缺刀工单加工面对应缺的刀
|
||||
for work in workorder_ids.filtered(lambda a: a.tool_state == '1'):
|
||||
if work.processing_panel not in list(data.keys()):
|
||||
data.update({work.processing_panel: []})
|
||||
for cnc in work.cnc_ids.filtered(lambda a: a.tool_state == '1'):
|
||||
if cnc.cutting_tool_name not in data[work.processing_panel]:
|
||||
data[work.processing_panel].append(cnc.cutting_tool_name)
|
||||
# 按格式生成缺刀提示信息
|
||||
for key in data:
|
||||
if data.get(key) and not data.get(key):
|
||||
if tool_state_remark != '':
|
||||
tool_state_remark = f'{tool_state_remark}\n{key}缺刀:{data.get(key)}'
|
||||
else:
|
||||
tool_state_remark = f'{key}缺刀:{data.get(key)}'
|
||||
item.tool_state = '1'
|
||||
item.tool_state_remark = tool_state_remark
|
||||
item.tool_state_remark2 = ''
|
||||
else:
|
||||
item.tool_state = '0'
|
||||
item.tool_state_remark = ''
|
||||
item.tool_state_remark2 = ''
|
||||
|
||||
# state = fields.Selection(selection_add=[
|
||||
# ('pending_scheduling', '待排程'),
|
||||
# ('pending_processing', '待加工'),
|
||||
@@ -53,10 +90,10 @@ class MrpProduction(models.Model):
|
||||
check_status = fields.Boolean(string='启用状态', default=False, readonly=True)
|
||||
active = fields.Boolean(string='已归档', default=True)
|
||||
programming_no = fields.Char('编程单号')
|
||||
reprogramming_num = fields.Integer('重新编程次数', default=0)
|
||||
work_state = fields.Char('业务状态')
|
||||
programming_state = fields.Selection(
|
||||
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发')], string='编程状态',
|
||||
[('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')],
|
||||
string='编程状态',
|
||||
tracking=True)
|
||||
glb_file = fields.Binary("glb模型文件")
|
||||
production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True)
|
||||
@@ -82,10 +119,10 @@ class MrpProduction(models.Model):
|
||||
part_drawing = fields.Binary('零件图纸')
|
||||
|
||||
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
||||
rework_production = fields.Many2one('mrp.production', string='返工的制造订单')
|
||||
is_scrap = fields.Boolean('是否报废', default=False)
|
||||
|
||||
@api.depends(
|
||||
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state',
|
||||
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
|
||||
'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state')
|
||||
def _compute_state(self):
|
||||
for production in self:
|
||||
@@ -127,20 +164,27 @@ class MrpProduction(models.Model):
|
||||
production.state = 'pending_cam'
|
||||
|
||||
if production.state == 'progress':
|
||||
if all(wo_state not in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')):
|
||||
if all(wo_state not in ('progress', 'done', 'rework') for wo_state in
|
||||
production.workorder_ids.mapped('state')):
|
||||
production.state = 'pending_cam'
|
||||
if production.is_rework is True:
|
||||
production.state = 'rework'
|
||||
# if production.state == 'pending_cam':
|
||||
# if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')):
|
||||
# production.state = 'done'
|
||||
if any(
|
||||
(
|
||||
wo.test_results == '返工' and wo.state == 'done' and production.programming_state == '已编程') or (
|
||||
wo.test_results == '返工' and wo.state == 'done' and production.programming_state in [
|
||||
'已编程']) 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'
|
||||
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
|
||||
if production.tool_state == '2':
|
||||
production.state = 'rework'
|
||||
|
||||
def action_check(self):
|
||||
"""
|
||||
@@ -176,11 +220,12 @@ class MrpProduction(models.Model):
|
||||
try:
|
||||
if not self:
|
||||
reproduction = self.env['mrp.production'].search(
|
||||
[('state', '=', 'rework'), ('programming_state', '=', '编程中')])
|
||||
[('state', '=', 'rework'), ('programming_state', '=', '编程中'), ('is_rework', '=', True)])
|
||||
else:
|
||||
reproduction = self
|
||||
if reproduction:
|
||||
programming_no = [item.programming_no for item in reproduction]
|
||||
programming_no_set = set([str(item.programming_no) for item in reproduction])
|
||||
programming_no = list(programming_no_set)
|
||||
programming_no_str = ','.join(programming_no)
|
||||
res = {'programming_no': programming_no_str}
|
||||
logging.info('res=%s:' % res)
|
||||
@@ -195,13 +240,14 @@ class MrpProduction(models.Model):
|
||||
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 '编程中'})
|
||||
for rp in reproduction:
|
||||
if rp.programming_no == item['programming_no']:
|
||||
rp.write({'programming_state': '已编程未下发' if item[
|
||||
'programming_state'] == '已编程' else '编程中'})
|
||||
logging.info('rp:%s' % rp.name)
|
||||
|
||||
else:
|
||||
return item['programming_state']
|
||||
return item
|
||||
|
||||
else:
|
||||
raise UserError(ret['message'])
|
||||
@@ -211,7 +257,8 @@ class MrpProduction(models.Model):
|
||||
# 编程单更新
|
||||
def update_programming_state(self):
|
||||
try:
|
||||
res = {'programming_no': self.programming_no, 'manufacturing_type': 'rework'}
|
||||
res = {'programming_no': self.programming_no,
|
||||
'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'}
|
||||
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'])
|
||||
@@ -486,28 +533,6 @@ class MrpProduction(models.Model):
|
||||
for workorder in production.workorder_ids:
|
||||
workorder.duration_expected = workorder._get_duration_expected()
|
||||
|
||||
# 在之前的销售单上重新生成制造订单
|
||||
def create_production1_values(self, production):
|
||||
production_values_str = {'origin': production.origin,
|
||||
'product_id': production.product_id.id,
|
||||
'product_description_variants': production.product_description_variants,
|
||||
'product_qty': production.product_qty,
|
||||
'product_uom_id': production.product_uom_id.id,
|
||||
'location_src_id': production.location_src_id.id,
|
||||
'location_dest_id': production.location_dest_id.id,
|
||||
'bom_id': production.bom_id.id,
|
||||
'date_deadline': production.date_deadline,
|
||||
'date_planned_start': production.date_planned_start,
|
||||
'date_planned_finished': production.date_planned_finished,
|
||||
'procurement_group_id': False,
|
||||
'propagate_cancel': production.propagate_cancel,
|
||||
'orderpoint_id': production.orderpoint_id.id,
|
||||
'picking_type_id': production.picking_type_id.id,
|
||||
'company_id': production.company_id.id,
|
||||
'move_dest_ids': production.move_dest_ids.ids,
|
||||
'user_id': production.user_id.id}
|
||||
return production_values_str
|
||||
|
||||
# 工单排序
|
||||
def _reset_work_order_sequence1(self, k):
|
||||
for rec in self:
|
||||
@@ -763,10 +788,10 @@ class MrpProduction(models.Model):
|
||||
|
||||
# 返工
|
||||
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)
|
||||
cloud_programming = None
|
||||
if self.programming_state in ['已编程']:
|
||||
cloud_programming = self._cron_get_programming_state()
|
||||
logging.info('cloud_programming_state:%s' % cloud_programming['programming_state'])
|
||||
logging.info('programming_state:%s' % self.programming_state)
|
||||
return {
|
||||
'name': _('返工'),
|
||||
@@ -776,20 +801,12 @@ class MrpProduction(models.Model):
|
||||
'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
|
||||
'default_reprogramming_num': cloud_programming['reprogramming_num'],
|
||||
'default_programming_state': cloud_programming['programming_state'],
|
||||
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
|
||||
}
|
||||
}
|
||||
|
||||
# 报废
|
||||
def button_scrap_new(self):
|
||||
return True
|
||||
|
||||
# 更新程序
|
||||
def do_update_program(self):
|
||||
program_production = self
|
||||
@@ -811,13 +828,19 @@ class MrpProduction(models.Model):
|
||||
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:
|
||||
productions_not_delivered = self.env['mrp.production'].search(
|
||||
[('programming_no', '=', production.programming_no), ('programming_state', '=', '已编程未下发')])
|
||||
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': '已下发'})
|
||||
for rework_item in rework_workorder:
|
||||
pending_workorder = production.workorder_ids.filtered(
|
||||
lambda m1: m1.state in [
|
||||
'pending'] and m1.processing_panel == rework_item.processing_panel and m1.routing_type == 'CNC加工')
|
||||
if not pending_workorder.cnc_ids:
|
||||
production.get_new_program(rework_item.processing_panel)
|
||||
# production.write({'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
||||
productions_not_delivered.write(
|
||||
{'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
|
||||
|
||||
# 从cloud获取重新编程过的最新程序
|
||||
def get_new_program(self, processing_panel):
|
||||
@@ -843,11 +866,12 @@ class MrpProduction(models.Model):
|
||||
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'])])
|
||||
[('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')
|
||||
ac: ac.processing_panel == processing_panel and ac.routing_type == 'CNC加工' and ac.state not in (
|
||||
'rework', 'done'))
|
||||
if panel_workorder:
|
||||
if panel_workorder.cnc_ids:
|
||||
panel_workorder.cmm_ids.sudo().unlink()
|
||||
@@ -879,6 +903,134 @@ class MrpProduction(models.Model):
|
||||
logging.info('get_new_program error:%s' % e)
|
||||
raise UserError("从云平台获取最新程序失败,请联系管理员")
|
||||
|
||||
def recreateManufacturing(self):
|
||||
"""
|
||||
重新生成制造订单
|
||||
"""
|
||||
if self.is_scrap is True:
|
||||
sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
|
||||
values = self.env['mrp.production'].create_production1_values(self.production_id)
|
||||
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
|
||||
self.production_id.company_id).create(
|
||||
values)
|
||||
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
productions._create_workorder()
|
||||
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
|
||||
(
|
||||
p.move_dest_ids.procure_method != 'make_to_order' and
|
||||
not p.move_raw_ids and not p.workorder_ids)).action_confirm()
|
||||
for production_item in productions:
|
||||
process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
|
||||
('is_subcontract', '=', True)])
|
||||
if process_parameter_workorder:
|
||||
is_pick = False
|
||||
consecutive_workorders = []
|
||||
m = 0
|
||||
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
|
||||
for i in range(len(sorted_workorders) - 1):
|
||||
if m == 0:
|
||||
is_pick = False
|
||||
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
|
||||
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
|
||||
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
|
||||
if sorted_workorders[i] not in consecutive_workorders:
|
||||
consecutive_workorders.append(sorted_workorders[i])
|
||||
consecutive_workorders.append(sorted_workorders[i + 1])
|
||||
m += 1
|
||||
continue
|
||||
else:
|
||||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
|
||||
production_item)
|
||||
if sorted_workorders[i] in consecutive_workorders:
|
||||
is_pick = True
|
||||
consecutive_workorders = []
|
||||
m = 0
|
||||
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
|
||||
if is_pick is False:
|
||||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
|
||||
production_item)
|
||||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
|
||||
production_item)
|
||||
if sorted_workorders[i] in consecutive_workorders:
|
||||
is_pick = True
|
||||
consecutive_workorders = []
|
||||
m = 0
|
||||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
|
||||
if is_pick is False and m == 0:
|
||||
if len(sorted_workorders) == 1:
|
||||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
|
||||
else:
|
||||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
|
||||
|
||||
for production in productions:
|
||||
origin_production = production.move_dest_ids and production.move_dest_ids[
|
||||
0].raw_material_production_id or False
|
||||
orderpoint = production.orderpoint_id
|
||||
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
|
||||
production.message_post(
|
||||
body=_('This production order has been created from Replenishment Report.'),
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_note')
|
||||
elif orderpoint:
|
||||
production.message_post_with_view(
|
||||
'mail.message_origin_link',
|
||||
values={'self': production, 'origin': orderpoint},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
elif origin_production:
|
||||
production.message_post_with_view(
|
||||
'mail.message_origin_link',
|
||||
values={'self': production, 'origin': origin_production},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
|
||||
'''
|
||||
创建生产计划
|
||||
'''
|
||||
# 工单耗时
|
||||
workorder_duration = 0
|
||||
for workorder in productions.workorder_ids:
|
||||
workorder_duration += workorder.duration_expected
|
||||
|
||||
if sale_order:
|
||||
sale_order.mrp_production_ids |= productions
|
||||
# sale_order.write({'schedule_status': 'to schedule'})
|
||||
self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
|
||||
'name': productions.name,
|
||||
'order_deadline': sale_order.deadline_of_delivery,
|
||||
'production_id': productions.id,
|
||||
'date_planned_start': productions.date_planned_start,
|
||||
'origin': productions.origin,
|
||||
'product_qty': productions.product_qty,
|
||||
'product_id': productions.product_id.id,
|
||||
'state': 'draft',
|
||||
})
|
||||
|
||||
# 在之前的销售单上重新生成制造订单
|
||||
def create_production1_values(self, production, sale_order):
|
||||
production_values_str = {'origin': production.origin,
|
||||
'product_id': production.product_id.id,
|
||||
'product_description_variants': production.product_description_variants,
|
||||
'product_qty': production.product_qty,
|
||||
'product_uom_id': production.product_uom_id.id,
|
||||
'location_src_id': production.location_src_id.id,
|
||||
'location_dest_id': production.location_dest_id.id,
|
||||
'bom_id': production.bom_id.id,
|
||||
'date_deadline': production.date_deadline,
|
||||
'date_planned_start': production.date_planned_start,
|
||||
'date_planned_finished': production.date_planned_finished,
|
||||
'procurement_group_id': sale_order.id,
|
||||
'propagate_cancel': production.propagate_cancel,
|
||||
'orderpoint_id': production.orderpoint_id.id,
|
||||
'picking_type_id': production.picking_type_id.id,
|
||||
'company_id': production.company_id.id,
|
||||
'move_dest_ids': production.move_dest_ids.ids,
|
||||
'user_id': production.user_id.id}
|
||||
return production_values_str
|
||||
|
||||
|
||||
class sf_detection_result(models.Model):
|
||||
_name = 'sf.detection.result'
|
||||
|
||||
@@ -13,13 +13,13 @@ from dateutil.relativedelta import relativedelta
|
||||
# import subprocess
|
||||
from odoo import api, fields, models, SUPERUSER_ID, _
|
||||
from odoo.addons.sf_base.commons.common import Common
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
|
||||
|
||||
|
||||
class ResMrpWorkOrder(models.Model):
|
||||
_inherit = 'mrp.workorder'
|
||||
_order = 'id'
|
||||
_order = 'sequence asc'
|
||||
|
||||
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
|
||||
|
||||
@@ -47,7 +47,18 @@ class ResMrpWorkOrder(models.Model):
|
||||
('切割', '切割'), ('表面工艺', '表面工艺')
|
||||
], string="工序类型")
|
||||
results = fields.Char('结果')
|
||||
state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
|
||||
state = fields.Selection([
|
||||
('pending', '等待其他工单'),
|
||||
('waiting', '等待组件'),
|
||||
('ready', '就绪'),
|
||||
('progress', '进行中'),
|
||||
('to be detected', "待检测"),
|
||||
('done', '已完工'),
|
||||
('rework', '返工'),
|
||||
('cancel', '取消')], string='Status',
|
||||
compute='_compute_state', store=True,
|
||||
default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True)
|
||||
# state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
|
||||
|
||||
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
|
||||
|
||||
@@ -159,6 +170,21 @@ class ResMrpWorkOrder(models.Model):
|
||||
# 加工图纸
|
||||
processing_drawing = fields.Binary(string='加工图纸')
|
||||
|
||||
# 功能刀具状态
|
||||
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
|
||||
store=True, compute='_compute_tool_state')
|
||||
|
||||
@api.depends('cnc_ids.tool_state')
|
||||
def _compute_tool_state(self):
|
||||
for item in self:
|
||||
if item:
|
||||
if item.cnc_ids.filtered(lambda a: a.tool_state == '2'):
|
||||
item.tool_state = '2'
|
||||
elif item.cnc_ids.filtered(lambda a: a.tool_state == '1'):
|
||||
item.tool_state = '1'
|
||||
else:
|
||||
item.tool_state = '0'
|
||||
|
||||
@api.depends('production_id')
|
||||
def _compute_save_name(self):
|
||||
"""
|
||||
@@ -474,6 +500,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
'context': {
|
||||
'default_workorder_id': self.id,
|
||||
'default_production_id': self.production_id.id,
|
||||
# 'default_programming_state': self.production_id.programming_state,
|
||||
'default_routing_type': self.routing_type
|
||||
}}
|
||||
|
||||
@@ -684,119 +711,6 @@ class ResMrpWorkOrder(models.Model):
|
||||
# 'target':'new'
|
||||
# }
|
||||
|
||||
def recreateManufacturingOrWorkerOrder(self):
|
||||
"""
|
||||
重新生成制造订单或者重新生成工单
|
||||
"""
|
||||
if self.test_results in ['返工', '报废']:
|
||||
values = self.env['mrp.production'].create_production1_values(self.production_id)
|
||||
productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
|
||||
self.production_id.company_id).create(
|
||||
values)
|
||||
# self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||
self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
productions._create_workorder()
|
||||
productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
|
||||
(
|
||||
p.move_dest_ids.procure_method != 'make_to_order' and
|
||||
not p.move_raw_ids and not p.workorder_ids)).action_confirm()
|
||||
for production_item in productions:
|
||||
process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
|
||||
('is_subcontract', '=', True)])
|
||||
if process_parameter_workorder:
|
||||
is_pick = False
|
||||
consecutive_workorders = []
|
||||
m = 0
|
||||
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
|
||||
for i in range(len(sorted_workorders) - 1):
|
||||
if m == 0:
|
||||
is_pick = False
|
||||
if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
|
||||
sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
|
||||
sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
|
||||
if sorted_workorders[i] not in consecutive_workorders:
|
||||
consecutive_workorders.append(sorted_workorders[i])
|
||||
consecutive_workorders.append(sorted_workorders[i + 1])
|
||||
m += 1
|
||||
continue
|
||||
else:
|
||||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
|
||||
production_item)
|
||||
if sorted_workorders[i] in consecutive_workorders:
|
||||
is_pick = True
|
||||
consecutive_workorders = []
|
||||
m = 0
|
||||
# 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
|
||||
if is_pick is False:
|
||||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
|
||||
production_item)
|
||||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
|
||||
production_item)
|
||||
if sorted_workorders[i] in consecutive_workorders:
|
||||
is_pick = True
|
||||
consecutive_workorders = []
|
||||
m = 0
|
||||
if m == len(consecutive_workorders) - 1 and m != 0:
|
||||
self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
|
||||
if is_pick is False and m == 0:
|
||||
if len(sorted_workorders) == 1:
|
||||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
|
||||
else:
|
||||
self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
|
||||
|
||||
for production in productions:
|
||||
origin_production = production.move_dest_ids and production.move_dest_ids[
|
||||
0].raw_material_production_id or False
|
||||
orderpoint = production.orderpoint_id
|
||||
if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
|
||||
production.message_post(
|
||||
body=_('This production order has been created from Replenishment Report.'),
|
||||
message_type='comment',
|
||||
subtype_xmlid='mail.mt_note')
|
||||
elif orderpoint:
|
||||
production.message_post_with_view(
|
||||
'mail.message_origin_link',
|
||||
values={'self': production, 'origin': orderpoint},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
elif origin_production:
|
||||
production.message_post_with_view(
|
||||
'mail.message_origin_link',
|
||||
values={'self': production, 'origin': origin_production},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
|
||||
'''
|
||||
创建生产计划
|
||||
'''
|
||||
# 工单耗时
|
||||
workorder_duration = 0
|
||||
for workorder in productions.workorder_ids:
|
||||
workorder_duration += workorder.duration_expected
|
||||
|
||||
sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
|
||||
if sale_order:
|
||||
sale_order.mrp_production_ids |= productions
|
||||
# sale_order.write({'schedule_status': 'to schedule'})
|
||||
self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
|
||||
'name': productions.name,
|
||||
'order_deadline': sale_order.deadline_of_delivery,
|
||||
'production_id': productions.id,
|
||||
'date_planned_start': productions.date_planned_start,
|
||||
'origin': productions.origin,
|
||||
'product_qty': productions.product_qty,
|
||||
'product_id': productions.product_id.id,
|
||||
'state': 'draft',
|
||||
})
|
||||
# if self.test_results == '返工':
|
||||
# productions = self.production_id
|
||||
# # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
|
||||
# # self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
|
||||
# productions._create_workorder2(self.processing_panel)
|
||||
# else:
|
||||
# self.results = '合格'
|
||||
|
||||
def json_workorder_str1(self, k, production, route):
|
||||
workorders_values_str = [0, '', {
|
||||
'product_uom_id': production.product_uom_id.id,
|
||||
@@ -827,28 +741,73 @@ class ResMrpWorkOrder(models.Model):
|
||||
re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id),
|
||||
('processing_panel', '=', workorder.processing_panel),
|
||||
('is_rework', '=', True), ('state', 'in', ['done', 'rework'])])
|
||||
logging.info('re_work:%s' % re_work.state)
|
||||
cnc_workorder = self.env['mrp.workorder'].search(
|
||||
[('production_id', '=', workorder.production_id.id),
|
||||
('processing_panel', '=', workorder.processing_panel),
|
||||
('routing_type', '=', 'CNC加工'), ('state', 'in', ['done', 'rework']),
|
||||
('test_results', '=', '返工')])
|
||||
if workorder.state not in ['cancel', 'progress', 'rework']:
|
||||
logging.info('工序:%s' % workorder.routing_type)
|
||||
logging.info('状态:%s' % workorder.state)
|
||||
logging.info('is_rework:%s' % workorder.is_rework)
|
||||
logging.info('面:%s' % workorder.processing_panel)
|
||||
logging.info('编程状态:%s' % workorder.production_id.programming_state)
|
||||
logging.info('制造状态:%s' % workorder.production_id.state)
|
||||
# if workorder.state =='done' and workorder.is_rework is True:
|
||||
# workorder.state = 'rework'
|
||||
# else:
|
||||
# if re_work:
|
||||
# workorder.state = 'rework'
|
||||
if workorder.production_id.state == 'rework':
|
||||
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'
|
||||
logging.info('len(re_work):%s' % len(re_work))
|
||||
logging.info('len(cnc_workorder):%s' % len(cnc_workorder))
|
||||
logging.info('工序:%s' % workorder.routing_type)
|
||||
logging.info('状态:%s' % workorder.state)
|
||||
logging.info('is_rework:%s' % workorder.is_rework)
|
||||
logging.info('production_id.is_rework:%s' % workorder.production_id.is_rework)
|
||||
logging.info('面:%s' % workorder.processing_panel)
|
||||
logging.info('编程状态:%s' % workorder.production_id.programming_state)
|
||||
logging.info('制造状态:%s' % workorder.production_id.state)
|
||||
if workorder.routing_type == '装夹预调' and workorder.state not in ['done', 'rework',
|
||||
'cancel']:
|
||||
# # 有返工工单
|
||||
# if re_work:
|
||||
# 新工单
|
||||
if workorder.is_rework is False:
|
||||
if workorder.production_id.programming_state == '已编程' and workorder.production_id.is_rework is False:
|
||||
if re_work:
|
||||
workorder.state = 'ready'
|
||||
else:
|
||||
if workorder.production_id.is_rework is True:
|
||||
if re_work or cnc_workorder:
|
||||
workorder.state = 'waiting'
|
||||
|
||||
elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'rework', 'cancel']:
|
||||
pre_workorder = self.env['mrp.workorder'].search(
|
||||
[('production_id', '=', workorder.production_id.id),
|
||||
('processing_panel', '=', workorder.processing_panel),
|
||||
('routing_type', '=', '装夹预调'), ('state', '=', 'done')])
|
||||
if pre_workorder:
|
||||
if re_work:
|
||||
workorder.state = 'waiting'
|
||||
elif workorder.routing_type == '解除装夹' and workorder.state not in ['done', 'rework', 'cancel']:
|
||||
if cnc_workorder:
|
||||
workorder.state = 'waiting'
|
||||
# else:
|
||||
# if workorder.production_id.is_rework is True:
|
||||
# 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'
|
||||
logging.info('len(re_work):%s' % len(re_work))
|
||||
logging.info('len(cnc_workorder):%s' % len(cnc_workorder))
|
||||
logging.info('工序:%s' % workorder.routing_type)
|
||||
logging.info('状态:%s' % workorder.state)
|
||||
logging.info('is_rework:%s' % workorder.is_rework)
|
||||
logging.info('production_id.is_rework:%s' % workorder.production_id.is_rework)
|
||||
logging.info('面:%s' % workorder.processing_panel)
|
||||
logging.info('编程状态:%s' % workorder.production_id.programming_state)
|
||||
logging.info('制造状态:%s' % workorder.production_id.state)
|
||||
if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已编程' and \
|
||||
workorder.is_rework is False and workorder.state not in [
|
||||
'done', 'rework',
|
||||
'cancel']:
|
||||
if re_work and workorder.production_id.is_rework is False:
|
||||
workorder.state = 'ready'
|
||||
if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']:
|
||||
unclamp_workorder = self.env['mrp.workorder'].search(
|
||||
[('production_id', '=', workorder.production_id.id),
|
||||
('sequence', '=', workorder.sequence - 1),
|
||||
('state', 'in', ['done'])])
|
||||
if unclamp_workorder:
|
||||
workorder.state = 'ready'
|
||||
# else:
|
||||
# if workorder.state not in ['cancel', 'rework']:
|
||||
# workorder.state = 'rework'
|
||||
@@ -988,7 +947,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
if record.routing_type == '装夹预调':
|
||||
if not record.material_center_point and record.X_deviation_angle > 0:
|
||||
raise UserError("请对前置三元检测定位参数进行计算定位")
|
||||
if not record.rfid_code:
|
||||
if not record.rfid_code and record.is_rework is False:
|
||||
raise UserError("请扫RFID码进行绑定")
|
||||
record.process_state = '待加工'
|
||||
# record.write({'process_state': '待加工'})
|
||||
@@ -997,15 +956,15 @@ class ResMrpWorkOrder(models.Model):
|
||||
record.process_state = '待解除装夹'
|
||||
# record.write({'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})]})
|
||||
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})],
|
||||
'is_scrap': True if record.test_results == '报废' else False})
|
||||
if record.routing_type == '解除装夹':
|
||||
'''
|
||||
记录结束时间
|
||||
@@ -1060,18 +1019,25 @@ class ResMrpWorkOrder(models.Model):
|
||||
# record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code})
|
||||
# if record.is_remanufacture is True:
|
||||
# record.recreateManufacturingOrWorkerOrder()
|
||||
is_production_id = True
|
||||
for workorder in record.production_id.workorder_ids:
|
||||
if workorder.state != 'done':
|
||||
is_production_id = False
|
||||
if record.routing_type == '解除装夹':
|
||||
is_production_id = False
|
||||
rework_workorder = record.production_id.workorder_ids.filtered(lambda p: p.state == 'rework')
|
||||
done_workorder = record.production_id.workorder_ids.filtered(lambda p1: p1.state == 'done')
|
||||
if (len(rework_workorder) + len(done_workorder) == len(record.production_id.workorder_ids)) or (
|
||||
len(done_workorder) == len(record.production_id.workorder_ids)):
|
||||
is_production_id = True
|
||||
if record.routing_type in ['解除装夹'] or (
|
||||
record.is_rework is True and record.routing_type in ['装夹预调']) or (
|
||||
record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']):
|
||||
for workorder in record.production_id.workorder_ids:
|
||||
if workorder.processing_panel == record.processing_panel:
|
||||
rfid_code = workorder.rfid_code
|
||||
workorder.write({'rfid_code_old': rfid_code,
|
||||
'rfid_code': False})
|
||||
workorder.rfid_code_old = rfid_code
|
||||
workorder.rfid_code = False
|
||||
if workorder.rfid_code:
|
||||
raise ValidationError(f'【{workorder.name}】工单解绑失败,请重新点击完成按钮!!!')
|
||||
# workorder.rfid_code_old = rfid_code
|
||||
# workorder.rfid_code = False
|
||||
logging.info('workorder.rfid_code:%s' % workorder.rfid_code)
|
||||
if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']:
|
||||
logging.info('product_qty:%s' % record.production_id.product_qty)
|
||||
for move_raw_id in record.production_id.move_raw_ids:
|
||||
@@ -1128,32 +1094,6 @@ class ResMrpWorkOrder(models.Model):
|
||||
workorder.detection_report = base64.b64encode(open(report_file_path, 'rb').read())
|
||||
return True
|
||||
|
||||
# 重新下发nc程序
|
||||
# def button_send_program_again(self):
|
||||
# try:
|
||||
# res = {'programming_no': self.production_id.programming_no}
|
||||
# configsettings = self.env['res.config.settings'].get_values()
|
||||
# config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key'])
|
||||
# url = '/api/intelligent_programming/reset_state_again'
|
||||
# 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:
|
||||
# productions = self.env['mrp.production'].search(
|
||||
# [('programming_no', '=', self.production_id.programming_no), ('programming_state', '=', '已编程')])
|
||||
# if productions:
|
||||
# workorder = productions.workorder_ids.filtered(
|
||||
# lambda ap: ap.routing_type in ['装夹预调', 'CNC加工'] and ap.state not in ['done', 'cancel',
|
||||
# 'progress'])
|
||||
# if workorder:
|
||||
# productions.write({'work_state': '编程中', 'programming_state': '编程中'})
|
||||
# else:
|
||||
# raise UserError(result['message'])
|
||||
# except Exception as e:
|
||||
# logging.info('button_send_program_again error:%s' % e)
|
||||
# raise UserError("重新下发nc程序失败,请联系管理员")
|
||||
|
||||
def print_method(self):
|
||||
"""
|
||||
解除装夹处调用关联制造订单的关联序列号的打印方法
|
||||
@@ -1191,6 +1131,8 @@ class CNCprocessing(models.Model):
|
||||
program_path = fields.Char('程序文件路径')
|
||||
program_create_date = fields.Datetime('程序创建日期')
|
||||
|
||||
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='刀具状态', default='0')
|
||||
|
||||
# mrs下发编程单创建CNC加工
|
||||
def cnc_processing_create(self, cnc_workorder, ret, program_path, program_path_tmp):
|
||||
cnc_processing = None
|
||||
|
||||
@@ -142,7 +142,13 @@ access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_us
|
||||
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_rework_wizard_group_plan_dispatch,sf_rework_wizard_group_plan_dispatch,model_sf_rework_wizard,sf_base.group_plan_dispatch,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_detection_result_group_plan_dispatch,sf_detection_result_group_plan_dispatch,model_sf_detection_result,sf_base.group_plan_dispatch,1,1,1,0
|
||||
access_sf_detection_result_group_sf_equipment_user,sf_detection_result_group_sf_equipment_user,model_sf_detection_result,sf_base.group_sf_equipment_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
|
||||
access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
|
||||
access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -75,7 +75,7 @@
|
||||
</xpath>
|
||||
<xpath expr="//sheet//group//group[2]//label" position="before">
|
||||
<!-- <field name="process_state"/> -->
|
||||
<field name="state"/>
|
||||
<field name="state" readonly="1"/>
|
||||
<!-- <field name="process_state"/> -->
|
||||
|
||||
</xpath>
|
||||
@@ -88,6 +88,8 @@
|
||||
decoration-danger="programming_state =='已编程未下发'"/>
|
||||
<field name="work_state" invisible="1"/>
|
||||
<field name="schedule_state" invisible='1'/>
|
||||
<field name="is_scrap" invisible='1'/>
|
||||
<field name="is_rework" invisible='1'/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='user_id']" position="before">
|
||||
<field name="plan_start_processing_time" readonly="1"/>
|
||||
@@ -97,6 +99,9 @@
|
||||
<!-- <field name="production_line_state" readonly="1"/>-->
|
||||
<field name="part_number" string="成品的零件图号"/>
|
||||
<field name="part_drawing"/>
|
||||
<field name="tool_state"/>
|
||||
<field name="tool_state_remark" string="备注" attrs="{'invisible': [('tool_state', '!=', '1')]}"/>
|
||||
<field name="tool_state_remark2" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
||||
<button name="action_cancel" type="object" string="取消" data-hotkey="z"
|
||||
@@ -122,9 +127,10 @@
|
||||
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')]}"/>
|
||||
attrs="{'invisible': ['|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程')]}"/>
|
||||
<button name="%(sf_manufacturing.action_sf_production_wizard)d" string="报废" type="action"
|
||||
groups="sf_base.group_sf_mrp_user"
|
||||
attrs="{'invisible': [('is_scrap', '=', False)]}"/>
|
||||
</xpath>
|
||||
<xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace">
|
||||
<button name="button_mark_done" attrs="{'invisible': [
|
||||
@@ -279,7 +285,7 @@
|
||||
|
||||
<xpath expr="//sheet//notebook//page[@name='operations']" position="after">
|
||||
<page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}">
|
||||
<field name="detection_result_ids" string="" readonly="0">
|
||||
<field name="detection_result_ids" string="" readonly="1">
|
||||
<tree sample="1">
|
||||
<field name="production_id" invisible="1"/>
|
||||
<field name="processing_panel"/>
|
||||
@@ -418,7 +424,9 @@
|
||||
<xpath expr="//header//button[@name='action_cancel']" position="replace">
|
||||
<button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='state']" position="after">
|
||||
<field name="tool_state" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -455,6 +463,9 @@
|
||||
</xpath>
|
||||
<xpath expr="//filter[@name='planning_issues']" position="before">
|
||||
<separator/>
|
||||
<filter string="返工且已编程" name="filter_rework_programmed"
|
||||
domain="[('state', '=', 'rework'),('programming_state', '=', '已编程')]"/>
|
||||
<separator/>
|
||||
<filter name="filter_programming" string="编程中"
|
||||
domain="[('programming_state', '=', '编程中')]"/>
|
||||
<filter name="filter_programmed" string="已编程"
|
||||
@@ -555,7 +566,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<field name="handle_result"/>
|
||||
<!-- <field name="handle_result"/>-->
|
||||
<field name="test_report" readonly="1" widget="pdf_viewer"/>
|
||||
</group>
|
||||
</form>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
</field>
|
||||
<field name="state" position="after">
|
||||
<field name="work_state" optional="hide"/>
|
||||
<field name="tool_state" invisible="1"/>
|
||||
<field name="product_tmpl_name" invisible="1"/>
|
||||
</field>
|
||||
<field name="duration" position="replace">
|
||||
@@ -112,6 +113,10 @@
|
||||
<field name="model">mrp.workorder</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header/field[@name='state']" position="replace">
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button type="object" name="action_view_surface_technics_picking" class="oe_stat_button" icon="fa-truck"
|
||||
groups="base.group_user,sf_base.group_sf_order_user"
|
||||
@@ -120,6 +125,7 @@
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='state']" position="before">
|
||||
<field name='tool_state' invisible="1"/>
|
||||
<field name='user_permissions' invisible="1"/>
|
||||
<field name='name' invisible="1"/>
|
||||
<field name='is_rework' invisible="1"/>
|
||||
@@ -185,7 +191,7 @@
|
||||
<xpath expr="//label[1]" position="before">
|
||||
<field name='routing_type' readonly="1"/>
|
||||
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
|
||||
<field name="rfid_code" cache="True"
|
||||
<field name="rfid_code" force_save="1" readonly="1" cache="True"
|
||||
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
|
||||
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
|
||||
</xpath>
|
||||
@@ -481,7 +487,8 @@
|
||||
<field name="results" invisible="1"/>
|
||||
<page string="后置三元检测" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
|
||||
<group>
|
||||
<field name="test_results" attrs='{"invisible":[("results","!=",False)]}'/>
|
||||
<field name="test_results"
|
||||
attrs='{"readonly":[("state","!=","to be detected")],"invisible":[("results","!=",False)]}'/>
|
||||
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
|
||||
<!-- <field name="is_fetchcnc"-->
|
||||
<!-- attrs='{"invisible":["|",("test_results","=","合格"),("is_remanufacture","=",False)]}'/>-->
|
||||
@@ -491,7 +498,7 @@
|
||||
attrs='{"required":[("test_results","!=","合格")],"invisible":[("test_results","=","合格")]}'/>
|
||||
<!-- <field name="results" readonly="1" attrs='{"invisible":[("results","!=","合格")]}'/>-->
|
||||
<field name="detection_report" attrs='{"invisible":[("results","!=",False)]}'
|
||||
widget="pdf_viewer"/>
|
||||
widget="pdf_viewer" readonly="1"/>
|
||||
</group>
|
||||
<!-- <div class="col-12 col-lg-6 o_setting_box">-->
|
||||
<!-- <button type="object" class="oe_highlight" name="recreateManufacturingOrWorkerOrder"-->
|
||||
@@ -508,6 +515,7 @@
|
||||
<field name="sequence_number"/>
|
||||
<field name="program_name"/>
|
||||
<field name="cutting_tool_name"/>
|
||||
<field name="tool_state"/>
|
||||
<field name="cutting_tool_no"/>
|
||||
<field name="processing_type"/>
|
||||
<field name="margin_x_y"/>
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from . import workpiece_delivery_wizard
|
||||
from . import rework_wizard
|
||||
from . import production_wizard
|
||||
|
||||
21
sf_manufacturing/wizard/production_wizard.py
Normal file
21
sf_manufacturing/wizard/production_wizard.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- 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 ProductionWizard(models.TransientModel):
|
||||
_name = 'sf.production.wizard'
|
||||
_description = '制造订单向导'
|
||||
|
||||
production_id = fields.Many2one('mrp.production', string='制造订单号')
|
||||
is_reprogramming = fields.Boolean(string='申请重新编程', default=True)
|
||||
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True)
|
||||
|
||||
def confirm(self):
|
||||
if self.is_reprogramming is True:
|
||||
self.production_id.update_programming_state()
|
||||
self.production_id.action_cancel()
|
||||
|
||||
34
sf_manufacturing/wizard/production_wizard_views.xml
Normal file
34
sf_manufacturing/wizard/production_wizard_views.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="sf_production_wizard_form_view">
|
||||
<field name="name">sf.production.wizard.form.view</field>
|
||||
<field name="model">sf.production.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<field name="production_id" invisible="True"/>
|
||||
<div>
|
||||
重新生成制造订单
|
||||
<field name="is_remanufacture"/>
|
||||
</div>
|
||||
<div>
|
||||
申请重新编程
|
||||
<field name="is_reprogramming" attrs='{"invisible": [("is_remanufacture","=",False)]}'/>
|
||||
</div>
|
||||
<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_production_wizard" model="ir.actions.act_window">
|
||||
<field name="name">报废</field>
|
||||
<field name="res_model">sf.production.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -24,9 +24,11 @@ class ReworkWizard(models.TransientModel):
|
||||
# 根据工单的加工面来显示
|
||||
processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面")
|
||||
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
|
||||
is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
|
||||
reprogramming_num = fields.Integer('重新编程次数', default=0)
|
||||
programming_state = fields.Selection(
|
||||
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发')],
|
||||
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
|
||||
('已下发', '已下发')],
|
||||
string='编程状态')
|
||||
|
||||
def confirm(self):
|
||||
@@ -43,6 +45,25 @@ class ReworkWizard(models.TransientModel):
|
||||
self.workorder_id.button_finish()
|
||||
else:
|
||||
if self.production_id.workorder_ids:
|
||||
handle_result = self.production_id.detection_result_ids.filtered(
|
||||
lambda dr: dr.handle_result == '待处理')
|
||||
if handle_result:
|
||||
processing_panels_to_handle = set(handle_item.processing_panel for handle_item in handle_result)
|
||||
processing_panels_choice = set(dr_panel.name for dr_panel in self.processing_panel_id)
|
||||
# 使用集合的差集操作找出那些待处理结果中有但实际可用加工面中没有的加工面
|
||||
processing_panels_missing = processing_panels_to_handle - processing_panels_choice
|
||||
# 存在不一致的加工面
|
||||
if processing_panels_missing:
|
||||
processing_panels_str = ','.join(processing_panels_missing)
|
||||
raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
|
||||
# processing_panels = set()
|
||||
# for handle_item in handle_result:
|
||||
# for dr_panel in self.processing_panel_id:
|
||||
# if dr_panel.name == handle_item.processing_panel:
|
||||
# processing_panels.add(dr_panel.name)
|
||||
# if len(processing_panels) != len(handle_result):
|
||||
# processing_panels_str = ','.join(processing_panels)
|
||||
# return UserError(f'您还有待处理的检测结果中为{processing_panels_str}的加工面未选择')
|
||||
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')
|
||||
@@ -59,19 +80,83 @@ class ReworkWizard(models.TransientModel):
|
||||
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.write({'workorder_ids': workorders_values, 'is_rework': True})
|
||||
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 False:
|
||||
self.production_id.get_new_program(panel)
|
||||
if self.programming_state in ['已编程', '已下发']:
|
||||
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
|
||||
self.production_id.get_new_program(panel.name)
|
||||
if self.reprogramming_num >= 0 and self.programming_state == '已下发':
|
||||
ret = {'programming_list': []}
|
||||
cnc_rework = self.production_id.workorder_ids.filtered(
|
||||
lambda crw: crw.processing_panel == panel.name and crw.state in (
|
||||
'rework') and crw.routing_type == 'CNC加工')
|
||||
if cnc_rework.cnc_ids:
|
||||
for item_line in cnc_rework.cnc_ids:
|
||||
vals = {
|
||||
'sequence_number': item_line.sequence_number,
|
||||
'program_name': item_line.program_name,
|
||||
'cutting_tool_name': item_line.cutting_tool_name,
|
||||
'cutting_tool_no': item_line.cutting_tool_no,
|
||||
'processing_type': item_line.processing_type,
|
||||
'margin_x_y': item_line.margin_x_y,
|
||||
'margin_z': item_line.margin_z,
|
||||
'depth_of_processing_z': item_line.depth_of_processing_z,
|
||||
'cutting_tool_extension_length': item_line.cutting_tool_extension_length,
|
||||
'estimated_processing_time': item_line.estimated_processing_time,
|
||||
'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
|
||||
'program_path': item_line.program_path,
|
||||
'ftp_path': item_line.program_path,
|
||||
'processing_panel': panel.name,
|
||||
'program_create_date': datetime.strftime(item_line.program_create_date,
|
||||
'%Y-%m-%d %H:%M:%S'),
|
||||
'remark': item_line.remark
|
||||
}
|
||||
ret['programming_list'].append(vals)
|
||||
for cmm_line in cnc_rework.cmm_ids:
|
||||
vals = {
|
||||
'sequence_number': cmm_line.sequence_number,
|
||||
'program_name': cmm_line.program_name,
|
||||
'program_path': cmm_line.program_path,
|
||||
'ftp_path': item_line.program_path,
|
||||
'processing_panel': panel.name,
|
||||
'program_create_date': datetime.strftime(
|
||||
cmm_line.program_create_date,
|
||||
'%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
ret['programming_list'].append(vals)
|
||||
new_cnc_workorder = self.production_id.workorder_ids.filtered(
|
||||
lambda ap1: ap1.processing_panel == panel.name and ap1.state not in (
|
||||
'rework', 'done') and ap1.routing_type == 'CNC加工')
|
||||
if not new_cnc_workorder.cnc_ids:
|
||||
new_cnc_workorder.write({
|
||||
'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel.name,
|
||||
ret),
|
||||
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name,
|
||||
ret),
|
||||
'cnc_worksheet': new_cnc_workorder.cnc_worksheet})
|
||||
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
|
||||
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
|
||||
'rework', 'done'))
|
||||
if new_pre_workorder:
|
||||
pre_rework = self.production_id.workorder_ids.filtered(
|
||||
lambda pr: pr.processing_panel == panel.name and pr.state in (
|
||||
'rework') and pr.routing_type == '装夹预调')
|
||||
new_pre_workorder.write(
|
||||
{'processing_drawing': pre_rework.processing_drawing})
|
||||
self.production_id.write({'state': 'progress', 'is_rework': False})
|
||||
elif self.programming_state in ['待编程', '编程中']:
|
||||
self.production_id.write(
|
||||
{'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True})
|
||||
if self.is_reprogramming is True:
|
||||
self.production_id.update_programming_state()
|
||||
self.production_id.write(
|
||||
{'programming_state': '编程中', 'work_state': '编程中'})
|
||||
if self.production_id.state == 'progress':
|
||||
self.production_id.write({'programming_state': '已编程'})
|
||||
self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'})
|
||||
|
||||
@api.onchange('production_id')
|
||||
def onchange_processing_panel_id(self):
|
||||
@@ -81,10 +166,14 @@ class ReworkWizard(models.TransientModel):
|
||||
if production_id:
|
||||
if self.env.user.has_group('sf_base.group_sf_order_user'):
|
||||
panel_ids = []
|
||||
panel_arr = production_id.product_id.model_processing_panel
|
||||
for p in production_id.detection_result_ids.filtered(
|
||||
lambda ap1: ap1.handle_result == '待处理'):
|
||||
if p.processing_panel not in panel_arr:
|
||||
panel_arr += ','.join(p.processing_panel)
|
||||
for item in panel_arr.split(','):
|
||||
panel = self.env['sf.processing.panel'].search(
|
||||
[('name', 'ilike', p.processing_panel)])
|
||||
[('name', 'ilike', item)])
|
||||
if panel:
|
||||
panel_ids.append(panel.id)
|
||||
domain = {'processing_panel_id': [('id', 'in', panel_ids)]}
|
||||
|
||||
@@ -20,13 +20,15 @@
|
||||
style='color:red;'/>次,且当前编程状态为
|
||||
<field name="programming_state" string=""
|
||||
decoration-info="programming_state == '待编程'"
|
||||
decoration-success="programming_state == '已编程'"
|
||||
decoration-success="programming_state == '已下发'"
|
||||
decoration-warning="programming_state =='编程中'"
|
||||
decoration-danger="programming_state =='已编程未下发'" readonly="1"/>
|
||||
decoration-danger="programming_state =='已编程'" readonly="1"/>
|
||||
</div>
|
||||
<group>
|
||||
<field name="is_reprogramming"
|
||||
attrs='{"invisible": [("routing_type","in",["装夹预调","CNC加工"])],"readonly": [("programming_state","in",["编程中","已编程未下发"])]}'/>
|
||||
<field name="is_reprogramming" force_save="1"
|
||||
attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","not in",["已下发"])]}'/>
|
||||
<field name="is_reprogramming_readonly" string="申请重新编程"
|
||||
attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","in",["已下发"])],"readonly": [("is_reprogramming_readonly","=",False)]}'/>
|
||||
<field name="rework_reason"
|
||||
attrs='{"invisible": [("routing_type","not in",["装夹预调","CNC加工"])],"required": [("routing_type","in",["装夹预调"])]}'/>
|
||||
<field name="detailed_reason"
|
||||
|
||||
@@ -44,7 +44,6 @@ class ReSaleOrder(models.Model):
|
||||
store=True, readonly=False, precompute=True, check_company=True, tracking=True,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
|
||||
remark = fields.Text('备注')
|
||||
|
||||
validity_date = fields.Date(
|
||||
string="Expiration",
|
||||
compute='_compute_validity_date',
|
||||
|
||||
@@ -313,42 +313,27 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
|
||||
'applicant': None,
|
||||
'sf_functional_tool_assembly_id': None})
|
||||
|
||||
def create_cam_work_plan(self, cnc_processing_ids):
|
||||
def create_cam_work_plan(self, cnc_processing):
|
||||
"""
|
||||
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
||||
"""
|
||||
for cnc_processing in cnc_processing_ids:
|
||||
status = False
|
||||
if cnc_processing.cutting_tool_name:
|
||||
functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search(
|
||||
[('name', '=', cnc_processing.cutting_tool_name)])
|
||||
if functional_tools:
|
||||
for functional_tool in functional_tools:
|
||||
if functional_tool.on_tool_stock_num == 0:
|
||||
if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0:
|
||||
status = True
|
||||
else:
|
||||
status = True
|
||||
if status:
|
||||
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
|
||||
'name': cnc_processing.workorder_id.production_id.name,
|
||||
'cam_procedure_code': cnc_processing.program_name,
|
||||
'filename': cnc_processing.cnc_id.name,
|
||||
'functional_tool_name': cnc_processing.cutting_tool_name,
|
||||
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
|
||||
'process_type': cnc_processing.processing_type,
|
||||
'margin_x_y': float(cnc_processing.margin_x_y),
|
||||
'margin_z': float(cnc_processing.margin_z),
|
||||
'finish_depth': float(cnc_processing.depth_of_processing_z),
|
||||
'extension_length': float(cnc_processing.cutting_tool_extension_length),
|
||||
'shank_model': cnc_processing.cutting_tool_handle_type,
|
||||
'estimated_processing_time': cnc_processing.estimated_processing_time,
|
||||
})
|
||||
logging.info('CAM工单程序用刀计划创建成功!!!')
|
||||
# 创建装刀请求
|
||||
knife_plan.apply_for_tooling()
|
||||
else:
|
||||
logging.info('功能刀具【%s】满足CNC用刀需求!!!' % cnc_processing.cutting_tool_name)
|
||||
knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({
|
||||
'name': cnc_processing.workorder_id.production_id.name,
|
||||
'cam_procedure_code': cnc_processing.program_name,
|
||||
'filename': cnc_processing.cnc_id.name,
|
||||
'functional_tool_name': cnc_processing.cutting_tool_name,
|
||||
'cam_cutter_spacing_code': cnc_processing.cutting_tool_no,
|
||||
'process_type': cnc_processing.processing_type,
|
||||
'margin_x_y': float(cnc_processing.margin_x_y),
|
||||
'margin_z': float(cnc_processing.margin_z),
|
||||
'finish_depth': float(cnc_processing.depth_of_processing_z),
|
||||
'extension_length': float(cnc_processing.cutting_tool_extension_length),
|
||||
'shank_model': cnc_processing.cutting_tool_handle_type,
|
||||
'estimated_processing_time': cnc_processing.estimated_processing_time,
|
||||
})
|
||||
logging.info('CAM工单程序用刀计划创建成功!!!')
|
||||
# 创建装刀请求
|
||||
knife_plan.apply_for_tooling()
|
||||
|
||||
def unlink_cam_plan(self, production):
|
||||
for item in production:
|
||||
@@ -834,7 +819,7 @@ class FunctionalToolDismantle(models.Model):
|
||||
integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', compute='_compute_functional_tool_num',
|
||||
store=True)
|
||||
integral_freight_id = fields.Many2one('sf.shelf.location', '整体式刀具目标货位',
|
||||
domain="[('product_id', 'in', (integral_product_id, False))]")
|
||||
domain="[('product_id', 'in', (integral_product_id, False)),('location_id.name', '=', '刀具房')]")
|
||||
|
||||
# 刀片
|
||||
blade_product_id = fields.Many2one('product.product', string='刀片', compute='_compute_functional_tool_num',
|
||||
@@ -844,7 +829,7 @@ class FunctionalToolDismantle(models.Model):
|
||||
blade_brand_id = fields.Many2one('sf.machine.brand', string='刀片品牌', related='blade_product_id.brand_id')
|
||||
blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', compute='_compute_functional_tool_num', store=True)
|
||||
blade_freight_id = fields.Many2one('sf.shelf.location', '刀片目标货位',
|
||||
domain="[('product_id', 'in', (blade_product_id, False))]")
|
||||
domain="[('product_id', 'in', (blade_product_id, False)),('location_id.name', '=', '刀具房')]")
|
||||
|
||||
# 刀杆
|
||||
bar_product_id = fields.Many2one('product.product', string='刀杆', compute='_compute_functional_tool_num',
|
||||
@@ -854,7 +839,7 @@ class FunctionalToolDismantle(models.Model):
|
||||
bar_brand_id = fields.Many2one('sf.machine.brand', string='刀杆品牌', related='bar_product_id.brand_id')
|
||||
bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', compute='_compute_functional_tool_num', store=True)
|
||||
bar_freight_id = fields.Many2one('sf.shelf.location', '刀杆目标货位',
|
||||
domain="[('product_id', 'in', (bar_product_id, False))]")
|
||||
domain="[('product_id', 'in', (bar_product_id, False)),('location_id.name', '=', '刀具房')]")
|
||||
|
||||
# 刀盘
|
||||
pad_product_id = fields.Many2one('product.product', string='刀盘', compute='_compute_functional_tool_num',
|
||||
@@ -864,7 +849,7 @@ class FunctionalToolDismantle(models.Model):
|
||||
pad_brand_id = fields.Many2one('sf.machine.brand', string='刀盘品牌', related='pad_product_id.brand_id')
|
||||
pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', compute='_compute_functional_tool_num', store=True)
|
||||
pad_freight_id = fields.Many2one('sf.shelf.location', '刀盘目标货位',
|
||||
domain="[('product_id', 'in', (pad_product_id, False))]")
|
||||
domain="[('product_id', 'in', (pad_product_id, False)),('location_id.name', '=', '刀具房')]")
|
||||
|
||||
# 夹头
|
||||
chuck_product_id = fields.Many2one('product.product', string='夹头', compute='_compute_functional_tool_num',
|
||||
@@ -874,7 +859,7 @@ class FunctionalToolDismantle(models.Model):
|
||||
chuck_brand_id = fields.Many2one('sf.machine.brand', string='夹头品牌', related='chuck_product_id.brand_id')
|
||||
chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', compute='_compute_functional_tool_num', store=True)
|
||||
chuck_freight_id = fields.Many2one('sf.shelf.location', '夹头目标货位',
|
||||
domain="[('product_id', 'in', (chuck_product_id, False))]")
|
||||
domain="[('product_id', 'in', (chuck_product_id, False)),('location_id.name', '=', '刀具房')]")
|
||||
|
||||
@api.onchange('functional_tool_id')
|
||||
def _onchange_freight(self):
|
||||
|
||||
@@ -255,6 +255,16 @@ class FunctionalCuttingToolEntity(models.Model):
|
||||
result['domain'] = [('id', '=', self.safe_inventory_id.id)]
|
||||
return result
|
||||
|
||||
def cnc_function_tool_use_verify(self):
|
||||
"""
|
||||
cnc程序用刀可用校验(校验是否是制造订单所缺刀)
|
||||
"""
|
||||
if self.tool_name_id.name:
|
||||
cnc_processing_ids = self.env['sf.cnc.processing'].search(
|
||||
[('tool_state', '=', '1'), ('cutting_tool_name', '=', self.tool_name_id.name)])
|
||||
if cnc_processing_ids:
|
||||
cnc_processing_ids.sudo().write({'tool_state': '0'})
|
||||
|
||||
def tool_inventory_displacement_out(self):
|
||||
"""
|
||||
机床当前刀库实时信息接口,功能刀具出库
|
||||
@@ -265,6 +275,7 @@ class FunctionalCuttingToolEntity(models.Model):
|
||||
self.create_stock_move(stock_location_id, False)
|
||||
self.current_location_id = stock_location_id.id
|
||||
self.current_shelf_location_id = False
|
||||
|
||||
# self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id,
|
||||
# self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id,
|
||||
# self.functional_tool_name_id.tool_groups_id)
|
||||
|
||||
@@ -29,13 +29,113 @@ class CNCprocessing(models.Model):
|
||||
# else:
|
||||
# raise ValidationError("MES装刀指令发送失败")
|
||||
|
||||
def cnc_tool_checkout(self, cnc_processing_ids):
|
||||
"""
|
||||
根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划
|
||||
"""
|
||||
logging.info('开始进行工单cnc程序用刀校验!!!')
|
||||
logging.info(f'cnc_processing_ids:{cnc_processing_ids}')
|
||||
if not cnc_processing_ids:
|
||||
return False
|
||||
cam_id = self.env['sf.cam.work.order.program.knife.plan']
|
||||
production_ids = [] # 制造订单集
|
||||
datas = {'缺刀': {}, '无效刀': {}} # 缺刀/无效刀集
|
||||
for cnc_processing in cnc_processing_ids:
|
||||
# ======创建字典: {'缺刀': {'制造订单1': {'加工面1': [], ...}, ...}, '无效刀': {'制造订单1': {'加工面1': [], ...}, ...}}======
|
||||
production_name = cnc_processing.workorder_id.production_id.name # 制造订单
|
||||
processing_panel = cnc_processing.workorder_id.processing_panel # 加工面
|
||||
if production_name not in list(datas['缺刀'].keys()):
|
||||
datas['缺刀'].update({production_name: {processing_panel: []}})
|
||||
datas['无效刀'].update({production_name: {processing_panel: []}})
|
||||
production_ids.append(cnc_processing.workorder_id.production_id)
|
||||
else:
|
||||
if processing_panel not in list(datas['缺刀'].get(production_name).keys()):
|
||||
datas['缺刀'].get(production_name).update({processing_panel: []})
|
||||
datas['无效刀'].get(production_name).update({processing_panel: []})
|
||||
# ======================================
|
||||
if cnc_processing.cutting_tool_name:
|
||||
tool_name = cnc_processing.cutting_tool_name
|
||||
# 检验CNC用刀是否是功能刀具清单中的刀具
|
||||
tool_inventory_id = self.env['sf.tool.inventory'].sudo().search([('name', '=', tool_name)])
|
||||
if not tool_inventory_id:
|
||||
if cnc_processing.cutting_tool_name not in datas['无效刀'][production_name][processing_panel]:
|
||||
datas['无效刀'][production_name][processing_panel].append(cnc_processing.cutting_tool_name)
|
||||
cnc_processing.tool_state = '2'
|
||||
logging.info(f'"无效刀":[{production_name}、{processing_panel}、{cnc_processing.cutting_tool_name}]')
|
||||
# 跳过本次循环
|
||||
continue
|
||||
# 校验CNC用刀在系统是否存在
|
||||
functional_tools = self.env['sf.functional.cutting.tool.entity'].sudo().search(
|
||||
[('tool_name_id', '=', tool_inventory_id.id), ('functional_tool_status', '=', '正常')])
|
||||
# 判断线边、机内是否有满足条件的刀
|
||||
if not functional_tools.filtered(lambda p: p.current_location in ('线边刀库', '机内刀库')):
|
||||
if cnc_processing.cutting_tool_name not in datas['缺刀'][production_name][processing_panel]:
|
||||
datas['缺刀'][production_name][processing_panel].append(cnc_processing.cutting_tool_name)
|
||||
cnc_processing.tool_state = '1'
|
||||
logging.info(f'"缺刀":[{production_name}、{processing_panel}、{cnc_processing.cutting_tool_name}]')
|
||||
# 判断是否有满足条件的刀
|
||||
if not functional_tools:
|
||||
# 创建CAM申请装刀记录
|
||||
cam_id.create_cam_work_plan(cnc_processing)
|
||||
logging.info('成功调用CAM工单程序用刀计划创建方法!!!')
|
||||
logging.info(f'datas:{datas}')
|
||||
for production_id in production_ids:
|
||||
logging.info(f'production_id: {production_id}')
|
||||
if production_id:
|
||||
data1 = datas['无效刀'].get(production_id.name) # data1: {'加工面1': [], ...}
|
||||
data2 = datas['缺刀'].get(production_id.name) # data2: {'加工面1': [], ...}
|
||||
tool_state_remark1 = ''
|
||||
tool_state_remark2 = ''
|
||||
# 对无效刀信息进行处理
|
||||
for key in data1:
|
||||
if data1.get(key):
|
||||
if tool_state_remark1 != '':
|
||||
tool_state_remark1 = f'{tool_state_remark1}\n{key}无效刀:{data1.get(key)}'
|
||||
else:
|
||||
tool_state_remark1 = f'{key}无效刀:{data1.get(key)}'
|
||||
# 无效刀处理逻辑
|
||||
# 1、创建制造订单无效刀检测结果记录
|
||||
logging.info('创建制造订单无效刀检测结果记录!')
|
||||
production_id.detection_result_ids.create({
|
||||
'production_id': production_id.id,
|
||||
'processing_panel': key,
|
||||
'routing_type': 'CNC加工',
|
||||
'rework_reason': 'programming', # 原因:编程(programming)
|
||||
'detailed_reason': '无效功能刀具',
|
||||
'test_results': '返工',
|
||||
'handle_result': '待处理'
|
||||
})
|
||||
# 2、将制造订单状态改为返工
|
||||
# production_id.write({
|
||||
# 'state': 'rework'
|
||||
# })
|
||||
# 对缺刀信息进行处理
|
||||
if tool_state_remark1 == '':
|
||||
for key in data2:
|
||||
if data2.get(key) and not data1.get(key):
|
||||
if tool_state_remark2 != '':
|
||||
tool_state_remark2 = f'{tool_state_remark2}\n{key}缺刀:{data2.get(key)}'
|
||||
else:
|
||||
tool_state_remark2 = f'{key}缺刀:{data2.get(key)}'
|
||||
# 将备注信息存入制造订单功能刀具状态的备注字段
|
||||
logging.info('修改制造订单功能刀具状态的备注字段')
|
||||
if production_id.tool_state_remark2 == '':
|
||||
production_id.write({
|
||||
'tool_state_remark': tool_state_remark2,
|
||||
'tool_state_remark2': tool_state_remark1
|
||||
})
|
||||
else:
|
||||
production_id.write({
|
||||
'tool_state_remark': tool_state_remark2,
|
||||
'tool_state_remark2': f'{production_id.tool_state_remark1}\n{tool_state_remark1}'
|
||||
})
|
||||
logging.info('工单cnc程序用刀校验已完成!')
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals):
|
||||
obj = super(CNCprocessing, self).create(vals)
|
||||
for item in obj:
|
||||
# 调用CAM工单程序用刀计划创建方法
|
||||
self.env['sf.cam.work.order.program.knife.plan'].create_cam_work_plan(item)
|
||||
logging.info('成功调用CAM工单程序用刀计划创建方法!!!')
|
||||
# 调用CAM工单程序用刀计划创建方法
|
||||
self.cnc_tool_checkout(obj)
|
||||
return obj
|
||||
|
||||
|
||||
|
||||
@@ -21,3 +21,40 @@ class ShelfLocation(models.Model):
|
||||
continue
|
||||
item.tool_rfid = ''
|
||||
item.tool_name_id = False
|
||||
|
||||
|
||||
class StockMoveLine(models.Model):
|
||||
_inherit = 'stock.move.line'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
records = super(StockMoveLine, self).create(vals_list)
|
||||
move_lines = records.filtered(lambda a: a.product_id.categ_id.name == '功能刀具' and a.state == 'done')
|
||||
if move_lines: # 校验是否为功能刀具移动历史
|
||||
self.button_function_tool_use_verify(move_lines)
|
||||
return records
|
||||
|
||||
def button_function_tool_use_verify(self, move_lines):
|
||||
"""
|
||||
对所有从【刀具房】到【制造前】的功能刀具进行校验(校验是否为制造订单所缺的刀)
|
||||
"""
|
||||
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
||||
location_dest_id = self.env['stock.location'].search([('name', '=', '制造前')])
|
||||
line_ids = move_lines.filtered(
|
||||
lambda a: a.location_id == location_id and a.location_dest_id == location_dest_id)
|
||||
for line_id in line_ids:
|
||||
if line_id.lot_id:
|
||||
self.env['sf.functional.cutting.tool.entity'].sudo().search(
|
||||
[('barcode_id', '=', line_id.lot_id.id),
|
||||
('functional_tool_status', '=', '正常')]).cnc_function_tool_use_verify()
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
def button_validate(self):
|
||||
res = super().button_validate()
|
||||
move_lines = self.move_line_ids.filtered(lambda a: a.product_id.categ_id.name == '功能刀具')
|
||||
if move_lines:
|
||||
self.env['stock.move.line'].sudo().button_function_tool_use_verify(move_lines)
|
||||
return res
|
||||
|
||||
Reference in New Issue
Block a user