Files
test/sf_sale/models/quick_easy_order.py
2024-08-29 14:21:18 +08:00

467 lines
25 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 logging
import base64
import hashlib
import os
import platform
import json
from datetime import datetime
import requests
from odoo import http
from odoo.http import request
from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file
from odoo import models, fields, api
from odoo.modules import get_resource_path
from odoo.exceptions import ValidationError, UserError
from odoo.addons.sf_base.commons.common import Common
from . import parser_and_calculate_work_time as pc
class QuickEasyOrder(models.Model):
_name = 'quick.easy.order'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = '简易下单'
_order = 'id desc'
name = fields.Char('订单编号', default=lambda self: self.env['ir.sequence'].next_by_code('quick.easy.order'))
model_length = fields.Float('长(mm)', digits=(16, 3))
model_width = fields.Float('宽(mm)', digits=(16, 3))
model_height = fields.Float('高(mm)', digits=(16, 3))
model_volume = fields.Float('体积(mm³)', digits=(16, 3))
model_processing_side = fields.Char('加工面', default='A')
model_feature = fields.Char('特征')
machining_precision = fields.Selection([
('0.10', '±0.10mm'),
('0.05', '±0.05mm'),
('0.03', '±0.03mm'),
('0.02', '±0.02mm'),
('0.01', '±0.01mm')], string='加工精度', default='0.10')
material_id = fields.Many2one('sf.production.materials', '材料', tracking=True)
material_model_id = fields.Many2one('sf.materials.model', '型号', tracking=True)
# process_id = fields.Many2one('sf.production.process', string='表面工艺')
parameter_ids = fields.Many2many('sf.production.process.parameter', 'process_item_order_rel', string='可选参数',
tracking=True)
quantity = fields.Integer('数量', default=1, tracking=True)
unit_price = fields.Float('单价', tracking=True)
price = fields.Float('总价', tracking=True)
model_file = fields.Binary('glb模型文件')
upload_model_file = fields.Many2many('ir.attachment', 'upload_qf_model_file_attachment_ref', string='上传模型文件')
delivery_time = fields.Date('交货日期')
customer_id = fields.Many2one('res.partner', string='客户', default=lambda self: self.env.user.partner_id.id)
state = fields.Selection([('草稿', '草稿'), ('待派单', '待派单'),
('待接单', '待接单'), ('加工中', '加工中'),
('物流中', '物流中'), ('已交付', '已交付')], string='订单状态', default='草稿',
readonly=True, tracking=True)
model_color_state = fields.Selection([
('success', '成功'),
('fail', '失败')], string='模型上色状态', tracking=True)
processing_time = fields.Integer('加工时长(min)')
sale_order_id = fields.Many2one('sale.order', '销售订单号')
@api.depends('unit_price', 'quantity')
def _compute_total_amount(self):
for item in self:
item.price = item.unit_price * item.quantity
@api.depends('material_id', 'material_model_id')
def _compute_material_model(self):
for item in self:
materials = self.env['sf.production.materials'].search([], limit=1, order='id desc')
item.material_id = materials.id
item.material_model_id = self.env['sf.materials.model'].search(
[('materials_id', '=', materials.id)],
limit=1, order='id desc')
@api.model
def create(self, vals):
if vals.get('upload_model_file'):
logging.info('create-attachment:%s' % vals['upload_model_file'][0])
for item in vals['upload_model_file']:
print(len(item[2]))
if len(item[2]) > 0:
logging.info('create-attachment:%s' % int(item[2][0]))
attachment = self.env['ir.attachment'].sudo().search([('id', '=', int(item[2][0]))])
# base64_data = base64.b64encode(attachment.datas)
# base64_datas = base64_data.decode('utf-8')
# model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# report_path = attachment._full_path(attachment.store_fname)
vals['model_file'] = self.model_analyze(attachment)
# logging.info('create-model_file:%s' % len(vals['model_file']))
obj = super(QuickEasyOrder, self).create(vals)
# self.model_coloring(obj)
logging.info('---------开始派单到工厂-------')
sale_order = self.distribute_to_factory(obj)
obj.sale_order_id = sale_order.id
obj.state = '待接单'
return obj
def model_analyze(self, model_attachment):
"""
step模型解析上传模型时转为web可显示的格式
:return:
"""
config = request.env['res.config.settings'].sudo().get_values()
try:
# 获取当前操作系统
os_name = platform.system()
for item in model_attachment:
# 将拿到的3D模型数据存入文件
# 定义文件名和文件的二进制内容
file_name = item.name # 请将这里替换为你的文件名
print('file_name', file_name)
# base64_data = base64.b64encode(item.datas)
# base64_datas = base64_data.decode('utf-8')
binary_content = item.datas # 请将这里替换为你的文件的二进制内容
# binary_content从字符串转为二进制
binary_content = base64.b64decode(binary_content)
# 定义新的文件夹路径
# 根据操作系统不同,文件路径不同
path_header = '/model_parser' if os_name == 'Linux' else 'D:/model_analysis'
# new_folder_path = 'D:/11111final' + '/' + item['name'].split(".")[0]
new_folder_path = path_header + '/' + item.name.rpartition('.')[0]
print('new_folder_path', new_folder_path)
# 检查新的文件夹是否存在,如果不存在,则创建
if not os.path.exists(new_folder_path):
os.makedirs(new_folder_path)
# 定义新的文件路径
new_file_path = os.path.join(new_folder_path, file_name)
# 将二进制内容写入新的文件
with open(new_file_path, 'wb') as f:
f.write(binary_content)
# 检查文件是否已经成功写入
if os.path.exists(new_file_path):
print(f'Successfully wrote binary content to {new_file_path}')
else:
print(f'Failed to write binary content to {new_file_path}')
# 附件
# attachment = request.env['ir.attachment'].sudo().create({
# 'datas': item['data'].encode('utf-8'),
# 'type': 'binary',
# 'description': '模型文件',
# 'name': item['name'],
# 'public': True,
# 'model_name': item['name'],
# })
headers = {'Content-Type': 'application/json'}
# 调用写入宿主机接口
# url_dir = 'http://192.168.50.202:8000/create_and_write_file'
url_dir = config['model_parser_url'] + '/create_and_write_file'
data = {
'folder_path': new_folder_path, # 您想要创建的文件夹路径
'file_path': new_file_path, # 您想要创建的文件名
'content': item['data'] # 您想要写入文件的内容
}
requests.post(url_dir, json=data, headers=headers)
# 调用特征包接口
url = config['model_parser_url'] + '/process_file'
payload = {
'file_path': new_file_path,
'dest_path': new_folder_path,
'back_url': config['bfm_url_new']
}
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
print("Request was successful.")
print("Response: ", response.json())
else:
print("Request failed.")
# 特征识别
xml_path = new_folder_path + '/' + item.name.rpartition('.')[0] + '_FeatrueTable.XML'
print('xml_path', xml_path)
parser_obj = pc.FeatureParser(xml_path)
print('parser_obj', parser_obj)
slot = parser_obj.slots
print('slot', slot)
hole = parser_obj.holes
print('hole', hole)
size = parser_obj.size
print('size', size)
open_slot = parser_obj.open_slots
print('open_slot', open_slot)
vector = parser_obj.vectors
print('vector', vector)
print('all parcer', size)
try:
hole_time = pc.hole_time(parser_obj)
print('hole_time', hole_time)
except Exception as e:
return json.dumps({'code': 400, 'msg': '孔尺寸超限', 'error_msg': str(e)})
try:
slot_time = pc.slot_time(parser_obj)
print('slot_time', slot_time)
except Exception as e:
return json.dumps({'code': 400, 'msg': '槽尺寸超限', 'error_msg': str(e)})
try:
open_slot_time = pc.open_slot_time(parser_obj)
print('open_slot_time', open_slot_time)
except Exception as e:
return json.dumps({'code': 400, 'msg': '开口槽尺寸超限', 'error_msg': str(e)})
total_time = hole_time + slot_time + open_slot_time
print(hole_time, slot_time, open_slot_time)
print('total_time', total_time)
ret = {'feature_infos': [{'name': 'all_feature', 'type': '', 'process_time': total_time}],
'boxshape': size, 'slugX': 10.0, 'slugY': 90.0, 'slugZ': 42.0,
'turn_over_times': 2,
'target_faces': ['A', 'B']}
self.model_feature = json.dumps(ret['feature_infos'], ensure_ascii=False)
self.model_length = size['length'] # 长 单位mm
self.model_width = size['width'] # 宽
self.model_height = size['height'] # 高
self.model_volume = size['length'] * size['width'] * size['height']
# 附件处理
base64_data = base64.b64encode(item.datas)
base64_datas = base64_data.decode('utf-8')
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# 读取文件
shapes = read_step_file(new_file_path)
output_file = os.path.join(new_folder_path, str(model_code) + '.stl')
write_stl_file(shapes, output_file, 'binary', 0.03, 0.5)
# 转化为glb
output_glb_file = os.path.join(new_folder_path, str(model_code) + '.glb')
util_path = get_resource_path('jikimo_gateway_api', 'static/util')
# 根据操作系统确定使用 'python' 还是 'python3'
python_cmd = 'python3' if os_name == 'Linux' else 'python'
print('python_cmd', python_cmd)
print('os_name', os_name)
# 使用引号包围路径
cmd = '%s "%s/stl2gltf.py" "%s" "%s" -b' % (python_cmd, util_path, output_file, output_glb_file)
logging.info(cmd)
os.system(cmd)
# 转base64
with open(output_glb_file, 'rb') as fileObj:
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
return base64_data
except Exception as e:
return UserError('模型自动报价失败,请联系管理员')
# 将attach的datas内容转为glb文件
def transition_glb_file(self, report_path, model_code):
shapes = read_step_file(report_path)
output_file = os.path.join('/tmp', str(model_code) + '.stl')
write_stl_file(shapes, output_file, 'binary', 0.03, 0.5)
# 转化为glb
output_glb_file = os.path.join('/tmp', str(model_code) + '.glb')
util_path = get_resource_path('sf_base', 'static/util')
cmd = 'python3 %s/stl2gltf.py %s %s -b' % (util_path, output_file, output_glb_file)
os.system(cmd)
# 转base64
with open(output_glb_file, 'rb') as fileObj:
image_data = fileObj.read()
base64_data = base64.b64encode(image_data)
return base64_data
# return False
@api.onchange('upload_model_file')
def onchange_model_file(self):
for item in self:
if len(item.upload_model_file) > 1:
raise ValidationError('只允许上传一个文件')
if item.upload_model_file:
file_attachment_id = item.upload_model_file[0]
item.model_file = self.model_analyze(file_attachment_id)
self._get_price(item)
else:
item.model_file = False
item.model_feature = False
item.model_length = 0
item.model_width = 0
item.model_height = 0
item.model_volume = 0
def distribute_to_factory(self, obj):
"""
派单到工厂
:return:
"""
try:
logging.info('---------派单到工厂-------')
res = {'bfm_process_order_list': []}
for item in obj:
attachment = item.upload_model_file[0]
base64_data = base64.b64encode(attachment.datas)
base64_datas = base64_data.decode('utf-8')
barcode = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
# logging.info('model_file-size: %s' % len(item.model_file))
res['bfm_process_order_list'].append({
'model_long': item.model_length,
'model_width': item.model_width,
'model_height': item.model_height,
'model_volume': item.model_volume,
'model_machining_precision': item.machining_precision,
'model_name': attachment.name,
'model_data': base64_datas,
'model_file': base64.b64encode(item.model_file).decode('utf-8'),
'texture_code': item.material_id.materials_no,
'texture_type_code': item.material_model_id.materials_no,
# 'surface_process_code': self.env['jikimo.surface.process']._json_surface_process_code(item),
'process_parameters_code': self.env[
'sf.production.process.parameter']._json_production_process_item_code(
item),
'price': item.price,
'number': item.quantity,
'total_amount': item.price,
'remark': '',
'barcode': barcode
})
# res['bfm_process_order_list'] = json.dumps(res['bfm_process_order_list'])
product_id = self.env.ref('sf_dlm.product_template_sf').sudo()
self_machining_id = self.env.ref('sf_dlm.product_embryo_sf_self_machining').sudo()
outsource_id = self.env.ref('sf_dlm.product_embryo_sf_outsource').sudo()
purchase_id = self.env.ref('sf_dlm.product_embryo_sf_purchase').sudo()
company_id = self.env.ref('base.main_company').sudo()
# user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝')
i = 1
# 给sale_order的default_code字段赋值
aa = self.env['sale.order'].sudo().search([('name', '=', order_id.name)])
logging.info('---------aa------- %s' % aa.name)
aa.default_code = obj.name
for item in res['bfm_process_order_list']:
product = self.env['product.template'].sudo().product_create(product_id, item, order_id,
obj.name, i)
bom_data = self.env['mrp.bom'].get_bom(product)
logging.info('bom_data:%s' % bom_data)
if bom_data:
bom = self.env['mrp.bom'].bom_create(product, 'normal', False)
bom.bom_create_line_has(bom_data)
else:
if product.materials_type_id.gain_way == '自加工':
# 创建坯料
self_machining_embryo = self.env['product.template'].sudo().no_bom_product_create(
self_machining_id,
item, order_id,
'self_machining',
i)
# 创建坯料的bom
self_machining_bom = self.env['mrp.bom'].bom_create(self_machining_embryo, 'normal', False)
# 创建坯料里bom的组件
self_machining_bom_line = self_machining_bom.bom_create_line(self_machining_embryo)
if self_machining_bom_line is False:
self.cr.rollback()
return UserError('该订单模型的材料型号在您分配的工厂里暂未有原材料,请先配置再进行分配')
# 产品配置bom
product_bom_self_machining = self.env['mrp.bom'].bom_create(product, 'normal', False)
product_bom_self_machining.bom_create_line_has(self_machining_embryo)
elif product.materials_type_id.gain_way == '外协':
# 创建坯料
outsource_embryo = self.env['product.template'].sudo().no_bom_product_create(outsource_id, item,
order_id,
'subcontract', i)
# 创建坯料的bom
outsource_bom = self.env['mrp.bom'].bom_create(outsource_embryo, 'subcontract', True)
# 创建坯料的bom的组件
outsource_bom_line = outsource_bom.with_user(
self.env.ref("base.user_admin")).bom_create_line(outsource_embryo)
if outsource_bom_line is False:
self.cr.rollback()
return UserError('该订单模型的材料型号在您分配的工厂里暂未有原材料,请先配置再进行分配')
# 产品配置bom
product_bom_outsource = self.env['mrp.bom'].bom_create(product, 'normal', False)
product_bom_outsource.bom_create_line_has(outsource_embryo)
elif product.materials_type_id.gain_way == '采购':
purchase_embryo = self.env['product.template'].sudo().no_bom_product_create(purchase_id, item,
order_id,
'purchase', i)
# 产品配置bom
product_bom_purchase = self.env['mrp.bom'].bom_create(product, 'normal', False)
product_bom_purchase.bom_create_line_has(purchase_embryo)
order_id.with_user(self.env.ref("base.user_admin")).sale_order_create_line(product, item)
except Exception as e:
logging.error('工厂创建销售订单和产品失败,请联系管理员'.format(e))
# self.cr.rollback()
return UserError('工厂创建销售订单和产品失败,请联系管理员')
# 特征识别
def feature_recognition(self, report_path, model_code):
feature_path = self.env['sf.auto_quatotion.common'].sudo().get_feature_full_path()
process_time_db_path = self.env['sf.auto_quatotion.common'].sudo().get_process_time_db_path()
ret = self.env['sf.auto_quatotion.common'].sudo().get_auto_quatotion(report_path, feature_path,
process_time_db_path,
model_code)
return ret
# 模型上色
def model_coloring(self, order):
url = '/api/library_of_models/create'
config = self.env['res.config.settings'].get_values()
config_header = Common.get_headers(self, config['token'], config['sf_secret_key'])
logging.info('order: %s' % order.name)
if order:
attachment = order.upload_model_file[0]
base64_data = base64.b64encode(attachment.datas)
base64_datas = base64_data.decode('utf-8')
model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest()
logging.info('model_file-size: %s' % len(order.model_file))
logging.info('attachment.datas-size: %s' % len(attachment.datas))
vals = {
'model_code': model_code,
'model_data': base64_data,
'model_name': attachment.name,
'model_long': order.model_length,
'model_width': order.model_width,
'model_height': order.model_height,
'model_volume': order.model_volume,
'model_order_no': order.name,
'remark': '订单号:%s 客户:%s' % (order.name, order.customer_id.name)
}
try:
ret = requests.post((config['sf_url'] + url), json={}, data=vals, headers=config_header,
timeout=60)
ret = ret.json()
# result = json.loads(ret['result'])
if ret['status'] == 1:
order.model_color_state = 'success'
else:
order.model_color_state = 'fail'
raise UserError(ret['message'])
except Exception as e:
order.model_color_state = 'fail'
raise UserError("模型上色失败")
# 自动报价
def _get_price(self, order):
url = '/api/automatic_quotes'
config = self.env['res.config.settings'].sudo().get_values()
config_header = Common.get_headers(self, config['token'], config['sf_secret_key'])
logging.info("报价接口..........% s" % order.name)
try:
if order:
vals = {}
# mrs合作伙伴token
vals['token'] = config['token']
vals['accuracy'] = order.machining_precision
vals['number'] = order.quantity
vals['process_code'] = 0
vals['texture_code'] = order.material_model_id.materials_no
vals['delivery_days'] = 15
if order.model_file:
for item in order.upload_model_file:
if item.ids[0]:
logging.info('create-attachment:%s' % int(item.ids[0]))
attachment = self.env['ir.attachment'].sudo().search([('id', '=', int(item.ids[0]))])
vals['attachment_id'] = attachment.id
else:
vals['attachment_id'] = ''
vals['feature_infos'] = order.model_feature
vals['model_long'] = order.model_length
vals['model_width'] = order.model_width
vals['model_height'] = order.model_height
logging.info('vals:%s' % vals)
ret = requests.post((config['sf_url'] + url), json={}, data=vals, headers=config_header)
result = json.dumps(json.loads(ret.text), ensure_ascii=False, indent=4, separators=(',', ':'))
logging.info('报价接口返回:%s' % result)
price_result = json.loads(result)
# random.randint(0, 10000)
order.write({'price': price_result.get('price')})
else:
raise UserError("订单不存在")
except Exception as e:
if ret['status'] != 1:
raise UserError(e)
else:
raise UserError("自动报价失败,请联系管理员")