from odoo import models, fields, api from odoo.exceptions import ValidationError import logging import base64 import os from OCC.Extend.DataExchange import read_step_file, write_stl_file from odoo.modules import get_resource_path class ResProductTemplate(models.Model): _inherit = 'product.template' # 模型的长,宽,高,体积,精度,材料 model_name = fields.Char('模型名称') model_long = 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('模型体积[m³]') model_machining_precision = fields.Selection([ ('±0.10mm', '±0.10mm'), ('±0.05mm', '±0.05mm'), ('±0.03mm', '±0.03mm'), ('±0.02mm', '±0.02mm'), ('±0.01mm', '±0.01mm')], string='加工精度') model_type_id = fields.Many2one('sf.model.type', string='模型类型') model_processing_panel = fields.Char('模型加工面板') model_surface_process_id = fields.Many2one('sf.production.process', string='表面工艺') model_process_parameters_id = fields.Many2one('sf.processing.technology', string='工艺参数') # model_price = fields.Float('模型单价', digits=(16, 3)) model_remark = fields.Char('模型备注说明') length = fields.Float('长[mm]', digits=(16, 3)) width = fields.Float('宽[mm]', digits=(16, 3)) height = fields.Float('高[mm]', digits=(16, 3)) materials_id = fields.Many2one('sf.production.materials', string='材料') materials_type_id = fields.Many2one('sf.materials.model', string='材料型号') single_manufacturing = fields.Boolean(string="单个制造") upload_model_file = fields.Many2many('ir.attachment', 'upload_model_file_attachment_ref', string='上传模型文件') model_file = fields.Binary('模型文件') # 胚料的库存路线设置 # def _get_routes(self, route_type): # route_manufacture = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False).sudo() # route_mto = self.env.ref('stock.route_warehouse0_mto', raise_if_not_found=False).sudo() # route_purchase = self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False).sudo() # if route_manufacture and route_mto: # # 外协 # if route_type == 'subcontract': # route_subcontract = self.env.ref('mrp_subcontracting.route_resupply_subcontractor_mto', # raise_if_not_found=False).sudo() # return [route_mto.id, route_purchase.id, route_subcontract.id] # elif route_type == 'purchase': # # 采购 # return [route_mto.id, route_purchase.id] # else: # return [route_mto.id, route_manufacture.id] # return [] # route_ids = fields.Many2many(default=lambda self: self._get_route()) # 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品 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' % (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), 'model_type_id': 1, # 'model_machining_precision': item['model_machining_precision'], 'model_processing_panel': 'A', 'model_machining_precision': '±0.10mm', 'length': item['model_long'], 'width': item['model_width'], 'height': item['model_height'], 'volume': item['model_long'] * item['model_width'] * item['model_height'], 'model_file': base64.b64decode(item['model_file']), 'model_name': attachment.name, 'upload_model_file': [(6, 0, [attachment.id])], # 'single_manufacturing': True, '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_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, '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) print(len(copy_product_id.model_file)) product_id.product_tmpl_id.active = False return copy_product_id def attachment_create(self, name, data): attachment = self.env['ir.attachment'].create({ 'datas': base64.b64decode(data), 'type': 'binary', 'description': '模型文件', 'name': name }) return attachment # 创建胚料 def no_bom_product_create(self, product_id, item, order_id, route_type): 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' % ( order_id.name, materials_id.name, materials_type_id.name, item['model_long'], item['model_width'], item['model_height']), '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), # 'model_price': item['price'], 'list_price': item['price'], 'materials_id': materials_id.id, 'materials_type_id': materials_type_id.id, # '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': 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, 'is_subcontractor': True})] logging.info('no_bom_copy_product_id-seller_ids-vals:%s' % no_bom_copy_product_id.seller_ids) elif 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})] logging.info('no_bom_copy_product_id-seller_ids-vals:%s' % no_bom_copy_product_id.seller_ids) 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.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_name = file_attachment_id.name # 附件路径 report_path = file_attachment_id._full_path(file_attachment_id.store_fname) shapes = read_step_file(report_path) output_file = get_resource_path('sf_dlm', 'static/file', 'out.stl') write_stl_file(shapes, output_file, 'binary', 0.03, 0.5) # 转化为glb output_glb_file = get_resource_path('sf_dlm', 'static/file', 'out.glb') util_path = get_resource_path('sf_dlm', 'static/util') cmd = 'python %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) item.model_file = base64_data class ResMrpBom(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 } logging.info('bom_create_line_has-vals:%s' % vals) 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 != False: subcontract = self.get_supplier(product.materials_type_id) bom_id.subcontractor_id = subcontract.partner_id.id logging.info('bom_create-vals:%s' % bom_id) return bom_id # 胚料BOM组件:选取当前胚料原材料, # 然后根据当前的胚料的体积得出需要的原材料重量(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤) # 胚料所需原材料公式:当前的胚料的体积(立方米m³) *材料密度 * 1000 = 所需原材料重量KG(公斤) def bom_create_line(self, embryo): # 选取当前胚料原材料 bom_line = self.get_raw_bom(embryo) vals = { 'bom_id': self.id, 'product_id': bom_line.id, 'product_tmpl_id': bom_line.product_tmpl_id.id, 'product_qty': bom_line.volume * bom_line.materials_type_id.density * 1000, 'product_uom_id': bom_line.uom_id.id } logging.info('bom_create_line-vals1:%s' % vals) return self.env['mrp.bom.line'].create(vals) # 查询材料型号默认排第一的供应商 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') logging.info('get_supplier-vals:%s' % seller_id) return seller_id # 匹配bom def get_bom(self, product): logging.info('get_bom-product:%s' % product) logging.info('get_bom-product:%s' % product.materials_type_id.id) 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) ], 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 class ResProductCategory(models.Model): _inherit = "product.category" type = fields.Selection( [("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")], default="", string="类型") # @api.constrains('type') # def _check_type(self): # category = self.env['product.category'].search( # [('type', '=', self.type)]) # if category: # raise ValidationError("该类别已存在,请选择其他类别")