diff --git a/jikimo_sale_multiple_supply_methods/controllers/main.py b/jikimo_sale_multiple_supply_methods/controllers/main.py index ed6ec426..e45265d3 100644 --- a/jikimo_sale_multiple_supply_methods/controllers/main.py +++ b/jikimo_sale_multiple_supply_methods/controllers/main.py @@ -1,5 +1,7 @@ import logging import json +import traceback + from odoo import http from odoo.http import request from odoo.addons.sf_bf_connect.controllers.controllers import Sf_Bf_Connect @@ -44,7 +46,8 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect): i += 1 res['factory_order_no'] = order_id.name except Exception as e: - _logger.info('get_bfm_process_order_list error:%s' % e) + traceback_error = traceback.format_exc() + logging.error('get_bfm_process_order_list error: %s' % traceback_error) res['status'] = -1 res['message'] = '工厂创建销售订单和产品失败,请联系管理员' request.cr.rollback() diff --git a/sf_bf_connect/controllers/controllers.py b/sf_bf_connect/controllers/controllers.py index 90c7e6c5..70a8d68f 100644 --- a/sf_bf_connect/controllers/controllers.py +++ b/sf_bf_connect/controllers/controllers.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import json import logging +import traceback + from odoo import http from odoo.http import request @@ -123,7 +125,8 @@ class Sf_Bf_Connect(http.Controller): res['factory_order_no'] = order_id.name return json.JSONEncoder().encode(res) except Exception as e: - logging.info('get_bfm_process_order_list error:%s' % e) + traceback_error = traceback.format_exc() + logging.error('get_bfm_process_order_list error: %s' % traceback_error) res['status'] = -1 res['message'] = '工厂创建销售订单和产品失败,请联系管理员' request.cr.rollback() diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 315ab908..112a41ff 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -278,7 +278,7 @@ class MrpProduction(models.Model): @api.depends( 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state', - 'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state', 'programming_state') + 'workorder_ids.state', 'product_qty', 'qty_producing', 'schedule_state', 'programming_state', 'is_adjust') def _compute_state(self): for production in self: if not production.state or not production.product_uom_id: @@ -314,11 +314,16 @@ class MrpProduction(models.Model): if not production.workorder_ids or production.is_adjust is True: production.state = 'technology_to_confirmed' else: - production.state = 'confirmed' + if production.is_adjust is True: + production.state = 'technology_to_confirmed' + else: + production.state = 'confirmed' elif production.state == 'pending_cam' and production.schedule_state == '未排': production.state = 'confirmed' elif production.state == 'to_close' and production.schedule_state == '已排': production.state = 'pending_cam' + elif production.state == 'confirmed' and production.is_adjust is True: + production.state = 'technology_to_confirmed' if production.state == 'progress': if all(wo_state not in ('progress', 'done', 'rework', 'scrap') for wo_state in production.workorder_ids.mapped('state')): @@ -383,9 +388,12 @@ class MrpProduction(models.Model): def technology_confirm(self): process_parameters = [] account_moves = [] + parameters_not = [] special_design = self.technology_design_ids.filtered( lambda a: a.routing_tag == 'special' and a.is_auto is False) for special in special_design: + if special.route_id.routing_type == '表面工艺' and not special.process_parameters_id: + parameters_not.append(special.route_id.name) if special.process_parameters_id: product_production_process = self.env['product.template'].search( [('server_product_process_parameters_id', '=', special.process_parameters_id.id)]) @@ -399,6 +407,8 @@ class MrpProduction(models.Model): account_moves.append(purchase.name) if account_moves: raise UserError(_("请联系工厂生产经理对采购订单为%s生成的账单进行取消", ", ".join(account_moves))) + if parameters_not: + raise UserError(_("【工艺设计】-【工序】为%s未选择参数,请选择", ", ".join(parameters_not))) if process_parameters: raise UserError(_("【工艺设计】-【参数】为%s的在【产品】中不存在,请先创建", ", ".join(process_parameters))) # 判断同一个加工面的标准工序的顺序是否依次排序 @@ -1126,6 +1136,14 @@ class MrpProduction(models.Model): cloud_programming = None if self.programming_state in ['已编程']: cloud_programming = self._cron_get_programming_state() + result_ids = self.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理') + work_ids = [] + if result_ids: + for result_id in result_ids: + work_ids.append(self.workorder_ids.filtered( + lambda wk: (wk.name == result_id.routing_type + and wk.processing_panel == result_id.processing_panel + and wk.state == 'done')).id) return { 'name': _('返工'), 'type': 'ir.actions.act_window', @@ -1134,7 +1152,7 @@ class MrpProduction(models.Model): 'target': 'new', 'context': { 'default_production_id': self.id, - 'default_workorder_ids': self.workorder_ids.filtered(lambda wk: wk.state == 'done').ids, + 'default_workorder_ids': work_ids, 'default_reprogramming_num': cloud_programming['reprogramming_num'], 'default_programming_state': cloud_programming['programming_state'], 'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False @@ -1288,6 +1306,7 @@ class MrpProduction(models.Model): raise_user_error=not self.env.context.get('from_orderpoint')) productions = self.env['mrp.production'].sudo().search( [('origin', '=', self.origin)], order='id desc', limit=1) + productions.write({'programming_no': self.programming_no, 'is_remanufacture': True}) move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc') for mo in move: domain = [] @@ -1314,7 +1333,6 @@ class MrpProduction(models.Model): mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id, 'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id, 'production_id': False}) - productions.write({'programming_no': self.programming_no, 'is_remanufacture': True}) # productions.procurement_group_id.mrp_production_ids.move_dest_ids.write( # {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])}) stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)]) @@ -1389,7 +1407,7 @@ class MrpProduction(models.Model): def _compute_production_type(self): for production in self: production.production_type = '自动化产线加工' if not production.product_id.is_manual_processing else '人工线下加工' - + @api.model_create_multi def create(self, vals_list): """ @@ -1405,7 +1423,7 @@ class MrpProduction(models.Model): vals['name'] = self.env['stock.picking.type'].browse(picking_type_id).sequence_id.next_by_id() if not vals.get('procurement_group_id'): product_id = self.env['product.product'].browse(vals['product_id']) - if product_id.product_tmpl_id.single_manufacturing: + if product_id.product_tmpl_id.single_manufacturing: if product_id.id not in product_group_id.keys(): procurement_group_vals = self._prepare_procurement_group_vals(vals) group_id = self.env["procurement.group"].create(procurement_group_vals).id @@ -1415,23 +1433,29 @@ class MrpProduction(models.Model): vals['procurement_group_id'] = product_group_id[product_id.id] return super(MrpProduction, self).create(vals_list) - @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id', 'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id') + @api.depends('procurement_group_id.stock_move_ids.created_purchase_line_id.order_id', + 'procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id') def _compute_purchase_order_count(self): for production in self: # 找到来源的第一张制造订单的采购组 if production.product_id.product_tmpl_id.single_manufacturing == True: - first_production = self.env['mrp.production'].search([('origin', '=', production.origin), ('product_id', '=', production.product_id.id)], limit=1, order='id asc') - production.purchase_order_count = len(first_production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | - first_production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id) + first_production = self.env['mrp.production'].search( + [('origin', '=', production.origin), ('product_id', '=', production.product_id.id)], limit=1, + order='id asc') + production.purchase_order_count = len( + first_production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | + first_production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id) else: - production.purchase_order_count = len(production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | - production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id) + production.purchase_order_count = len( + production.procurement_group_id.stock_move_ids.created_purchase_line_id.order_id | + production.procurement_group_id.stock_move_ids.move_orig_ids.purchase_line_id.order_id) @api.depends('procurement_group_id', 'procurement_group_id.stock_move_ids.group_id') def _compute_picking_ids(self): for order in self: if order.product_id.product_tmpl_id.single_manufacturing == True: - first_order = self.env['mrp.production'].search([('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc') + first_order = self.env['mrp.production'].search( + [('origin', '=', order.origin), ('product_id', '=', order.product_id.id)], limit=1, order='id asc') order.picking_ids = self.env['stock.picking'].search([ ('group_id', '=', first_order.procurement_group_id.id), ('group_id', '!=', False), ]) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 02bfda14..ccb7ed5c 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -281,7 +281,7 @@ class ResMrpWorkOrder(models.Model): # if technology_design.is_auto is False: # domain = [('origin', '=', order.production_id.name)] # else: - domain = [('purchase_type', '=', 'consignment'),('origin', '=', ','.join(production_list))] + domain = [('purchase_type', '=', 'consignment'), ('origin', '=', ','.join(production_list))] purchase = self.env['purchase.order'].search(domain) if not purchase: order.surface_technics_purchase_count = 0 @@ -1062,7 +1062,7 @@ class ResMrpWorkOrder(models.Model): if workorder.production_id.workorder_ids.filtered(lambda wk: wk.sequence == 0): continue # ===== 对所有按序号排序的非[进行中、完成、返工、取消]状态的工单,除了第一条之外的工单状态都设置为[等待其他工单] ===== - logging.info(workorder.state) + # logging.info(workorder.state) work_ids = workorder.production_id.workorder_ids.filtered( lambda wk: wk.state not in ['done', 'rework', 'cancel']) if not work_ids: @@ -1075,6 +1075,13 @@ class ResMrpWorkOrder(models.Model): if workorder.state != 'pending': workorder.state = 'pending' continue + # ================= 如果制造订单制造类型为【人工线下加工】========================== + if (workorder.production_id.production_type == '人工线下加工' + and workorder.production_id.schedule_state == '已排' + and len(workorder.production_id.picking_ids.filtered( + lambda w: w.state not in ['done', 'cancel'])) == 0): + workorder.state = 'ready' + continue # ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]========================== if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework' or workorder.production_id.schedule_state != '已排' @@ -1096,7 +1103,7 @@ class ResMrpWorkOrder(models.Model): # if purchase_order.picking_ids.filtered(lambda p: p.state in ['waiting', 'confirmed', 'assigned']): # workorder.state = 'waiting' # continue - if workorder.routing_type == '表面工艺': + if workorder.technology_design_id.routing_tag == 'special': if workorder.is_subcontract is False: workorder.state = 'ready' else: @@ -1195,11 +1202,12 @@ class ResMrpWorkOrder(models.Model): 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程序,请稍后再试')) + if self.production_id.production_type == '自动化产线加工': + 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': diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index b01cc465..f5f685aa 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -874,8 +874,8 @@ class ResProductMo(models.Model): '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])], + 'model_name': attachment.name if attachment else None, + 'upload_model_file': [(6, 0, [attachment.id])] if attachment else None, 'list_price': item['price'], 'materials_id': self.env['sf.production.materials'].search( [('materials_no', '=', item['texture_code'])]).id, @@ -924,6 +924,8 @@ class ResProductMo(models.Model): return [(6, 0, process_parameters_ids)] def attachment_create(self, name, data): + if not data: + return None attachment = self.env['ir.attachment'].create({ 'datas': base64.b64decode(data), 'type': 'binary', diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 42141da9..031672b8 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -337,8 +337,8 @@ class StockRule(models.Model): product_routing_workcenter = self.env[model].search(domain, order='sequence asc') if production_item.production_type == '自动化产线加工': for k in (production_item.product_id.model_processing_panel.split(',')): - i += 1 for route in product_routing_workcenter: + i += 1 technology_design_values.append( self.env['sf.technology.design'].json_technology_design_str(k, route, i, False)) else: @@ -480,6 +480,8 @@ class ProductionLot(models.Model): [('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', product.name.split('[')[0])], limit=1, order='name desc') + if not last_serial: + return "%s-%03d" % (product.name, 1) return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1] now = datetime.now().strftime("%Y%m%d") if product.cutting_tool_model_id: @@ -973,12 +975,19 @@ class ReStockMove(models.Model): 创建调拨单时,在此新增或修改调拨单的数据 """ res = super(ReStockMove, self)._get_new_picking_values() - if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']: - production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc') - productions = self.env['mrp.production'].search( - [('origin', '=', production.origin), ('product_id', '=', production.product_id.id)]) - res['origin'] = ','.join(productions.mapped('name')) - res['retrospect_ref'] = production.product_id.name + ## 制造订单报废生成的新制造订单不走合并 + production_remanufacture = None + if 'origin' in res: + if self.picking_type_id.name in ['生产发料', '内部调拨']: + production_remanufacture = self.env['mrp.production'].search( + [('name', '=', res['origin']), ('is_remanufacture', '=', True)]) + if not production_remanufacture: + if self[0].origin and self.picking_type_id.name in ['生产发料', '内部调拨']: + production = self.env['mrp.production'].search([('name', '=', self[0].origin)], limit=1, order='id asc') + productions = self.env['mrp.production'].search( + [('origin', '=', production.origin), ('product_id', '=', production.product_id.id)]) + res['origin'] = ','.join(productions.mapped('name')) + res['retrospect_ref'] = production.product_id.name return res diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index f9f4c159..24c38570 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -11,11 +11,11 @@ access_sf_model_type_group_sale_director,sf_model_type_group_sale_director,model access_sf_model_type_group_purchase_director,sf_model_type_group_purchase_director,model_sf_model_type,sf_base.group_purchase_director,1,0,0,0 access_sf_model_type_group_plan_director,sf_model_type_group_plan_director,model_sf_model_type,sf_base.group_plan_director,1,0,0,0 access_sf_product_model_type_routing_sort_group_sf_mrp_user,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0 -access_sf_product_model_type_routing_sort_manager,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0 +access_sf_product_model_type_routing_sort_manager,sf_product_model_type_routing_sort,model_sf_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1 access_sf_embryo_model_type_routing_sort_group_sf_mrp_user,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0 -access_sf_embryo_model_type_routing_sort_manager,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0 +access_sf_embryo_model_type_routing_sort_manager,sf_embryo_model_type_routing_sort,model_sf_embryo_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1 access_sf_surface_technics_model_type_routing_sort,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0 -access_sf_surface_technics_model_type_routing_sort_manager,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0 +access_sf_surface_technics_model_type_routing_sort_manager,sf_surface_technics_model_type_routing_sort,model_sf_surface_technics_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1 access_sf_production_line_group_sf_mrp_user,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_user,1,1,1,0 access_sf_production_line_manager,sf.production.line,model_sf_production_line,sf_base.group_sf_mrp_manager,1,1,1,0 access_maintenance_equipment_tool_group_sf_mrp_user,maintenance_equipment_tool,model_maintenance_equipment_tool,sf_base.group_sf_mrp_user,1,0,0,0 @@ -176,6 +176,6 @@ access_sf_production_technology_re_adjust_wizard_group_sf_mrp_manager,sf_product access_sf_production_technology_re_adjust_wizard_group_production_engineer,sf_production_technology_re_adjust_wizard_group_production_engineer,model_sf_production_technology_re_adjust_wizard,sf_base.group_production_engineer,1,1,1,0 access_sf_manual_product_model_type_routing_sort_group_sf_mrp_user,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_user,1,0,0,0 -access_sf_manual_product_model_type_routing_sort_manager,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,0 +access_sf_manual_product_model_type_routing_sort_manager,sf_manual_product_model_type_routing_sort,model_sf_manual_product_model_type_routing_sort,sf_base.group_sf_mrp_manager,1,1,1,1 access_sf_manual_product_model_type_routing_sort_group_plan_dispatch,sf_manual_product_model_type_routing_sort_group_plan_dispatch,model_sf_manual_product_model_type_routing_sort,sf_base.group_plan_dispatch,1,0,0,0 access_sf_detection_result_manager,sf_detection_result_manager,model_sf_detection_result,,1,1,1,1 diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index 725f5eb8..783f39dd 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -120,7 +120,8 @@ - + @@ -365,7 +366,8 @@ - - + True - + True - + 子MO diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 1faec0ac..53134cc4 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -251,7 +251,7 @@ decoration-danger="tag_type == '重新加工'"/> + attrs="{'invisible': [('rfid_code_old', '!=', False)]}" widget="qrcode_widget"/> diff --git a/sf_manufacturing/wizard/production_technology_re_adjust_wizard.py b/sf_manufacturing/wizard/production_technology_re_adjust_wizard.py index 774dbd74..746c953e 100644 --- a/sf_manufacturing/wizard/production_technology_re_adjust_wizard.py +++ b/sf_manufacturing/wizard/production_technology_re_adjust_wizard.py @@ -14,7 +14,7 @@ class ProductionTechnologyReAdjustWizard(models.TransientModel): def confirm(self): if self.is_technology_re_adjust is True: - domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'), + domain = [('origin', '=', self.origin), ('state', '=', 'confirmed'), ('product_id', '=', self.production_id.product_id.id)] else: domain = [('id', '=', self.production_id.id)] diff --git a/sf_manufacturing/wizard/rework_wizard.py b/sf_manufacturing/wizard/rework_wizard.py index b2c251f7..5a697f31 100644 --- a/sf_manufacturing/wizard/rework_wizard.py +++ b/sf_manufacturing/wizard/rework_wizard.py @@ -51,18 +51,6 @@ class ReworkWizard(models.TransientModel): self.workorder_id.button_finish() else: if self.workorder_ids: - # handle_result = self.production_id.detection_result_ids.filtered( - # lambda dr: dr.handle_result == '待处理') - # if handle_result: - # processing_panels_to_handle = set(handle_item.processing_panel for handle_item in handle_result) - # processing_panels_choice = set(dr_panel.name for dr_panel in self.processing_panel_id) - # # 使用集合的差集操作找出那些待处理结果中有但实际可用加工面中没有的加工面 - # processing_panels_missing = processing_panels_to_handle - processing_panels_choice - # # 存在不一致的加工面 - # if processing_panels_missing: - # processing_panels_str = ','.join(processing_panels_missing) - # raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str) - rework_workorder_ids = self.production_id.workorder_ids.filtered( lambda ap: ap.id in self.workorder_ids.ids) clamp_workorder_ids = None @@ -87,8 +75,9 @@ class ReworkWizard(models.TransientModel): (item.route_id.name == work.name and item.panel and item.panel == work.processing_panel)) if route: - workorders_values.append( - self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0])) + work_list = self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0]) + work_list[2].update({'tag_type': '重新加工'}) + workorders_values.append(work_list) # 创建新工单,并进行返工配置的相关操作 if workorders_values: # 创建新工单、工序排序、完成检测结果单据 diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py index 70482f23..a73b0a35 100644 --- a/sf_sale/models/sale_order.py +++ b/sf_sale/models/sale_order.py @@ -131,7 +131,7 @@ class ReSaleOrder(models.Model): product.materials_id.name), 'price_unit': product.list_price, 'product_uom_qty': item['number'], - 'model_glb_file': base64.b64decode(item['model_file']), + 'model_glb_file': base64.b64decode(item['model_file']) if item['model_file'] else None, 'remark': item.get('remark'), 'embryo_redundancy_id': item.get('embryo_redundancy_id'), 'is_incoming_material': True if item.get('embryo_redundancy_id') else False,