diff --git a/sf_base/__manifest__.py b/sf_base/__manifest__.py index 7e53ef7b..0640922a 100644 --- a/sf_base/__manifest__.py +++ b/sf_base/__manifest__.py @@ -10,7 +10,7 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['account', 'base', 'mrp_workorder'], + 'depends': ['account', 'base', 'mrp_workorder','sale'], 'data': [ 'security/group_security.xml', 'security/ir.model.access.csv', @@ -18,6 +18,7 @@ 'views/common_view.xml', 'views/fixture_view.xml', 'views/functional_fixture_view.xml', + # 'views/quick_easy_order_view.xml', 'views/menu_view.xml', "views/tool_views.xml", "views/tool_menu.xml", diff --git a/sf_base/models/__init__.py b/sf_base/models/__init__.py index cc906ab8..20bf8c6b 100644 --- a/sf_base/models/__init__.py +++ b/sf_base/models/__init__.py @@ -3,6 +3,8 @@ from . import common from . import tool_base_new from . import fixture from . import functional_fixture +# from . import quick_easy_order + diff --git a/sf_base/models/base.py b/sf_base/models/base.py index 168272c6..042b77f4 100644 --- a/sf_base/models/base.py +++ b/sf_base/models/base.py @@ -256,7 +256,7 @@ class MachineToolType(models.Model): name = fields.Char('名称') brand_id = fields.Many2one('sf.machine.brand', string='品牌') knife_type = fields.Selection( - [("BT40", "BT40"), ("BT30", "BT30")], + [("BT40", "BT40"), ("BT30", "BT30"), ("BT50", "BT50")], default="", string="刀把类型") number_of_knife_library = fields.Integer('刀库数量') rotate_speed = fields.Integer('转速') diff --git a/sf_base/models/quick_easy_order.py b/sf_base/models/quick_easy_order.py new file mode 100644 index 00000000..c39b3fc5 --- /dev/null +++ b/sf_base/models/quick_easy_order.py @@ -0,0 +1,26 @@ +from odoo import models, fields +import datetime +import base64 + + +class QuickEasyOrder(models.Model): + _name = 'quick.easy.order' + _description = '简易下单' + + name = fields.Char('订单编号', default=lambda self: self.env['ir.sequence'].next_by_code('quick.easy.order')) + 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='加工精度') + material_id = fields.Many2one('sf.production.materials', '材料') + material_model_id = fields.Many2one('sf.materials.model', '型号') + process_id = fields.Many2one('sf.production.process', string='表面工艺') + parameter_ids = fields.One2many('sf.production.process.parameter', 'process_id', string='可选参数') + quantity = fields.Integer('数量') + price = fields.Float('总价') + model_file = fields.Binary('模型文件') + 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) diff --git a/sf_base/views/base_view.xml b/sf_base/views/base_view.xml index 4e7e5290..cdff5a4b 100644 --- a/sf_base/views/base_view.xml +++ b/sf_base/views/base_view.xml @@ -30,6 +30,7 @@ + diff --git a/sf_base/views/quick_easy_order_view.xml b/sf_base/views/quick_easy_order_view.xml new file mode 100644 index 00000000..5126afb1 --- /dev/null +++ b/sf_base/views/quick_easy_order_view.xml @@ -0,0 +1,91 @@ + + + + + 快速订单编码规则 + quick.easy.order + FP-%(year)s-%(month)s%(day)s- + 4 + + + # ---------- 快速订单 ------------ + + + tree.quick.easy.order + quick.easy.order + + + + + + + + + + + + + + + + form.quick.easy.order + quick.easy.order + + + + + + + + + + + + + + + + + + + + + + + + + + search.quick.easy.order + quick.easy.order + + + + + + + + + + + 快速订单 + quick.easy.order + tree,form + [] + {} + + + [便捷订单] 还没有哦!点左上角的[创建]按钮,沙发归你了! + + + + + + + + + + + + + \ No newline at end of file diff --git a/sf_base/views/tool_menu.xml b/sf_base/views/tool_menu.xml index 91d95b37..c426195e 100644 --- a/sf_base/views/tool_menu.xml +++ b/sf_base/views/tool_menu.xml @@ -2,35 +2,35 @@ - + 刀具物料 ir.actions.act_window sf.cutting.tool.material tree - + 刀具类型 ir.actions.act_window sf.cutting.tool.type tree - + 刀具型号 ir.actions.act_window sf.cutting.tool.model tree,form - + 功能刀具 ir.actions.act_window sf.functional.cutting.tool tree,form - + 功能刀具类型 ir.actions.act_window @@ -38,52 +38,53 @@ tree - + - + /> + - + /> + - + /> + - + /> + - + /> + + /> + diff --git a/sf_bf_connect/models/http.py b/sf_bf_connect/models/http.py index eb969eb6..6b0b3ebd 100644 --- a/sf_bf_connect/models/http.py +++ b/sf_bf_connect/models/http.py @@ -35,8 +35,8 @@ class Http(models.AbstractModel): timestamp_str = int(time.time()) # 设置API接口请求时间,不能超过5秒 deltime = datetime.timedelta(seconds=5) - if abs(int(datas['HTTP_TIMESTAMP'])-timestamp_str) > deltime.seconds: - raise AuthenticationError('请求已过期') + # if abs(int(datas['HTTP_TIMESTAMP'])-timestamp_str) > deltime.seconds: + # raise AuthenticationError('请求已过期') # 获得sha1_str加密字符串 post_time = int(datas['HTTP_TIMESTAMP']) check_str = '%s%s%s' % (datas['HTTP_TOKEN'], post_time, factory_secret.sf_secret_key) diff --git a/sf_dlm/models/__init__.py b/sf_dlm/models/__init__.py index 2e6676ee..f6ca84cc 100644 --- a/sf_dlm/models/__init__.py +++ b/sf_dlm/models/__init__.py @@ -1,5 +1,5 @@ -from . import product_template -from. import product_supplierinfo +# from . import product_template +from . import product_supplierinfo diff --git a/sf_dlm/models/product_template.py b/sf_dlm/models/product_template.py index 23576de7..c6719361 100644 --- a/sf_dlm/models/product_template.py +++ b/sf_dlm/models/product_template.py @@ -9,8 +9,8 @@ import hashlib import os -class ResProduct(models.Model): - _inherit = 'product.template' +# class ResProduct(models.Model): +# _inherit = 'product.template' # image_1920 = fields.Image(related='cutting_tool_parameter_image', store=True, # domain=[('cutting_tool_parameter_image', '!=', False)]) @@ -184,194 +184,6 @@ class ResProduct(models.Model): # if self.cutting_tool_parameter_nut <= 0: # raise ValueError('该产品中配对螺母不能为零,请确认并重新输入!') - def _get_volume_uom_id_from_ir_config_parameter(self): - product_length_in_feet_param = self.env['ir.config_parameter'].sudo().get_param('product.volume_in_cubic_feet') - if product_length_in_feet_param == '1': - return self.env.ref('uom.product_uom_cubic_foot') - else: - return self.env.ref('sf_dlm.product_uom_cubic_millimeter') - - # 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品 - def product_create(self, product_id, item, order_id, order_number, i): - copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy() - copy_product_id.product_tmpl_id.active = True - model_type = self.env['sf.model.type'].search([], limit=1) - attachment = self.attachment_create(item['model_name'], item['model_data']) - vals = { - 'name': '%s-%s-%s' % ('P', order_id.name, i), - 'model_long': item['model_long'] + model_type.embryo_tolerance, - 'model_width': item['model_width'] + model_type.embryo_tolerance, - 'model_height': item['model_height'] + model_type.embryo_tolerance, - 'model_volume': (item['model_long'] + model_type.embryo_tolerance) * ( - item['model_width'] + model_type.embryo_tolerance) * ( - item['model_height'] + model_type.embryo_tolerance), - 'product_model_type_id': model_type.id, - 'model_processing_panel': 'R', - 'model_machining_precision': item['model_machining_precision'], - 'model_code': item['barcode'], - 'length': item['model_long'], - 'width': item['model_width'], - 'height': item['model_height'], - 'volume': item['model_long'] * item['model_width'] * item['model_height'], - 'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']), - 'model_name': attachment.name, - 'upload_model_file': [(6, 0, [attachment.id])], - # 'tag_ids': [(6, 0, [t.id for t in account_template.tag_ids])], - # 'single_manufacturing': True, - # 'tracking': 'serial', - 'list_price': item['price'], - # 'categ_id': self.env.ref('sf_dlm.product_category_finished_sf').id, - 'materials_id': self.env['sf.production.materials'].search( - [('materials_no', '=', item['texture_code'])]).id, - 'materials_type_id': self.env['sf.materials.model'].search( - [('materials_no', '=', item['texture_type_code'])]).id, - # 'model_surface_process_ids': self.get_production_process_id(item['surface_process_code']), - 'model_process_parameters_ids': self.get_process_parameters_id(item['process_parameters_code']), - 'model_remark': item['remark'], - 'default_code': '%s-%s' % (order_number, i), - # 'barcode': item['barcode'], - 'active': True, - # 'route_ids': self._get_routes('') - } - copy_product_id.sudo().write(vals) - # product_id.product_tmpl_id.active = False - return copy_product_id - - def _get_ids(self, param): - type_ids = [] - if not param: - return [] - for item in param: - type_ids.append(item.id) - return [(6, 0, type_ids)] - - def get_process_parameters_id(self, process_parameters_code): - process_parameters_ids = [] - for item in process_parameters_code: - process_parameters = self.env['sf.production.process.parameter'].search([('code', '=', item)]) - process_parameters_ids.append(process_parameters.id) - return [(6, 0, process_parameters_ids)] - - def attachment_create(self, name, data): - attachment = self.env['ir.attachment'].create({ - 'datas': base64.b64decode(data), - 'type': 'binary', - 'public': True, - 'description': '模型文件', - 'name': name - }) - return attachment - - # 创建坯料 - def no_bom_product_create(self, product_id, item, order_id, route_type, i): - no_bom_copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy() - no_bom_copy_product_id.product_tmpl_id.active = True - materials_id = self.env['sf.production.materials'].search( - [('materials_no', '=', item['texture_code'])]) - materials_type_id = self.env['sf.materials.model'].search( - [('materials_no', '=', item['texture_type_code'])]) - model_type = self.env['sf.model.type'].search([], limit=1) - supplier = self.env['mrp.bom'].get_supplier(materials_type_id) - logging.info('no_bom_copy_product_supplier-vals:%s' % supplier) - vals = { - 'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R', - order_id.name, i, materials_id.name, materials_type_id.name, - item['model_long'] + model_type.embryo_tolerance, - item['model_width'] + model_type.embryo_tolerance, - item['model_height'] + model_type.embryo_tolerance), - 'length': item['model_long'] + model_type.embryo_tolerance, - 'width': item['model_width'] + model_type.embryo_tolerance, - 'height': item['model_height'] + model_type.embryo_tolerance, - 'volume': (item['model_long'] + model_type.embryo_tolerance) * ( - item['model_width'] + model_type.embryo_tolerance) * ( - item['model_height'] + model_type.embryo_tolerance), - 'embryo_model_type_id': model_type.id, - 'list_price': item['price'], - 'materials_id': materials_id.id, - 'materials_type_id': materials_type_id.id, - 'is_bfm': True, - # 'route_ids': self._get_routes(route_type), - # 'categ_id': self.env.ref('sf_dlm.product_category_embryo_sf').id, - # 'model_surface_process_id': self.env['sf.production.process'].search( - # [('process_encode', '=', item['surface_process_code'])]).id, - # 'model_process_parameters_id': self.env['sf.processing.technology'].search( - # [('process_encode', '=', item['process_parameters_code'])]).id, - 'active': True - } - # 外协和采购生成的坯料需要根据材料型号绑定供应商 - if route_type == 'subcontract' or route_type == 'purchase': - no_bom_copy_product_id.purchase_ok = True - no_bom_copy_product_id.seller_ids = [ - (0, 0, {'partner_id': supplier.partner_id.id, 'delay': 1.0})] - if route_type == 'subcontract': - partner = self.env['res.partner'].search([('id', '=', supplier.partner_id.id)]) - partner.is_subcontractor = True - no_bom_copy_product_id.write(vals) - logging.info('no_bom_copy_product_id-vals:%s' % vals) - # product_id.product_tmpl_id.active = False - return no_bom_copy_product_id - - @api.model_create_multi - def create(self, vals_list): - for vals in vals_list: - if vals.get('upload_model_file'): - if vals.get('is_bfm') is False and vals.get('categ_type') == '成品': - for item in vals['upload_model_file']: - 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.transition_glb_file(report_path, model_code) - logging.info('create-model_file:%s' % len(vals['model_file'])) - self._sanitize_vals(vals) - templates = super(ResProduct, self).create(vals_list) - if "create_product_product" not in self._context: - templates._create_variant_ids() - - # This is needed to set given values to first variant after creation - for template, vals in zip(templates, vals_list): - related_vals = {} - for field_name in self._get_related_fields_variant_template(): - if vals.get(field_name): - related_vals[field_name] = vals[field_name] - if related_vals: - template.write(related_vals) - return templates - - @api.onchange('upload_model_file') - def onchange_model_file(self): - for item in self: - if item.upload_model_file: - if len(item.upload_model_file) > 1: - raise ValidationError('只允许上传一个文件') - manufacturing_order = self.env['mrp.production'].search([('product_id', '=', self.id)]) - if manufacturing_order: - raise ValidationError('该产品已生成制造订单,无法进行修改') - file_attachment_id = item.upload_model_file[0] - # 附件路径 - report_path = file_attachment_id._full_path(file_attachment_id.store_fname) - base64_data = base64.b64encode(file_attachment_id.datas) - base64_datas = base64_data.decode('utf-8') - model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest() - item.model_file = self.transition_glb_file(report_path, model_code) - - # 将attach的datas内容转为glb文件 - def transition_glb_file(self, report_path, code): - shapes = read_step_file(report_path) - output_file = os.path.join('/tmp', str(code) + '.stl') - write_stl_file(shapes, output_file, 'binary', 0.03, 0.5) - # 转化为glb - output_glb_file = os.path.join('/tmp', str(code) + '.glb') - util_path = get_resource_path('sf_dlm', '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 # @api.onchange('cutting_tool_material_id') # def _get_cutting_tool_material_info(self): diff --git a/sf_maintenance/views/maintenance_request_views.xml b/sf_maintenance/views/maintenance_request_views.xml index c942deac..39c82384 100644 --- a/sf_maintenance/views/maintenance_request_views.xml +++ b/sf_maintenance/views/maintenance_request_views.xml @@ -21,7 +21,7 @@ - + diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index aa9da848..eac02928 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -1,4 +1,12 @@ from odoo import models, fields, api +from odoo.exceptions import ValidationError +from odoo.modules import get_resource_path +from OCC.Extend.DataExchange import read_step_file +from OCC.Extend.DataExchange import write_stl_file +import logging +import base64 +import hashlib +import os class ResProductMo(models.Model): @@ -318,8 +326,276 @@ class ResProductMo(models.Model): item.cutting_tool_blade_ids = False item.cutting_tool_handle_ids = False + def _get_volume_uom_id_from_ir_config_parameter(self): + product_length_in_feet_param = self.env['ir.config_parameter'].sudo().get_param('product.volume_in_cubic_feet') + if product_length_in_feet_param == '1': + return self.env.ref('uom.product_uom_cubic_foot') + else: + return self.env.ref('sf_dlm.product_uom_cubic_millimeter') + + # 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品 + def product_create(self, product_id, item, order_id, order_number, i): + copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy() + copy_product_id.product_tmpl_id.active = True + model_type = self.env['sf.model.type'].search([], limit=1) + attachment = self.attachment_create(item['model_name'], item['model_data']) + vals = { + 'name': '%s-%s-%s' % ('P', order_id.name, i), + 'model_long': item['model_long'] + model_type.embryo_tolerance, + 'model_width': item['model_width'] + model_type.embryo_tolerance, + 'model_height': item['model_height'] + model_type.embryo_tolerance, + 'model_volume': (item['model_long'] + model_type.embryo_tolerance) * ( + item['model_width'] + model_type.embryo_tolerance) * ( + item['model_height'] + model_type.embryo_tolerance), + 'product_model_type_id': model_type.id, + 'model_processing_panel': 'R', + 'model_machining_precision': item['model_machining_precision'], + 'model_code': item['barcode'], + 'length': item['model_long'], + 'width': item['model_width'], + 'height': item['model_height'], + 'volume': item['model_long'] * item['model_width'] * item['model_height'], + 'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']), + 'model_name': attachment.name, + 'upload_model_file': [(6, 0, [attachment.id])], + # 'tag_ids': [(6, 0, [t.id for t in account_template.tag_ids])], + # 'single_manufacturing': True, + # 'tracking': 'serial', + 'list_price': item['price'], + # 'categ_id': self.env.ref('sf_dlm.product_category_finished_sf').id, + 'materials_id': self.env['sf.production.materials'].search( + [('materials_no', '=', item['texture_code'])]).id, + 'materials_type_id': self.env['sf.materials.model'].search( + [('materials_no', '=', item['texture_type_code'])]).id, + # 'model_surface_process_ids': self.get_production_process_id(item['surface_process_code']), + 'model_process_parameters_ids': [(6, 0, [])] if not item.get('process_parameters_code') else self.get_process_parameters_id(item['process_parameters_code']), + 'model_remark': item['remark'], + 'default_code': '%s-%s' % (order_number, i), + # 'barcode': item['barcode'], + 'active': True, + # 'route_ids': self._get_routes('') + } + copy_product_id.sudo().write(vals) + # product_id.product_tmpl_id.active = False + return copy_product_id + + def _get_ids(self, param): + type_ids = [] + if not param: + return [] + for item in param: + type_ids.append(item.id) + return [(6, 0, type_ids)] + + def get_process_parameters_id(self, process_parameters_code): + process_parameters_ids = [] + for item in process_parameters_code: + process_parameters = self.env['sf.production.process.parameter'].search([('code', '=', item)]) + process_parameters_ids.append(process_parameters.id) + return [(6, 0, process_parameters_ids)] + + def attachment_create(self, name, data): + attachment = self.env['ir.attachment'].create({ + 'datas': base64.b64decode(data), + 'type': 'binary', + 'public': True, + 'description': '模型文件', + 'name': name + }) + return attachment + + # 创建坯料 + def no_bom_product_create(self, product_id, item, order_id, route_type, i): + no_bom_copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy() + no_bom_copy_product_id.product_tmpl_id.active = True + materials_id = self.env['sf.production.materials'].search( + [('materials_no', '=', item['texture_code'])]) + materials_type_id = self.env['sf.materials.model'].search( + [('materials_no', '=', item['texture_type_code'])]) + model_type = self.env['sf.model.type'].search([], limit=1) + supplier = self.env['mrp.bom'].get_supplier(materials_type_id) + logging.info('no_bom_copy_product_supplier-vals:%s' % supplier) + vals = { + 'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R', + order_id.name, i, materials_id.name, materials_type_id.name, + item['model_long'] + model_type.embryo_tolerance, + item['model_width'] + model_type.embryo_tolerance, + item['model_height'] + model_type.embryo_tolerance), + 'length': item['model_long'] + model_type.embryo_tolerance, + 'width': item['model_width'] + model_type.embryo_tolerance, + 'height': item['model_height'] + model_type.embryo_tolerance, + 'volume': (item['model_long'] + model_type.embryo_tolerance) * ( + item['model_width'] + model_type.embryo_tolerance) * ( + item['model_height'] + model_type.embryo_tolerance), + 'embryo_model_type_id': model_type.id, + 'list_price': item['price'], + 'materials_id': materials_id.id, + 'materials_type_id': materials_type_id.id, + 'is_bfm': True, + # 'route_ids': self._get_routes(route_type), + # 'categ_id': self.env.ref('sf_dlm.product_category_embryo_sf').id, + # 'model_surface_process_id': self.env['sf.production.process'].search( + # [('process_encode', '=', item['surface_process_code'])]).id, + # 'model_process_parameters_id': self.env['sf.processing.technology'].search( + # [('process_encode', '=', item['process_parameters_code'])]).id, + 'active': True + } + # 外协和采购生成的坯料需要根据材料型号绑定供应商 + if route_type == 'subcontract' or route_type == 'purchase': + no_bom_copy_product_id.purchase_ok = True + no_bom_copy_product_id.seller_ids = [ + (0, 0, {'partner_id': supplier.partner_id.id, 'delay': 1.0})] + if route_type == 'subcontract': + partner = self.env['res.partner'].search([('id', '=', supplier.partner_id.id)]) + partner.is_subcontractor = True + no_bom_copy_product_id.write(vals) + logging.info('no_bom_copy_product_id-vals:%s' % vals) + # product_id.product_tmpl_id.active = False + return no_bom_copy_product_id + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if vals.get('upload_model_file'): + if vals.get('is_bfm') is False and vals.get('categ_type') == '成品': + for item in vals['upload_model_file']: + 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.transition_glb_file(report_path, model_code) + logging.info('create-model_file:%s' % len(vals['model_file'])) + self._sanitize_vals(vals) + templates = super(ResProductMo, self).create(vals_list) + if "create_product_product" not in self._context: + templates._create_variant_ids() + + # This is needed to set given values to first variant after creation + for template, vals in zip(templates, vals_list): + related_vals = {} + for field_name in self._get_related_fields_variant_template(): + if vals.get(field_name): + related_vals[field_name] = vals[field_name] + if related_vals: + template.write(related_vals) + return templates + + @api.onchange('upload_model_file') + def onchange_model_file(self): + for item in self: + if item.upload_model_file: + if len(item.upload_model_file) > 1: + raise ValidationError('只允许上传一个文件') + manufacturing_order = self.env['mrp.production'].search([('product_id', '=', self.id)]) + if manufacturing_order: + raise ValidationError('该产品已生成制造订单,无法进行修改') + file_attachment_id = item.upload_model_file[0] + # 附件路径 + report_path = file_attachment_id._full_path(file_attachment_id.store_fname) + base64_data = base64.b64encode(file_attachment_id.datas) + base64_datas = base64_data.decode('utf-8') + model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest() + item.model_file = self.transition_glb_file(report_path, model_code) + + # 将attach的datas内容转为glb文件 + def transition_glb_file(self, report_path, code): + shapes = read_step_file(report_path) + output_file = os.path.join('/tmp', str(code) + '.stl') + write_stl_file(shapes, output_file, 'binary', 0.03, 0.5) + # 转化为glb + output_glb_file = os.path.join('/tmp', str(code) + '.glb') + util_path = get_resource_path('sf_dlm', '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 + class ResMrpBomMo(models.Model): _inherit = 'mrp.bom' subcontractor_id = fields.Many2one('res.partner', string='外包商') + + def bom_create_line_has(self, embryo): + vals = { + 'bom_id': self.id, + 'product_id': embryo.id, + 'product_tmpl_id': embryo.product_tmpl_id.id, + 'product_qty': 1, + 'product_uom_id': 1 + } + return self.env['mrp.bom.line'].create(vals) + + # 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom + + def bom_create(self, product, bom_type, product_type): + bom_id = self.env['mrp.bom'].create({ + 'product_tmpl_id': product.product_tmpl_id.id, + 'type': bom_type, + # 'subcontractor_id': '' or subcontract.partner_id.id, + 'product_qty': 1, + 'product_uom_id': 1 + }) + if bom_type == 'subcontract' and product_type is not False: + subcontract = self.get_supplier(product.materials_type_id) + bom_id.subcontractor_id = subcontract.partner_id.id + return bom_id + + # 坯料BOM组件:选取当前坯料原材料, + # 然后根据当前的坯料的体积得出需要的原材料重量(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤) + # 坯料所需原材料公式:当前的坯料的体积(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤) + + def bom_create_line(self, embryo): + # 选取当前坯料原材料 + raw_bom_line = self.get_raw_bom(embryo) + if raw_bom_line: + bom_line = self.env['mrp.bom.line'].create({ + 'bom_id': self.id, + 'product_id': raw_bom_line.id, + 'product_tmpl_id': raw_bom_line.product_tmpl_id.id, + 'product_qty': round(embryo.volume * raw_bom_line.materials_type_id.density / 1000000), + 'product_uom_id': raw_bom_line.uom_id.id, + }) + return bom_line + else: + return False + + # 查询材料型号默认排第一的供应商 + + def get_supplier(self, materials_type): + seller_id = self.env['sf.supplier.sort'].search( + [('materials_model_id', '=', materials_type.id)], + limit=1, + order='sequence asc') + return seller_id + + # 匹配bom + + def get_bom(self, product): + embryo_has = self.env['product.product'].search( + [('categ_id.type', '=', '坯料'), ('materials_type_id', '=', product.materials_type_id.id), + ('length', '>', product.length), ('width', '>', product.width), + ('height', '>', product.height), ('is_bfm', '=', False) + ], + limit=1, + order='volume desc' + ) + logging.info('get_bom-vals:%s' % embryo_has) + if embryo_has: + rate_of_waste = ((embryo_has.volume - product.model_volume) % embryo_has.volume) * 100 + if rate_of_waste <= 20: + return embryo_has + else: + return + + # 查bom的原材料 + + def get_raw_bom(self, product): + raw_bom = self.env['product.product'].search( + [('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)]) + return raw_bom diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index 8ab2384a..3d309689 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -569,15 +569,20 @@ class MachineBrand(models.Model): result = json.loads(r['result']) if result['status'] == 1: for item in result['machine_brand_all_list']: + brand = self.env['sf.machine.brand'].search( [("code", '=', item['code'])]) if not brand: + if item.get('image_brand'): + image = base64.b64decode(item['image_brand']) + else: + image = '' self.env['sf.machine.brand'].create({ "id": item['id'], "name": item['name'], "code": item['code'], "remark": item['remark'], - "image_brand": '' if not item['image_brand'] else base64.b64encode(item.image_brand), + "image_brand": image, "tag_ids": self.env['sf.machine.brand.tags'].search( [("name", 'in', item['tag_ids'])]).ids }) diff --git a/sf_sale/__manifest__.py b/sf_sale/__manifest__.py index 19a102e9..b3cb99a1 100644 --- a/sf_sale/__manifest__.py +++ b/sf_sale/__manifest__.py @@ -10,10 +10,12 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sale', 'sale_management', 'point_of_sale', 'web_widget_model_viewer'], + 'depends': ['sale_management', 'web_widget_model_viewer',], 'data': [ + 'security/group_security.xml', + 'security/ir.model.access.csv', 'views/sale_order_view.xml', - 'views/quick_easy_order_view.xml', + 'views/quick_easy_order_view.xml' ], 'demo': [ ], diff --git a/sf_sale/models/quick_easy_order.py b/sf_sale/models/quick_easy_order.py index c39b3fc5..6037c27a 100644 --- a/sf_sale/models/quick_easy_order.py +++ b/sf_sale/models/quick_easy_order.py @@ -1,6 +1,16 @@ -from odoo import models, fields -import datetime +from odoo import models, fields, api +from odoo.modules import get_resource_path +from OCC.Extend.DataExchange import read_step_file +from OCC.Extend.DataExchange import write_stl_file +from odoo.exceptions import ValidationError, UserError +from odoo.addons.sf_base.commons.common import Common +from datetime import datetime +import logging import base64 +import hashlib +import os +import json +import requests class QuickEasyOrder(models.Model): @@ -8,19 +18,166 @@ class QuickEasyOrder(models.Model): _description = '简易下单' 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('加工面') 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='加工精度') - material_id = fields.Many2one('sf.production.materials', '材料') - material_model_id = fields.Many2one('sf.materials.model', '型号') + ('0.01', '±0.01mm')], string='加工精度', default='0.10') + material_id = fields.Many2one('sf.production.materials', '材料', compute='_compute_material_model', store=True) + material_model_id = fields.Many2one('sf.materials.model', '型号', compute='_compute_material_model', store=True) process_id = fields.Many2one('sf.production.process', string='表面工艺') parameter_ids = fields.One2many('sf.production.process.parameter', 'process_id', string='可选参数') - quantity = fields.Integer('数量') + quantity = fields.Integer('数量', default=1) + unit_price = fields.Float('单价') price = fields.Float('总价') model_file = fields.Binary('模型文件') - upload_model_file = fields.Many2many('ir.attachment', 'upload_qf_model_file_attachment_ref', string='上传模型文件') + 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) + + @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.transition_glb_file(report_path, model_code) + logging.info('create-model_file:%s' % len(vals['model_file'])) + feature_path = self.env['jikimo.auto_quatotion.common'].sudo().get_feature_full_path() + # price_path = self.env['jikimo.auto_quatotion.common'].get_price_full_path() + process_time_db_path = self.env['jikimo.auto_quatotion.common'].sudo().get_process_time_db_path() + ret = self.env['jikimo.auto_quatotion.common'].sudo().get_auto_quatotion(report_path, feature_path, + process_time_db_path, + model_code) + logging.info("自动报价返回值: %s" % ret) + shapes = ret['boxshape'].tolist() + logging.info("自动报价boxshape: %s" % shapes) + target_faces = ','.join(ret['target_faces']), + # item.price = ret['price'] + item.model_length = shapes[0] # 长 单位mm + item.model_width = shapes[1] # 宽 + item.model_height = shapes[2] # 高 + item.model_volume = shapes[0] * shapes[1] * shapes[2] + item.model_processing_side = target_faces[0] + obj = super(QuickEasyOrder, self).create(vals) + self.distribute_to_factory(obj) + return obj + + # 将attach的datas内容转为glb文件 + def transition_glb_file(self, report_path, model_code): + shapes = read_step_file(report_path) + output_file = os.path.join('C:/Users/43484/Desktop/机企猫工作文档', str(model_code) + '.stl') + # 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('C:/Users/43484/Desktop/机企猫工作文档', str(model_code) + '.glb') + # output_glb_file = os.path.join('/tmp', str(model_code) + '.glb') + util_path = get_resource_path('mrs_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] + # 附件路径 + report_path = file_attachment_id._full_path(file_attachment_id.store_fname) + logging.info("模型路径: %s" % report_path) + base64_data = base64.b64encode(file_attachment_id.datas) + base64_datas = base64_data.decode('utf-8') + model_code = hashlib.sha1(base64_datas.encode('utf-8')).hexdigest() + logging.info("模型编码: %s" % model_code) + item.model_file = self.transition_glb_file(report_path, model_code) + else: + item.model_file = False + + def distribute_to_factory(self, obj): + """ + 派单到工厂 + :return: + """ + web_base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url', default=''), + url = '/api/bfm_process_order/list' + res = {'order_number': obj.name, 'delivery_end_date': str(datetime.now()), + 'delivery_name': 'XXXXX', 'delivery_telephone': 'XXXXX', + 'delivery_address': 'XXXXX', + 'bfm_process_order_list': []} + factory = self.env['res.partner'].sudo().search([], limit=1, order='id desc') + config_header = Common.get_headers(self, factory.sf_token, factory.sf_secret_key) + 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)) + val = { + 'model_long': 100, + 'model_width': 100, + 'model_height': 100, + 'model_volume': 300, + '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['jikimo.surface.process.item']._json_surface_process_item_code( + # item), + 'price': item.price, + 'number': item.quantity, + 'total_amount': item.price, + 'remark': '', + 'barcode': barcode + } + res['bfm_process_order_list'].append(val) + res['bfm_process_order_list'] = json.dumps(res['bfm_process_order_list']) + try: + ret = requests.post((web_base_url[0] + url), json={}, data=res, + headers=config_header) + ret = ret.json() + if ret['status'] == 1: + self.write( + {'state': '待接单'}) + else: + raise UserError(ret['message']) + except Exception as e: + if ret['status'] != 1: + raise UserError(e) + else: + raise UserError("分配工厂失败,请联系管理员") diff --git a/sf_sale/security/group_security.xml b/sf_sale/security/group_security.xml new file mode 100644 index 00000000..b8668782 --- /dev/null +++ b/sf_sale/security/group_security.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/sf_sale/security/ir.model.access.csv b/sf_sale/security/ir.model.access.csv new file mode 100644 index 00000000..9bc8bba0 --- /dev/null +++ b/sf_sale/security/ir.model.access.csv @@ -0,0 +1,9 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_quick_easy_order,quick_easy_order,model_quick_easy_order,base.group_user,1,1,1,1 + + + + + + + diff --git a/sf_sale/views/quick_easy_order_view.xml b/sf_sale/views/quick_easy_order_view.xml index ad254b6d..3d2fd66f 100644 --- a/sf_sale/views/quick_easy_order_view.xml +++ b/sf_sale/views/quick_easy_order_view.xml @@ -33,21 +33,26 @@ - - - + + - + + + + + + @@ -82,7 +87,13 @@ - + + + + + \ No newline at end of file diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index 361ea14a..d7254f4a 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -29,12 +29,12 @@ - + - + - + - + - + - + - + - + - + - - - - + + + + diff --git a/sf_tool_management/views/tool_material_search.xml b/sf_tool_management/views/tool_material_search.xml index e75344dd..9e844bfd 100644 --- a/sf_tool_management/views/tool_material_search.xml +++ b/sf_tool_management/views/tool_material_search.xml @@ -126,12 +126,16 @@ - + + + + + + - @@ -143,147 +147,158 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web_widget_model_viewer/static/src/images/not_model.png b/web_widget_model_viewer/static/src/images/not_model.png new file mode 100644 index 00000000..16e69cea Binary files /dev/null and b/web_widget_model_viewer/static/src/images/not_model.png differ diff --git a/web_widget_model_viewer/static/src/js/3d_viewer.js b/web_widget_model_viewer/static/src/js/3d_viewer.js index 582c887f..f93cf207 100644 --- a/web_widget_model_viewer/static/src/js/3d_viewer.js +++ b/web_widget_model_viewer/static/src/js/3d_viewer.js @@ -19,31 +19,34 @@ var QWeb = core.qweb; import { Component, onWillUpdateProps, useState, useRef, useEffect } from "@odoo/owl"; export class StepViewer extends Component { - setup() { - console.log('setup') + setup() { this.props.url = this.formatUrl(); } - formatUrl(){ var url = ''; if (this.props.value) { - if (utils.is_bin_size(this.props.value)) { + if (this.props.value.slice(-1) == 'b' && !isNaN(this.props.value.split(' ')[0])) { var url_props = { base_url: session['web.base.url'], model: this.props.record.resModel, id: JSON.stringify(this.props.record.data['id']), field: this.props.name} - url = url_props['base_url']+'/web/content/'+url_props['model']+'/'+url_props['id']+'/'+url_props['field']+'?download=true' - console.log('url', url) - + url = url_props['base_url'].replace('http://', 'https://') +'/web/content/'+url_props['model']+'/'+url_props['id']+'/'+url_props['field']+'?download=true' + // url = 'http://localhost:8069'+'/web/content/'+url_props['model']+'/'+url_props['id']+'/'+url_props['field']+'?download=true' + console.log('url111111',url) + return url } else { - url = "data:model/gltf-binary;base64," + this.props.value; - console.log('url222', url) + url = "data:model/gltf-binary;base64," + this.props.value; + console.log('url2',url) + return url + // localStorage.setItem('url',url) + // let new_url = localStorage.getItem(('url')) + // var oViewer = document.getElementsByTagName('model-viewer')[0]; + // return new_url + // url = "web_widget_model_viewer/static/src/images/not_model.png"; } } - return url - } - + } } StepViewer.template = "web_widget_model_viewer.BinaryField3d";
+ [便捷订单] 还没有哦!点左上角的[创建]按钮,沙发归你了! +
+