Files
test/sf_manufacturing/models/mrp_workorder.py
2024-09-14 14:32:40 +08:00

1909 lines
99 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import re
import json
import logging
import base64
import urllib.parse
from datetime import date
from datetime import datetime, timedelta
import requests
import os
import math
from lxml import etree
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, ValidationError
from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
_order = 'sequence asc'
product_tmpl_name = fields.Char('坯料产品名称', related='production_bom_id.bom_line_ids.product_id.name')
product_tmpl_id_length = fields.Float(related='production_id.product_tmpl_id.length', readonly=True, store=True,
string="坯料长度(mm)")
product_tmpl_id_width = fields.Float(related='production_id.product_tmpl_id.width', readonly=True, store=True,
string="坯料宽度(mm)")
product_tmpl_id_height = fields.Float(related='production_id.product_tmpl_id.height', readonly=True, store=True,
string="坯料高度(mm)")
product_tmpl_id_materials_id = fields.Many2one(related='production_id.product_tmpl_id.materials_id', readonly=True,
store=True, check_company=True, string="材料")
product_tmpl_id_materials_type_id = fields.Many2one(related='production_id.product_tmpl_id.materials_type_id',
readonly=True, store=True, check_company=True, string="型号")
workcenter_id = fields.Many2one('mrp.workcenter', string='工作中心', required=False)
users_ids = fields.Many2many("res.users", 'users_workorder', related="workcenter_id.users_ids")
processing_panel = fields.Char('加工面')
sequence = fields.Integer(string='工序')
routing_type = fields.Selection([
# ('获取CNC加工程序', '获取CNC加工程序'),
('装夹预调', '装夹预调'),
# ('前置三元定位检测', '前置三元定位检测'),
('CNC加工', 'CNC加工'),
# ('后置三元质量检测', '后置三元质量检测'),
('解除装夹', '解除装夹'),
('切割', '切割'), ('表面工艺', '表面工艺')
], string="工序类型")
results = fields.Char('结果')
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)
@api.depends('production_id.manual_quotation')
def _compute_manual_quotation(self):
for item in self:
item.manual_quotation = item.production_id.manual_quotation
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
def _compute_working_users(self):
super()._compute_working_users()
for item in self:
if item.state == 'to be detected':
if self.env.user.has_group('sf_base.group_sf_equipment_user'):
item.is_user_working = True
@api.onchange('users_ids')
def get_user_permissions(self):
uid = self.env.uid
for workorder in self:
if workorder.users_ids:
list_user_id = []
for item in workorder.users_ids:
list_user_id.append(item.id)
if uid in list_user_id:
workorder.user_permissions = True
else:
workorder.user_permissions = False
else:
workorder.user_permissions = False
user_permissions = fields.Boolean('用户权限', compute='get_user_permissions')
programming_no = fields.Char('编程单号')
work_state = fields.Char('业务状态')
programming_state = fields.Char('编程状态')
cnc_worksheet = fields.Binary(
'工作指令', readonly=True)
material_center_point = fields.Char(string='坯料中心点')
X1_axis = fields.Float(default=0)
Y1_axis = fields.Float(default=0)
Z1_axis = fields.Float(default=0)
X2_axis = fields.Float(default=0)
Y2_axis = fields.Float(default=0)
Z2_axis = fields.Float(default=0)
X3_axis = fields.Float(default=0)
Y3_axis = fields.Float(default=0)
Z3_axis = fields.Float(default=0)
X4_axis = fields.Float(default=0)
Y4_axis = fields.Float(default=0)
Z4_axis = fields.Float(default=0)
X5_axis = fields.Float(default=0)
Y5_axis = fields.Float(default=0)
Z5_axis = fields.Float(default=0)
X6_axis = fields.Float(default=0)
Y6_axis = fields.Float(default=0)
Z6_axis = fields.Float(default=0)
X7_axis = fields.Float(default=0)
Y7_axis = fields.Float(default=0)
Z7_axis = fields.Float(default=0)
X8_axis = fields.Float(default=0)
Y8_axis = fields.Float(default=0)
Z8_axis = fields.Float(default=0)
X9_axis = fields.Float(default=0)
Y9_axis = fields.Float(default=0)
Z9_axis = fields.Float(default=0)
X10_axis = fields.Float(default=0)
Y10_axis = fields.Float(default=0)
Z10_axis = fields.Float(default=0)
X_deviation_angle = fields.Integer(string="X轴偏差度", default=0)
test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格',
string="检测结果", tracking=True)
cnc_ids = fields.One2many("sf.cnc.processing", 'workorder_id', string="CNC加工程序")
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
tray_code = fields.Char(string="托盘编码")
glb_file = fields.Binary("glb模型文件", related='production_id.model_file')
is_subcontract = fields.Boolean(string='是否外协')
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
picking_ids = fields.Many2many('stock.picking', string='外协出入库单')
# purchase_id = fields.Many2one('purchase.order', string='外协采购单')
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
# 是否绑定托盘
is_trayed = fields.Boolean(string='是否绑定托盘', default=False)
@api.depends('name', 'production_id.name')
def _compute_surface_technics_picking_ids(self):
for workorder in self:
if workorder.routing_type == '表面工艺':
domain = [('origin', '=', workorder.production_id.name)]
previous_workorder = self.env['mrp.workorder'].search(
[('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
('production_id', '=', workorder.production_id.id)])
if previous_workorder:
process_product = self.env['product.template']._get_process_parameters_product(
previous_workorder.surface_technics_parameters_id)
domain += [('partner_id', '=', process_product.partner_id.id)]
else:
domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
picking_ids = self.env['stock.picking'].search(domain, order='id asc')
workorder.surface_technics_picking_count = len(picking_ids)
workorder.picking_ids = picking_ids.ids
else:
workorder.surface_technics_picking_count = 0
def action_view_surface_technics_picking(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
if len(self.picking_ids) > 1:
action['domain'] = [('id', 'in', self.picking_ids.ids)]
elif self.picking_ids:
# action['name'] = '工艺外协'
action['res_id'] = self.picking_ids.id
action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
if 'views' in action:
action['views'] += [(state, view) for state, view in action['views'] if view != 'form']
action['context'] = dict(self._context, default_origin=self.name)
return action
@api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self):
for order in self:
if order.routing_type == '表面工艺':
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', order.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming]
purchase = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
for line in purchase.order_line:
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id and line.product_qty == len(
production_no_remanufacture):
order.surface_technics_purchase_count = len(purchase)
else:
order.surface_technics_purchase_count = 0
def action_view_surface_technics_purchase(self):
self.ensure_one()
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
result = {
"type": "ir.actions.act_window",
"res_model": "purchase.order",
"res_id": purchase_orders.id,
# "domain": [['id', 'in', self.purchase_id]],
"name": _("Purchase Orders"),
'view_mode': 'form',
}
return result
supplier_id = fields.Many2one('res.partner', string='外协供应商')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
# 保存名称
save_name = fields.Char(string='检测文件保存名称', compute='_compute_save_name')
# 获取数据状态
data_state = fields.Boolean(string='获取数据状态', default=False)
# 坯料长宽高
material_length = fields.Float(string='')
material_width = fields.Float(string='')
material_height = fields.Float(string='')
# 零件图号
part_number = fields.Char(string='零件图号')
# 工序状态
process_state = fields.Selection([
('待装夹', '待装夹'),
('待检测', '待检测'),
('待加工', '装夹预调完工'),
('待解除装夹', '待解除装夹'),
('已完工', '已完工'),
], string='工序状态', default='待装夹', readonly='True')
# 加工图纸
processing_drawing = fields.Binary(string='加工图纸')
# 功能刀具状态
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
store=True, compute='_compute_tool_state')
tool_state_remark = fields.Text(string='功能刀具状态备注(缺刀)', compute='_compute_tool_state_remark', store=True)
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
@api.depends('cnc_ids.tool_state')
def _compute_tool_state_remark(self):
for item in self:
if item.cnc_ids:
if item.cnc_ids.filtered(lambda a: a.tool_state == '2'):
item.tool_state_remark = None
elif item.cnc_ids.filtered(lambda a: a.tool_state == '1'):
tool_state_remark = []
cnc_ids = item.cnc_ids.filtered(lambda a: a.tool_state == '1')
for cnc_id in cnc_ids:
if cnc_id.cutting_tool_name not in tool_state_remark:
tool_state_remark.append(cnc_id.cutting_tool_name)
item.tool_state_remark = f"{item.processing_panel}缺刀:{tool_state_remark}"
else:
item.tool_state_remark = None
@api.depends('cnc_ids.tool_state')
def _compute_tool_state(self):
for item in self:
if item.cnc_ids:
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):
"""
保存名称
"""
for record in self:
tem_name = record.production_id.name.replace('/', '_')
record.save_name = tem_name + '_' + record.processing_panel
schedule_state = fields.Selection(related='production_id.schedule_state', store=True)
# 工件装夹信息
functional_fixture_code = fields.Char(string="功能夹具编码", readonly=True)
functional_fixture_serial_number = fields.Char(string="功能夹具序列号", readonly=True)
functional_fixture_id = fields.Many2one('sf.functional.fixture', string="功能夹具")
functional_fixture_type_id = fields.Many2one('sf.functional.fixture.type', string="功能夹具类型", readonly=True)
chuck_serial_number = fields.Char(string="卡盘序列号")
chuck_name = fields.Char(string="卡盘名称")
chuck_brand_id = fields.Many2one('sf.machine.brand', string="卡盘品牌")
chuck_type_id = fields.Char(string="卡盘类型")
chuck_model_id = fields.Char(string="卡盘型号")
tray_serial_number = fields.Char(string="托盘序列号")
tray_product_id = fields.Many2one('product.product', string="托盘名称")
tray_brand_id = fields.Many2one('sf.machine.brand', string="托盘品牌")
tray_type_id = fields.Many2one('sf.fixture.material', string="托盘类型")
tray_model_id = fields.Many2one('sf.fixture.model', string="托盘型号")
total_wight = fields.Float(string="总重量")
maximum_carrying_weight = fields.Char(string="最大承载重量[kg]")
maximum_clamping_force = fields.Char(string="最大夹持力[n]")
preset_program_information = fields.Char(string="预调程序信息")
workpiece_delivery_ids = fields.One2many('sf.workpiece.delivery', 'workorder_id', '工件配送')
is_delivery = fields.Boolean('是否配送完成', default=False)
rfid_code = fields.Char('RFID码')
rfid_code_old = fields.Char('RFID码(已解除)')
production_line_id = fields.Many2one('sf.production.line', related='production_id.production_line_id',
string='生产线', store=True, tracking=True)
production_line_state = fields.Selection(
[('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')],
string='上/下产线', default='待上产线', tracking=True)
detection_report = fields.Binary('检测报告', readonly=False)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False)
is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False)
reason = fields.Selection(
[("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"),
("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True)
detailed_reason = fields.Text('详细原因')
is_rework = fields.Boolean(string='是否返工', default=False)
@api.constrains('blocked_by_workorder_ids')
def _check_no_cyclic_dependencies(self):
if self.production_id.state not in ['rework'] and self.state not in ['rework']:
if not self._check_m2m_recursion('blocked_by_workorder_ids'):
raise ValidationError(_("您不能创建周期性的依赖关系."))
def _plan_workorder(self, replan=False):
self.ensure_one()
# Plan workorder after its predecessors
start_date = max(self.production_id.date_planned_start, datetime.now())
for workorder in self.blocked_by_workorder_ids:
if workorder.state in ['done', 'cancel', 'rework']:
continue
workorder._plan_workorder(replan)
start_date = max(start_date, workorder.date_planned_finished)
# Plan only suitable workorders
if self.state not in ['pending', 'waiting', 'ready']:
return
if self.leave_id:
if replan:
self.leave_id.unlink()
else:
return
# Consider workcenter and alternatives
workcenters = self.workcenter_id | self.workcenter_id.alternative_workcenter_ids
best_finished_date = datetime.max
vals = {}
for workcenter in workcenters:
# Compute theoretical duration
if self.workcenter_id == workcenter:
duration_expected = self.duration_expected
else:
duration_expected = self._get_duration_expected(alternative_workcenter=workcenter)
from_date, to_date = workcenter._get_first_available_slot(start_date, duration_expected)
# If the workcenter is unavailable, try planning on the next one
if not from_date:
continue
# Check if this workcenter is better than the previous ones
if to_date and to_date < best_finished_date:
best_start_date = from_date
best_finished_date = to_date
best_workcenter = workcenter
vals = {
'workcenter_id': workcenter.id,
'duration_expected': duration_expected,
}
# If none of the workcenter are available, raise
if best_finished_date == datetime.max:
raise UserError(_('Impossible to plan the workorder. Please check the workcenter availabilities.'))
# Create leave on chosen workcenter calendar
leave = self.env['resource.calendar.leaves'].create({
'name': self.display_name,
'calendar_id': best_workcenter.resource_calendar_id.id,
'date_from': best_start_date,
'date_to': best_finished_date,
'resource_id': best_workcenter.resource_id.id,
'time_type': 'other'
})
vals['leave_id'] = leave.id
self.write(vals)
# @api.onchange('rfid_code')
# def _onchange(self):
# if self.rfid_code and self.state == 'progress':
# self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
def get_plan_workorder(self, production_line):
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
tomorrow_start = tomorrow + ' 00:00:00'
tomorrow_end = tomorrow + ' 23:59:59'
logging.info('tomorrow:%s' % tomorrow)
sql = """
SELECT *
FROM mrp_workorder
WHERE state!='rework'
to_char(date_planned_start::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')>= %s
AND to_char(date_planned_finished::timestamp + '8 hour','YYYY-MM-DD HH:mm:SS')<= %s
"""
params = [tomorrow_start, tomorrow_end]
if production_line:
sql += "AND production_line_id = %s"
params.append(production_line)
self.env.cr.execute(sql, params)
ids = [t[0] for t in self.env.cr.fetchall()]
return [('id', 'in', ids)]
@api.onchange('functional_fixture_id')
def _onchange_functional_fixture_id(self):
if self.functional_fixture_id:
self.functional_fixture_code = self.functional_fixture_id.code
self.functional_fixture_type_id = self.functional_fixture_id.type_id.id
def get_no_data(self, production_id):
process_parameter_workorder = self.search(
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_id)])
return process_parameter_workorder
# 获取三次元检测点数据
def get_three_check_datas(self):
ftp_resconfig = self.env['res.config.settings'].get_values()
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']),
ftp_resconfig['ftp_user'],
ftp_resconfig['ftp_password'])
local_dir_path = '/ftp/before'
os.makedirs(local_dir_path, exist_ok=True)
local_filename = self.save_name + '.xls'
local_file_path = os.path.join(local_dir_path, local_filename)
logging.info('local_file_path:%s' % local_file_path)
remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename
logging.info('remote_path:%s' % remote_path)
if not ftp.file_exists(remote_path):
paload_data = {
"filename": local_filename
}
if not ftp_resconfig['get_check_file_path']:
raise UserError('请先配置获取检测报告地址')
url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
response = requests.post(url, json=paload_data)
logging.info('response:%s' % response.json())
if response.json().get('detail'):
raise UserError(response.json().get('detail'))
if not ftp.file_exists(remote_path):
raise UserError(f"文件不存在: {remote_path}")
with open(local_file_path, 'wb') as local_file:
ftp.ftp.retrbinary('RETR ' + remote_path, local_file.write)
logging.info('下载文件成功')
# 解析本地文件
# file_path = 'WH_MO_00099.xls' # 使用下载的实际文件路径
parser = etree.XMLParser(recover=True) # Using recover to handle errors
tree = etree.parse(local_file_path, parser)
logging.info('tree:%s' % tree)
root = tree.getroot()
logging.info('root:%s' % root)
# 准备一个外部字典来存储以PT为键的坐标字典
pt_coordinates = {}
# 遍历每个工作表和行
for worksheet in root.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Worksheet'):
sheet_name = worksheet.attrib.get('{urn:schemas-microsoft-com:office:spreadsheet}Name')
logging.info('sheet_name:%s' % sheet_name)
if sheet_name == "Sheet1": # 确保我们只查看包含数据的工作表
current_pt = None
for row in worksheet.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Row'):
cells = list(row.iterfind('.//{urn:schemas-microsoft-com:office:spreadsheet}Cell'))
for i, cell in enumerate(cells):
data_cell = cell.find('.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
if data_cell is not None and data_cell.text is not None: # 添加检查以确保data_cell.text不为空
# 检查是否是PT标识
logging.info(f"Data in cell: {data_cell.text}") # 输出单元格数据
if "PT" in data_cell.text:
current_pt = data_cell.text
pt_coordinates[current_pt] = []
elif data_cell.text in ["X", "Y", "Z"] and current_pt is not None:
# 确保当前单元格后面还有单元格存在,以获取理论值
if i + 1 < len(cells):
next_cell = cells[i + 1]
theory_value = next_cell.find(
'.//{urn:schemas-microsoft-com:office:spreadsheet}Data')
if theory_value is not None:
# 为当前PT键添加坐标数据
pt_coordinates[current_pt].append({
data_cell.text: float(theory_value.text)
})
logging.info(f"PT: {current_pt} - {data_cell.text}: {theory_value.text}")
logging.info('pt_coordinates=====%s' % pt_coordinates)
# pt_coordinates:{'PT1': [{'X': 38.9221}, {'Y': -18.7304}, {'Z': 128.0783}],
# 'PT2': [{'X': 39.2456}, {'Y': -76.9169}, {'Z': 123.7541}]}
# 检查是否存在PT1等键
if 'PT1' in pt_coordinates and pt_coordinates['PT1']:
self.X1_axis = pt_coordinates['PT3'][0]['X']
self.Y1_axis = pt_coordinates['PT3'][1]['Y']
self.Z1_axis = pt_coordinates['PT3'][2]['Z']
else:
raise UserError('PT1点未测或数据错误')
if 'PT2' in pt_coordinates and pt_coordinates['PT2']:
self.X2_axis = pt_coordinates['PT4'][0]['X']
self.Y2_axis = pt_coordinates['PT4'][1]['Y']
self.Z2_axis = pt_coordinates['PT4'][2]['Z']
else:
raise UserError('PT2点未测或数据错误')
if 'PT3' in pt_coordinates and pt_coordinates['PT3']:
self.X3_axis = pt_coordinates['PT5'][0]['X']
self.Y3_axis = pt_coordinates['PT5'][1]['Y']
self.Z3_axis = pt_coordinates['PT5'][2]['Z']
else:
raise UserError('PT3点未测或数据错误')
if 'PT4' in pt_coordinates and pt_coordinates['PT4']:
self.X4_axis = pt_coordinates['PT6'][0]['X']
self.Y4_axis = pt_coordinates['PT6'][1]['Y']
self.Z4_axis = pt_coordinates['PT6'][2]['Z']
else:
raise UserError('PT4点未测或数据错误')
if 'PT5' in pt_coordinates and pt_coordinates['PT5']:
self.X5_axis = pt_coordinates['PT7'][0]['X']
self.Y5_axis = pt_coordinates['PT7'][1]['Y']
self.Z5_axis = pt_coordinates['PT7'][2]['Z']
else:
raise UserError('PT5点未测或数据错误')
if 'PT6' in pt_coordinates and pt_coordinates['PT6']:
self.X6_axis = pt_coordinates['PT8'][0]['X']
self.Y6_axis = pt_coordinates['PT8'][1]['Y']
self.Z6_axis = pt_coordinates['PT8'][2]['Z']
else:
raise UserError('PT6点未测或数据错误')
if 'PT7' in pt_coordinates and pt_coordinates['PT7']:
self.X7_axis = pt_coordinates['PT9'][0]['X']
self.Y7_axis = pt_coordinates['PT9'][1]['Y']
self.Z7_axis = pt_coordinates['PT9'][2]['Z']
else:
raise UserError('PT7点未测或数据错误')
if 'PT8' in pt_coordinates and pt_coordinates['PT8']:
self.X8_axis = pt_coordinates['PT10'][0]['X']
self.Y8_axis = pt_coordinates['PT10'][1]['Y']
self.Z8_axis = pt_coordinates['PT10'][2]['Z']
else:
raise UserError('PT8点未测或数据错误')
if 'PT9' in pt_coordinates and pt_coordinates['PT9']:
self.X9_axis = pt_coordinates['PT1'][0]['X']
self.Y9_axis = pt_coordinates['PT1'][1]['Y']
self.Z9_axis = pt_coordinates['PT1'][2]['Z']
else:
raise UserError('PT9点未测或数据错误')
if 'PT10' in pt_coordinates and pt_coordinates['PT10']:
self.X10_axis = pt_coordinates['PT2'][0]['X']
self.Y10_axis = pt_coordinates['PT2'][1]['Y']
self.Z10_axis = pt_coordinates['PT2'][2]['Z']
else:
raise UserError('PT10点未测或数据错误')
self.data_state = True
self.getcenter()
return True
# ftp.download_file('three_check_datas.xls', '/home/ftpuser/three_check_datas.xls')
# ftp.close()
# data = xlrd.open_workbook('/home/ftpuser/three_check_datas.xls')
# table = data.sheets()[0]
# nrows = table.nrows
# # 点坐标列表
# point_list = []
# datas = []
# for i in range(1, nrows):
# datas.append(table.row_values(i))
# return datas
# 计算配料中心点和与x轴倾斜度方法
def getcenter(self):
try:
x1 = self.X1_axis
x2 = self.X2_axis
x3 = self.X3_axis
x4 = self.X4_axis
x5 = self.X5_axis
x6 = self.X6_axis
x7 = self.X7_axis
x8 = self.X8_axis
y1 = self.Y1_axis
y2 = self.Y2_axis
y3 = self.Y3_axis
y4 = self.Y4_axis
y5 = self.Y5_axis
y6 = self.Y6_axis
y7 = self.Y7_axis
y8 = self.Y8_axis
z1 = self.Z9_axis
z2 = self.Z10_axis
x0 = ((x3 - x4) * (x2 * y1 - x1 * y2) - (x1 - x2) * (x4 * y3 - x3 * y4)) / (
(x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4))
y0 = ((y3 - y4) * (y2 * x1 - y1 * x2) - (y1 - y2) * (y4 * x3 - y3 * x4)) / (
(y3 - y4) * (x1 - x2) - (y1 - y2) * (x3 - x4))
x1 = ((x7 - x8) * (x6 * y5 - x5 * y6) - (x5 - x6) * (x8 * y7 - x7 * y8)) / (
(x7 - x8) * (y5 - y6) - (x5 - x6) * (y7 - y8))
y1 = ((y7 - y8) * (y6 * x5 - y5 * x6) - (y5 - y6) * (y8 * x7 - y7 * x8)) / (
(y7 - y8) * (x5 - x6) - (y5 - y6) * (x7 - x8))
x = (x0 + x1) / 2
y = (y0 + y1) / 2
z = (z1 + z2) / 2
jd = math.atan2((x5 - x6), (y5 - y6))
jdz = jd * 180 / math.pi
print("(%.2f,%.2f)" % (x, y))
self.material_center_point = ("(%.2f,%.2f,%.2f)" % (x, y, z))
self.X_deviation_angle = jdz
# 将补偿值写入CNC加工工单
workorder = self.env['mrp.workorder'].browse(self.ids)
work = workorder.production_id.workorder_ids
work.compensation_value_x = eval(self.material_center_point)[0]
work.compensation_value_y = eval(self.material_center_point)[1]
# work.process_state = '待加工'
# self.sudo().production_id.process_state = '待加工'
# self.sudo().production_id.process_state = '待加工'
self.date_finished = datetime.now()
workorder.button_finish()
except Exception as e:
# 重新抛出捕获到的异常信息
raise UserError(str(e))
def button_workpiece_delivery(self):
if self.routing_type == '装夹预调':
for item in self.workpiece_delivery_ids:
if not item.route_id:
raise UserError('【工件配送】明细中请选择【任务路线】')
else:
if self.state == 'done':
if item.is_cnc_program_down is True:
if item.status == '待下发':
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_workorder_id': self.id,
}}
else:
raise UserError(_("该制造订单还未下发CNC程序单无法进行工件配送"))
else:
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_programming_state': self.production_id.programming_state,
'default_routing_type': self.routing_type
}}
# 拼接工单对象属性值
def json_workorder_str(self, k, production, route, item):
# 计算预计时长duration_expected
routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹']
if route.routing_type in routing_types:
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', route.routing_type)])
duration_expected = routing_workcenter.time_cycle
reserved_duration = routing_workcenter.reserved_duration
# if route.routing_type == '切割':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '切割')]).time_cycle
# # elif route.routing_type == '获取CNC加工程序':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '获取CNC加工程序')]).time_cycle
# elif route.routing_type == '装夹预调':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '装夹预调')]).time_cycle
# # elif route.routing_type == '前置三元定位检测':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '前置三元定位检测')]).time_cycle
# elif route.routing_type == 'CNC加工':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', 'CNC加工')]).time_cycle
# # elif route.routing_type == '后置三元质量检测':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '后置三元质量检测')]).time_cycle
# elif route.routing_type == '解除装夹':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '解除装夹')]).time_cycle
else:
duration_expected = 60
reserved_duration = 30
workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id,
'qty_producing': 0,
'operation_id': False,
'name': route.route_workcenter_id.name,
'processing_panel': k,
'quality_point_ids': route.route_workcenter_id.quality_point_ids,
'routing_type': route.routing_type,
# 'work_state': '待发起',
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
# 设定初始化值避免出现变成bool问题
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': duration_expected,
'duration': 0,
'cnc_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cnc.processing']._json_cnc_processing(
k, item),
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
item),
# 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
# production)
'reserved_duration': reserved_duration,
}]
return workorders_values_str
def _json_workpiece_delivery_list(self):
# 修改在装夹工单完成后,生成上产线的工件配送单
# up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc')
# down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc')
return [
[0, '',
{
'production_id': self.production_id.id,
'production_line_id': self.production_id.production_line_id.id,
'type': '上产线',
'is_cnc_program_down': True,
'rfid_code': self.rfid_code
# 'route_id': up_route.id,
# 'feeder_station_start_id': agv_start_site_id,
# 'feeder_station_destination_id': up_route.end_site_id.id
}
],
# [0, '',
# {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线',
# 'route_id': down_route.id,
# 'feeder_station_start_id': down_route.start_site_id.id,
# 'feeder_station_destination_id': down_route.end_site_id.id}]
]
# 拼接工单对象属性值(表面工艺)
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id,
'qty_producing': 0,
'operation_id': False,
'name': '%s-%s' % (route.name, process_parameter.name),
'processing_panel': '',
'routing_type': '表面工艺',
'surface_technics_parameters_id': process_parameter.id,
'work_state': '',
'supplier_id': supplier_id,
'is_subcontract': True if process_parameter.gain_way == '外协' else False,
'workcenter_id': self.env[
'mrp.workcenter'].get_process_outsourcing_workcenter() if process_parameter.gain_way == '外协' else
self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': 60,
'duration': 0
}]
return workorders_values_str
# 维修模块按钮
def button_maintenance_req(self):
self.ensure_one()
return {
'name': _('New Maintenance Request'),
'view_mode': 'form',
'views': [(self.env.ref('mrp_maintenance.maintenance_request_view_form_inherit_mrp').id, 'form')],
'res_model': 'maintenance.request',
'type': 'ir.actions.act_window',
'context': {
'default_company_id': self.company_id.id,
'default_workorder_id': self.id,
'default_production_id': self.production_id.id,
'discard_on_footer_button': True,
},
'target': 'new',
'domain': [('workorder_id', '=', self.id)]
}
# tray_id = fields.Many2one('sf.tray', string="托盘信息", tracking=True)
# 扫码绑定托盘方法
# def gettray(self):
# if self.tray_code != False:
# values = self.env['sf.tray'].search([("code", "=", self.tray_code)])
# if values:
# if values.state == "占用":
# raise UserError('该托盘已占用')
# if values.state == "报损":
# raise UserError('该托盘已损坏')
# else:
# values.update({
# 'workorder_id': self,
# 'production_id': self.production_id,
# 'state': '占用',
# })
# self.work_state = "已绑定"
# orders = self.env['mrp.workorder'].search([('production_id', '=', self.production_id.id)])
# for a in orders:
# a.tray_id = values
# else:
# raise UserError('该托盘编码已失效')
# else:
# raise UserError('托盘码不能为空')
# 验证坯料序列号是否正确
def pro_code_is_ok(self, barcode):
if barcode is not False:
if barcode != self.pro_code:
raise UserError('坯料序列号错误')
return False
else:
return True
pro_code = fields.Char(string='坯料序列号')
pro_code_ok = fields.Boolean(default=False)
# 托盘扫码绑定
# def gettray_auto(self, barcode):
# if barcode != False:
# values = self.env['sf.tray'].search([("code", "=", barcode)])
#
# if values:
# if values.state == "占用":
# raise UserError('该托盘已占用')
# if values.state == "报损":
# raise UserError('该托盘已损坏')
# else:
# values.update({
# 'workorder_id': self,
# 'production_id': self.production_id,
# 'state': '占用',
# })
# self.work_state = "已绑定"
# orders = self.env['mrp.workorder'].search([('production_id', '=', self.production_id.id)])
# for a in orders:
# a.tray_id = values
#
# return values
#
# # return {
# # 'name': _('New Maintenance Request'),
# # 'view_mode': 'form',
# # 'res_model': 'maintenance.request',
# # 'type': 'ir.actions.act_window',
# # 'context': {
# # 'default_company_id': self.company_id.id,
# # 'default_production_id': self.id,
# # },
# # 'domain': [('production_id', '=', self.id)],
# # }
# else:
# raise UserError('该托盘编码已失效')
# else:
# raise UserError('托盘码不能为空')
# 解除托盘绑定
# def unbindtray(self):
# tray = self.env['sf.tray'].search([("production_id", "=", self.production_id.id)])
# if tray:
# tray.unclamp()
# self.tray_id = False
# return {
# 'name': _('New Maintenance Request'),
# 'view_mode': 'form',
# 'res_model': 'maintenance.request',
# 'res_id':self.id,
# 'type': 'ir.actions.act_window',
# 'context': {
# 'default_company_id': self.company_id.id,
# 'default_production_id': self.id,
# },
# 'domain': [('production_id', '=', self.id)],
# 'target':'new'
# }
def json_workorder_str1(self, k, production, route):
workorders_values_str = [0, '', {
'product_uom_id': production.product_uom_id.id,
'qty_producing': 0,
'operation_id': False,
'name': '%s(返工)' % route.route_workcenter_id.name,
'processing_panel': k,
'routing_type': route.routing_type,
'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids,
route.routing_type,
production.product_id),
'date_planned_start': datetime.now(),
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': 60,
'duration': 0,
'manual_quotation': production.workorder_ids.filtered(
lambda t: t.routing_type == 'CNC加工').manual_quotation,
'rfid_code': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').rfid_code,
'cnc_ids': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').cnc_ids,
'cmm_ids': production.workorder_ids.filtered(lambda t: t.routing_type == 'CNC加工').cmm_ids,
}]
return workorders_values_str
@api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state',
'production_id.tool_state')
def _compute_state(self):
super()._compute_state()
for workorder in self:
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'])])
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', '=', '返工')])
cnc_workorder_pending = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel),
('routing_type', '=', 'CNC加工'), ('state', 'in', ['pending'])])
unclamp_workorder = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('sequence', '=', workorder.sequence - 1),
('state', 'in', ['done'])])
if workorder.state not in ['cancel', 'progress', 'rework']:
if workorder.production_id.state == 'rework':
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 or cnc_workorder:
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:
if not cnc_workorder_pending or unclamp_workorder.test_results == '报废':
workorder.state = 'waiting'
# else:
# if workorder.production_id.is_rework is True:
# workorder.state = 'waiting'
elif workorder.production_id.state == 'progress':
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 workorder.production_id.is_rework is False:
if re_work or cnc_workorder or unclamp_workorder:
workorder.state = 'ready'
# if (re_work or cnc_workorder) and workorder.production_id.is_rework is False:
# workorder.state = 'ready'
if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']:
if unclamp_workorder:
if workorder.is_subcontract is False:
workorder.state = 'ready'
else:
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(
lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search(
[('origin', 'ilike', ','.join(production_list))])
for line in purchase_orders.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id and line.product_qty == len(
production_no_remanufacture):
if purchase_orders.state == 'purchase':
workorder.state = 'ready'
else:
workorder.state = 'waiting'
elif workorder.production_id.state == 'scrap':
if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
workorder.state = 'waiting'
if workorder.routing_type == '装夹预调' and workorder.state in ['waiting', 'ready', 'pending']:
workorder_ids = workorder.production_id.workorder_ids
work_bo = True
for wo in workorder_ids.filtered(lambda a: a.routing_type == '装夹预调' and a.state == 'rework'):
if not workorder_ids.filtered(
lambda a: (a.routing_type == '装夹预调' and a.state not in ['rework', 'cancel']
and a.processing_panel == wo.processing_panel)):
work_bo = False
break
if (workorder.production_id.programming_state == '已编程' and work_bo
and not workorder_ids.filtered(lambda a: a.sequence == 0)):
# 当工单对应制造订单的功能刀具状态为 【无效刀】时,先对的第一个装夹预调工单状态设置为 【等待组件】
if workorder.production_id.tool_state in ['1', '2']:
if workorder.state in ['ready']:
workorder.state = 'waiting'
continue
elif workorder.state in ['waiting']:
continue
elif workorder.state == 'pending' and workorder == self.search(
[('production_id', '=', workorder.production_id.id),
('routing_type', '=', '装夹预调'),
('state', 'not in', ['rework', 'done', 'cancel'])],
limit=1,
order="sequence"):
workorder.state = 'waiting'
continue
elif workorder.production_id.tool_state in ['0']:
if workorder_ids.filtered(lambda a: a.state == 'rework'):
if not workorder_ids.filtered(
lambda a: (a.routing_type not in ['装夹预调'] and
a.state not in ['pending', 'done', 'rework', 'cancel'])):
# 查询工序最小的非完工、非返工的装夹预调工单
work_id = self.search(
[('production_id', '=', workorder.production_id.id),
('routing_type', '=', '装夹预调'),
('state', 'not in', ['rework', 'done', 'cancel'])],
limit=1,
order="sequence")
if workorder == work_id:
if workorder.production_id.reservation_state == 'assigned':
workorder.state = 'ready'
elif workorder.production_id.reservation_state != 'assigned':
workorder.state = 'waiting'
continue
if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
workorder.state = 'waiting'
continue
# 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):
if self.routing_type == '装夹预调':
# 判断是否有坯料的序列号信息
boolean = False
if self.production_id.move_raw_ids[0].move_line_ids:
if self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name:
boolean = True
if not boolean:
raise UserError('制造订单【%s】缺少组件的序列号信息!' % self.production_id.name)
self.pro_code = self.production_id.move_raw_ids[0].move_line_ids[0].lot_id.name
# cnc校验
cnc_workorder = self.search(
[('production_id', '=', self.production_id.id), ('routing_type', '=', 'CNC加工')],
limit=1, order='id asc')
if not cnc_workorder.cnc_ids:
raise UserError(_('该制造订单还未下发CNC程序请稍后再试'))
else:
if self.production_id.tool_state in ['1', '2']:
if self.production_id.tool_state == '1':
state = '缺刀'
else:
state = '无效刀'
raise UserError(
f'制造订单【{self.production_id.name}】功能刀具状态为【{state}】!')
if self.routing_type == '解除装夹':
'''
记录开始时间
'''
self.date_start = datetime.now()
# 表面工艺外协出库单
if self.routing_type == '表面工艺':
if self.is_subcontract is True:
move_out = self.env['stock.move'].search(
[('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.production_id.name)])
if move_out.state != 'done':
move_out.write({'state': 'assigned', 'production_id': False})
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
# move_out._action_assign()
if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress':
self.move_raw_ids = self.production_id.move_raw_ids
self.move_raw_ids[0].write({
'materiel_length': self.move_raw_ids[0].product_id.length,
'materiel_width': self.move_raw_ids[0].product_id.width,
'materiel_height': self.move_raw_ids[0].product_id.height
})
self.write({
'material_length': self.move_raw_ids[0].product_id.length,
'material_width': self.move_raw_ids[0].product_id.width,
'material_height': self.move_raw_ids[0].product_id.height
})
self.ensure_one()
if any(not time.date_end for time in self.time_ids.filtered(lambda t: t.user_id.id == self.env.user.id)):
return True
# As button_start is automatically called in the new view
if self.state in ('done', 'cancel'):
return True
if self.product_tracking == 'serial':
self.qty_producing = 1.0
else:
self.qty_producing = self.qty_remaining
self.env['mrp.workcenter.productivity'].create(
self._prepare_timeline_vals(self.duration, datetime.now())
)
if self.production_id.state != 'progress':
self.production_id.write({
'date_start': datetime.now(),
})
if self.state == 'progress':
return True
start_date = datetime.now()
vals = {
'state': 'progress',
'date_start': start_date,
}
if not self.leave_id:
leave = self.env['resource.calendar.leaves'].create({
'name': self.display_name,
'calendar_id': self.workcenter_id.resource_calendar_id.id,
'date_from': start_date,
'date_to': start_date + relativedelta(minutes=self.duration_expected),
'resource_id': self.workcenter_id.resource_id.id,
'time_type': 'other'
})
vals['leave_id'] = leave.id
return self.write(vals)
else:
if self.date_planned_start > start_date:
vals['date_planned_start'] = start_date
# if self.date_planned_finished and self.date_planned_finished < start_date:
# vals['date_planned_finished'] = start_date
return self.write(vals)
else:
raise UserError(_('请先完成上一步工单'))
def button_finish(self):
for record in self:
if record.routing_type == '装夹预调':
if not record.rfid_code and record.is_rework is False:
raise UserError("请扫RFID码进行绑定")
if record.is_rework is False:
if not record.material_center_point or record.X_deviation_angle <= 0:
raise UserError("坯料中心点为空或X偏差角度小于等于0")
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工'
# 生成工件配送单
record.workpiece_delivery_ids = record._json_workpiece_delivery_list()
if record.routing_type == 'CNC加工':
record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待解除装夹'
self.env['sf.production.plan'].sudo().search([('name', '=', record.production_id.name)]).write({
'state': 'finished',
'actual_end_time': datetime.now()
})
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 in ['返工',
'报废'] 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 == '解除装夹':
'''
记录结束时间
'''
record.date_finished = datetime.now()
if record.routing_type == '表面工艺':
logging.info('record.picking_ids:%s' % record.picking_ids)
logging.info('record.picking_out:%s' % record.picking_ids[0])
if record.picking_ids:
for pick_item in record.picking_ids:
if pick_item.state not in ['done']:
raise UserError(
'请先完成该工单的工艺外协再进行操作')
picking_out = record.env['stock.move.line'].search(
[('picking_id', '=', record.picking_ids[0].id)])
logging.info('picking_out:%s' % picking_out.picking_id.name)
# if picking_out:
# order_line_ids = []
# logging.info('surface_technics_parameters_id:%s' % record.surface_technics_parameters_id.name)
#
# else:
# raise UserError(
# '请先在产品中配置表面工艺为%s相关的外协服务产品' % item.surface_technics_parameters_id.name)
tem_date_planned_finished = record.date_planned_finished
tem_date_finished = record.date_finished
logging.info('routing_type:%s' % record.routing_type)
super().button_finish()
logging.info('date_planned_finished:%s' % record.date_planned_finished)
# 表面工艺工单完成不走该修改
if record.routing_type != '表面工艺':
record.write({
'date_planned_finished': tem_date_planned_finished # 保持原值
})
# if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']:
# record.production_id.action_cancel()
# 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 = 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})
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:
move_raw_id.quantity_done = move_raw_id.product_uom_qty
record.process_state = '已完工'
record.production_id.process_state = '已完工'
if record.routing_type in ['表面工艺']:
raw_move = self.env['stock.move'].sudo().search(
[('origin', '=', record.production_id.name),
('procure_method', 'in', ['make_to_order', 'make_to_stock']),
('state', '!=', 'done')])
if raw_move:
raw_move.write({'state': 'done'})
record.production_id.button_mark_done1()
# record.production_id.state = 'done'
# 解绑托盘
def unbind_tray(self):
self.production_id.workorder_ids.write({
'rfid_code': False,
'tray_serial_number': False,
'tray_product_id': False,
'tray_brand_id': False,
'tray_type_id': False,
'tray_model_id': False,
'is_trayed': False})
# 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath):
logging.info('reportpath/ftp地址:%s' % reportpath)
logging.info('processing_panel:%s' % workorder.processing_panel)
serverdir = os.path.join('/tmp', workorder.production_id.name.replace('/', '_'), 'detection',
workorder.processing_panel)
ftp_resconfig = self.env['res.config.settings'].get_values()
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']),
ftp_resconfig['ftp_user'],
ftp_resconfig['ftp_password'])
if not ftp.file_exists_1(reportpath):
logging.info('文件不存在:%s' % reportpath)
download_state = ftp.download_program_file(reportpath, serverdir)
logging.info('download_state:%s' % download_state)
return download_state
# 根据中控系统提供的检测文件地址去ftp里对应的制造订单里获取
def get_detection_file(self, workorder, reportPath):
serverdir = os.path.join('/tmp', workorder.production_id.name.replace('/', '_'), 'detection',
workorder.processing_panel)
logging.info('get_detection_file-serverdir:%s' % serverdir)
for root, dirs, files in os.walk(serverdir):
for filename in files:
if filename == os.path.basename(reportPath):
report_file_path = os.path.join(root, filename)
logging.info('get_detection_file-report_file_path:%s' % report_file_path)
workorder.detection_report = base64.b64encode(open(report_file_path, 'rb').read())
return True
def print_method(self):
"""
解除装夹处调用关联制造订单的关联序列号的打印方法
"""
if self.production_id:
if self.production_id.lot_producing_id:
self.production_id.lot_producing_id.print_single_method()
else:
raise UserError("无关联制造订单或关联序列号,无法打印。请检查!")
@api.model
def get_views(self, views, options=None):
res = super().get_views(views, options)
if res['views'].get('list', {}) and self.env.context.get('search_default_workcenter_id'):
workcenter = self.env['mrp.workcenter'].browse(self.env.context.get('search_default_workcenter_id'))
tree_view = res['views']['list']
if workcenter.name == '工件拆卸中心':
arch = etree.fromstring(tree_view['arch'])
# 查找 tree 标签
tree_element = arch.xpath("//tree")[0]
# 查找或创建 header 标签
header_element = tree_element.find('header')
if header_element is None:
header_element = etree.Element('header')
tree_element.insert(0, header_element)
# 创建并添加按钮元素
button_element = etree.Element('button', {
'name': 'button_delivery',
'type': 'object',
'string': '解除装夹',
'class': 'btn-primary jikimo_button_confirm',
# 'className': 'btn-primary',
'modifiers': '{"force_show": 1}'
})
header_element.append(button_element)
# 更新 tree_view 的 arch
tree_view['arch'] = etree.tostring(arch, encoding='unicode')
return res
def button_delivery(self):
# production_ids = []
# workorder_ids = []
delivery_type = '运送空料架'
# max_num = 4 # 最大配送数量
# feeder_station_start_id = False
# if len(self) > max_num:
# raise UserError('仅限于拆卸1-4个制造订单请重新选择')
# for item in self:
# if item.state != 'ready':
# raise UserError('请选择状态为【就绪】的工单进行解除装夹')
#
# production_ids.append(item.production_id.id)
# workorder_ids.append(item.id)
# if not feeder_station_start_id:
# down_product_agv_scheduling = item.get_down_product_agv_scheduling()
# if down_product_agv_scheduling:
# feeder_station_start_id = down_product_agv_scheduling.end_site_id.id
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
# 'default_delivery_ids': [(6, 0, delivery_ids)],
# 'default_production_ids': [(6, 0, production_ids)],
'default_delivery_type': delivery_type,
# 'default_workorder_ids': [(6, 0, workorder_ids)],
'default_workcenter_id': self.env.context.get('default_workcenter_id'),
'default_confirm_button': '确认解除',
# 'default_feeder_station_start_id': feeder_station_start_id,
}}
class CNCprocessing(models.Model):
_name = 'sf.cnc.processing'
_description = "CNC加工"
_rec_name = 'program_name'
_order = 'sequence_number,id'
cnc_id = fields.Many2one('ir.attachment')
sequence_number = fields.Integer('序号')
program_name = fields.Char('程序名')
functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型')
cutting_tool_name = fields.Char('刀具名称')
cutting_tool_no = fields.Char('刀号')
processing_type = fields.Char('加工类型')
margin_x_y = fields.Char('余量_X/Y')
margin_z = fields.Char('余量_Z')
depth_of_processing_z = fields.Char('加工深度(Z)')
cutting_tool_extension_length = fields.Char('刀具伸出长度')
cutting_tool_handle_type = fields.Char('刀柄型号')
estimated_processing_time = fields.Char('预计加工时间')
remark = fields.Text('备注')
workorder_id = fields.Many2one('mrp.workorder', string="工单")
production_id = fields.Many2one('mrp.production', string="制造订单")
button_state = fields.Boolean(string='是否已经下发')
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
for obj in ret['programming_list']:
workorder = self.env['mrp.workorder'].search(
[('production_id.name', '=', cnc_workorder.name),
('processing_panel', '=', obj['processing_panel']),
('routing_type', '=', 'CNC加工')])
logging.info('workorder:%s' % workorder.id)
if obj['program_name'] in program_path:
logging.info('obj:%s' % obj['program_name'])
cnc_processing = self.env['sf.cnc.processing'].create({
'workorder_id': workorder.id,
'sequence_number': obj['sequence_number'],
'program_name': obj['program_name'],
'cutting_tool_name': obj['cutting_tool_name'],
'cutting_tool_no': obj['cutting_tool_no'],
'processing_type': obj['processing_type'],
'margin_x_y': obj['margin_x_y'],
'margin_z': obj['margin_z'],
'depth_of_processing_z': obj['depth_of_processing_z'],
'cutting_tool_extension_length': obj['cutting_tool_extension_length'],
'cutting_tool_handle_type': obj['cutting_tool_handle_type'],
'estimated_processing_time': obj['estimated_processing_time'],
'remark': obj['remark'],
'program_path': program_path.replace('/tmp', '/home/ftp/ftp_root/NC')
})
cnc_processing.get_cnc_processing_file(program_path_tmp, cnc_processing, program_path)
cnc_workorder.write({'programming_state': '已编程', 'work_state': '已编程'})
return cnc_processing
def _json_cnc_processing(self, panel, ret):
cnc_processing = []
if ret is not False:
for item in ret['programming_list']:
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1:
cnc_processing.append((0, 0, {
'sequence_number': item['sequence_number'],
'program_name': item['program_name'],
'cutting_tool_name': item['cutting_tool_name'],
'cutting_tool_no': item['cutting_tool_no'],
'processing_type': item['processing_type'],
'margin_x_y': item['margin_x_y'],
'margin_z': item['margin_z'],
'depth_of_processing_z': item['depth_of_processing_z'],
'cutting_tool_extension_length': item['cutting_tool_extension_length'],
'cutting_tool_handle_type': item['cutting_tool_handle_type'],
'estimated_processing_time': item['estimated_processing_time'],
'program_path': item['ftp_path'],
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
'remark': item['remark']
}))
return cnc_processing
# 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配
def get_cnc_processing_file(self, serverdir, cnc_processing, program_path):
logging.info('serverdir:%s' % serverdir)
logging.info('cnc_processing:%s' % cnc_processing)
for root, dirs, files in os.walk(serverdir):
for f in files:
logging.info('splitext(f):%s' % os.path.splitext(f)[1])
if os.path.splitext(f)[1] == ".pdf":
full_path = os.path.join(serverdir, root, f)
cnc_processing.workorder_id.cnc_worksheet = base64.b64encode(
open(full_path, 'rb').read())
else:
if f in program_path:
# if cnc_processing.program_name == f.split('.')[0]:
cnc_file_path = os.path.join(serverdir, root, f)
self.write_file(cnc_file_path, cnc_processing)
# 创建附件(nc文件)
def attachment_create(self, name, data):
attachment = self.env['ir.attachment'].create({
'datas': base64.b64encode(data),
'type': 'binary',
'public': True,
'description': '程序文件',
'name': name
})
return attachment
# 将FTP的多面的程序单文件下载到临时目录
def download_file_tmp(self, production_no, processing_panel):
remotepath = os.path.join('/home/ftp/ftp_root/NC', production_no, 'return', processing_panel)
serverdir = os.path.join('/tmp', production_no, 'return', processing_panel)
ftp_resconfig = self.env['res.config.settings'].get_values()
ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'],
ftp_resconfig['ftp_password'])
if not ftp.file_exists_1(remotepath):
logging.info('目录不存在:%s' % remotepath)
download_state = ftp.download_program_file(remotepath, serverdir)
logging.info('download_state:%s' % download_state)
return download_state
# 将nc文件存到attach的datas里
def write_file(self, nc_file_path, cnc):
nc_file_name = nc_file_path.split('/')
logging.info('file_name:%s' % nc_file_name[-1])
if os.path.exists(nc_file_path):
with open(nc_file_path, 'rb') as file:
data_bytes = file.read()
attachment = self.attachment_create(cnc.program_name + nc_file_name[-1], data_bytes)
cnc.write({'cnc_id': attachment.id})
file.close()
else:
return False
# end_date = datetime.now()
# for workorder in self:
# if workorder.state in ('done', 'cancel'):
# continue
# workorder.end_all()
# vals = {
# 'qty_produced': workorder.qty_produced or workorder.qty_producing or workorder.qty_production,
# 'state': 'done',
# 'date_finished': end_date,
# 'date_planned_finished': end_date,
# 'costs_hour': workorder.workcenter_id.costs_hour
# }
# if not workorder.date_start:
# vals['date_start'] = end_date
# if not workorder.date_planned_start or end_date < workorder.date_planned_start:
# vals['date_planned_start'] = end_date
# workorder.with_context(bypass_duration_calculation=True).write(vals)
return True
class SfWorkOrderBarcodes(models.Model):
"""
智能工厂工单处扫码绑定托盘
"""
_name = "mrp.workorder"
_inherit = ["mrp.workorder", "barcodes.barcode_events_mixin"]
def on_barcode_scanned(self, barcode):
logging.info('Rfid:%s' % barcode)
workorder = self.env['mrp.workorder'].browse(self.ids)
# workorder_preset = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
workorder_olds = self.env['mrp.workorder'].search(
[('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])
if workorder_olds:
name = ''
for workorder in workorder_olds:
name = '%s %s' % (name, workorder.production_id.name)
raise UserError('该托盘已绑定【%s】制造订单,请先解除绑定!!!' % name)
if workorder:
if workorder.routing_type == '装夹预调':
if workorder.state in ['done']:
work_state = {'done': '已完工'}
raise UserError('装夹%s,请勿重复扫码' % work_state.get(workorder.state))
lots = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
logging.info("托盘信息:%s" % lots)
if lots:
for lot in lots:
if lot.product_id.categ_type == '夹具':
val = {
'tray_serial_number': lot.name,
'tray_product_id': lot.product_id.id,
'tray_brand_id': lot.product_id.brand_id.id,
'tray_type_id': lot.product_id.fixture_material_id.id,
'tray_model_id': lot.product_id.fixture_model_id.id,
'rfid_code': barcode
}
workorder.write(val)
self.write(val)
workorder_rfid = self.env['mrp.workorder'].search(
[('production_id', '=', workorder.production_id.id),
('processing_panel', '=', workorder.processing_panel)])
if workorder_rfid:
for item in workorder_rfid:
item.write({'rfid_code': barcode})
logging.info("Rfid[%s]绑定成功!!!" % barcode)
else:
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
self.process_state = '待检测'
self.date_start = datetime.now()
self.is_trayed = True
else:
raise UserError('没有找到Rfid为【%s】的托盘信息!!!' % barcode)
# stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)])
# if stock_move_line.product_id.categ_type == '夹具':
# workorder.write({
# 'tray_serial_number': stock_move_line.lot_name,
# 'tray_product_id': stock_move_line.product_id.id,
# 'tray_brand_id': stock_move_line.product_id.brand_id.id,
# 'tray_type_id': stock_move_line.product_id.fixture_material_id.id,
# 'tray_model_id': stock_move_line.product_id.fixture_model_id.id
# })
# workorder.button_start()
# # return {
# # 'type': 'ir.actions.act_window',
# # 'res_model': 'mrp.workorder',
# # 'view_mode': 'form',
# # 'domain': [('id', 'in', workorder.id)],
# # 'target': 'current'
# # }
# else:
# embryo_stock_lot = self.env['stock.lot'].search([('name', '=', barcode)])
# if embryo_stock_lot:
# embryo_stock_move_line = self.env['stock.move.line'].search(
# [('product_id', '=', embryo_stock_lot.product_id.id),
# ('reference', '=', workorder.production_id.name),
# ('lot_id', '=', embryo_stock_lot.id),
# ('product_category_name', '=', '坯料')])
# if embryo_stock_move_line:
# bom_production = self.env['mrp.production'].search(
# [('product_id', '=', embryo_stock_lot.product_id.id),
# ('origin', '=', workorder.production_id.name)], limit=1, order='id asc')
# workpiece_delivery = self.env['sf.workpiece.delivery'].search(
# [('workorder_id', '=', workorder.id)], limit=1, order='id asc')
# if workpiece_delivery:
# embryo_workpiece_code = workpiece_delivery.workpiece_code
# if bom_production:
# if workpiece_delivery.workpiece_code and bom_production.name not in \
# workpiece_delivery.workpiece_code:
# embryo_workpiece_code = workpiece_delivery.workpiece_code + ',' + \
# bom_production.name
# if not workpiece_delivery.workpiece_code:
# embryo_workpiece_code = bom_production.name
# workpiece_delivery.write({'workpiece_code': embryo_workpiece_code})
# else:
# raise UserError('工件生产线不一致,请重新确认')
# else:
# workorder_rfid = self.env['mrp.workorder'].search(
# [('production_id', '=', workorder.production_id.id)])
# if workorder_rfid:
# for item in workorder_rfid:
# item.write({'rfid_code': barcode})
class WorkPieceDelivery(models.Model):
_name = "sf.workpiece.delivery"
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = '工件配送'
name = fields.Char('单据编码')
workorder_id = fields.Many2one('mrp.workorder', string='工单', readonly=True)
workorder_state = fields.Selection(related='workorder_id.state', string='工单状态')
rfid_code = fields.Char(related='workorder_id.rfid_code', string='rfid码', store=True)
production_id = fields.Many2one('mrp.production', string='制造订单号', readonly=True)
production_line_id = fields.Many2one('sf.production.line', string='目的生产线', tracking=True)
plan_start_processing_time = fields.Datetime('计划开始加工时间', readonly=True)
route_id = fields.Many2one('sf.agv.task.route', '任务路线', tracking=True)
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
task_delivery_time = fields.Datetime('任务下发时间')
task_completion_time = fields.Datetime('任务完成时间')
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection(
[('待下发', '待下发'), ('已下发', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
default='待下发', tracking=True)
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
is_manual_work = fields.Boolean('人工操作', default=False)
active = fields.Boolean(string="有效", default=True)
agv_scheduling_id = fields.Many2one('sf.agv.scheduling', 'AGV任务调度')
@api.model
def create(self, vals):
if vals.get('route_id') and vals.get('type') is None:
vals['type'] = '运送空料架'
else:
if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.workpiece.delivery') or '/'
obj = super(WorkPieceDelivery, self).create(vals)
if obj.type == '运送空料架':
if obj.name is False:
obj.name = "运送空料架路线:%s-%s" % (
obj.feeder_station_start_id.name, obj.feeder_station_destination_id.name)
return obj
# @api.constrains('route_id')
# def _check_route_id(self):
# if self.type == '运送空料架':
# if self.route_id and self.name is False:
# route = self.sudo().search(
# [('route_id', '=', self.route_id.id), ('id', '!=', self.id), ('name', 'ilike', '运送空料架路线')])
# if route:
# raise UserError("该任务路线已存在,请重新选择")
# @api.constrains('name')
# def _check_name(self):
# if self.type == '运送空料架':
# wd = self.sudo().search([('name', '=', self.name), ('id', '!=', self.id)])
# if wd:
# raise UserError("该名称已存在")
def action_delivery_history(self):
return {
'name': _('配送历史'),
'type': 'ir.actions.act_window',
'view_mode': 'tree',
'res_model': 'sf.workpiece.delivery',
'view_id': self.env.ref('sf_manufacturing.sf_workpiece_delivery_empty_racks_tree').id,
'domain': [('type', '=', '运送空料架'), ('route_id', '=', self.route_id.id), ('name', 'ilike', 'WDO')]
}
@api.onchange('route_id')
def onchange_route(self):
if self.route_id:
self.feeder_station_start_id = self.route_id.start_site_id.id
self.feeder_station_destination_id = self.route_id.end_site_id.id
# 工件配送
def button_delivery(self):
delivery_ids = []
production_ids = []
workorder_ids = []
is_cnc_down = 0
is_not_production_line = 0
same_production_line_id = None
delivery_type = '上产线'
max_num = 4 # 最大配送数量
if len(self) > max_num:
raise UserError('仅限于配送1-4个制造订单请重新选择')
for item in self:
if item.status != '待下发':
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if same_production_line_id is None:
same_production_line_id = item.production_line_id.id
if item.production_line_id.id != same_production_line_id:
is_not_production_line += 1
if item.is_cnc_program_down is False:
is_cnc_down += 1
if is_cnc_down == 0 and is_not_production_line == 0:
delivery_ids.append(item.id)
production_ids.append(item.production_id.id)
workorder_ids.append(item.workorder_id.id)
if is_cnc_down >= 1:
raise UserError('您所选择制造订单的【CNC程序】暂未下发请在程序下发后再进行配送')
if is_not_production_line >= 1:
raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认')
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
'default_delivery_ids': [(6, 0, delivery_ids)],
'default_production_ids': [(6, 0, production_ids)],
'default_delivery_type': delivery_type,
'default_workorder_ids': [(6, 0, workorder_ids)],
'default_confirm_button': '确认配送'
}}
# 验证agv站点是否可用
def _check_avgsite_state(self):
is_free = False
agv_site = self.env['sf.agv.site'].search([])
if agv_site:
has_site = agv_site.update_site_state()
if has_site is True:
for item in self:
if item.type in ['上产线', '下产线']:
logging.info('工件配送-起点状态:%s-%s' % (
item.feeder_station_start_id.name, item.feeder_station_start_id.state))
logging.info('工件配送-终点状态:%s-%s' % (
item.feeder_station_destination_id.name, item.feeder_station_destination_id.state))
if (
item.feeder_station_start_id.state == '占用' and item.feeder_station_destination_id.state == '空闲') or (
item.feeder_station_start_id.state == '空闲' and item.feeder_station_destination_id.state == '空闲'):
is_free = True
else:
if item.feeder_station_destination_id.state == '空闲':
is_free = True
logging.info('is_free:%s' % is_free)
return is_free
else:
raise UserError("接驳站暂未反馈站点实时状态,请稍后再试")
# 配送至avg小车
def _delivery_avg(self):
config = self.env['res.config.settings'].get_values()
positionCode_Arr = []
delivery_Arr = []
feeder_station_start = None
feeder_station_destination = None
route_id = None
for item in self:
if route_id is None:
route_id = item.route_id.id
if feeder_station_start is None:
feeder_station_start = item.feeder_station_start_id
if feeder_station_destination is None:
feeder_station_destination = item.feeder_station_destination_id
if item.type in ['上产线', '下产线']:
item.route_id = route_id
item.feeder_station_start_id = feeder_station_start.id
item.feeder_station_destination_id = feeder_station_destination.id
delivery_Arr.append(item.name)
else:
self = self.create(
{'name': self.env['ir.sequence'].next_by_code('sf.workpiece.delivery'),
'route_id': self.route_id.id,
'feeder_station_start_id': self.feeder_station_start_id.id,
'feeder_station_destination_id': self.feeder_station_destination_id.id})
delivery_Arr.append(self.name)
delivery_str = ','.join(map(str, delivery_Arr))
if feeder_station_start is not None:
positionCode_Arr.append({
'positionCode': feeder_station_start.name,
'code': '00'
})
if feeder_station_destination is not None:
positionCode_Arr.append({
'positionCode': feeder_station_destination.name,
'code': '00'
})
res = {'reqCode': delivery_str, 'reqTime': '', 'clientCode': '', 'tokenCode': '',
'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'],
'positionCodePath': positionCode_Arr,
'podCode': '',
'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '',
'data': ''}
try:
logging.info('AGV请求路径:%s' % config['agv_rcs_url'])
logging.info('AGV-json:%s' % res)
headers = {'Content-Type': 'application/json'}
ret = requests.post((config['agv_rcs_url']), json=res, headers=headers)
ret = ret.json()
logging.info('config-ret:%s' % ret)
if ret['code'] == 0:
req_codes = ret['reqCode'].split(',')
for delivery_item in self:
for req_code in req_codes:
if delivery_item.name == req_code.strip():
logging.info('delivery_item-name:%s' % delivery_item.name)
delivery_item.write({
'task_delivery_time': fields.Datetime.now(),
'status': '待配送'
})
if delivery_item.type == "上产线":
delivery_item.workorder_id.write({'is_delivery': True})
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('config-e:%s' % e)
raise UserError("工件配送请求agv失败:%s" % e)
@api.depends('task_delivery_time', 'task_completion_time')
def _compute_delivery_duration(self):
for obj in self:
if obj.task_delivery_time and obj.task_completion_time:
obj.delivery_duration = round(
(obj.task_completion_time - obj.task_delivery_time).total_seconds() / 60.0, 2)
else:
obj.delivery_duration = 0.0
class CMMprogram(models.Model):
_name = 'sf.cmm.program'
_description = "CMM程序"
sequence_number = fields.Integer('序号')
program_name = fields.Char('程序名')
remark = fields.Text('备注')
workorder_id = fields.Many2one('mrp.workorder', string="工单")
production_id = fields.Many2one('mrp.production', string="制造订单")
program_path = fields.Char('程序文件路径')
program_create_date = fields.Datetime('程序创建日期')
def _json_cmm_program(self, panel, ret):
cmm_program = []
if ret is not False:
for item in ret['programming_list']:
if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1:
cmm_program.append((0, 0, {
'sequence_number': 1,
'program_name': item['program_name'],
'program_path': item['ftp_path'],
'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'),
}))
return cmm_program