Accept Merge Request #1974: (feature/tool_standard_library_process -> develop)

Merge Request: 子制造订单零件图号与零件名称获取方式修改

Created By: @廖丹龙
Reviewed By: @胡尧
Approved By: @胡尧 
Accepted By: @廖丹龙
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1974
This commit is contained in:
廖丹龙
2025-03-26 17:24:40 +08:00
committed by Coding
27 changed files with 204 additions and 59 deletions

View File

@@ -9,5 +9,6 @@ class MrpBom(models.Model):
# 成品的供应商从模板中获取 # 成品的供应商从模板中获取
if product_type == 'product': if product_type == 'product':
bom_id.subcontractor_id = product.product_tmpl_id.seller_ids.partner_id.id if product.product_tmpl_id.seller_ids:
bom_id.subcontractor_id = product.product_tmpl_id.seller_ids[-1].partner_id.id
return bom_id return bom_id

View File

@@ -32,6 +32,7 @@ class FixtureModel(models.Model):
multi_mounting_type_id = fields.Many2one('sf.multi_mounting.type', string="联装类型") multi_mounting_type_id = fields.Many2one('sf.multi_mounting.type', string="联装类型")
brand_id = fields.Many2one('sf.machine.brand', string="品牌") brand_id = fields.Many2one('sf.machine.brand', string="品牌")
model_file = fields.Binary(string="图片") model_file = fields.Binary(string="图片")
glb_url = fields.Char(string="图片")
status = fields.Boolean('状态') status = fields.Boolean('状态')
active = fields.Boolean('有效', default=False) active = fields.Boolean('有效', default=False)

View File

@@ -158,6 +158,8 @@
<!-- <field name="upload_model_file" widget="many2many_binary"/>--> <!-- <field name="upload_model_file" widget="many2many_binary"/>-->
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1" <field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('model_file', '=', False)]}"/> attrs="{'invisible': [('model_file', '=', False)]}"/>
<field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('glb_url', '=', False)]}"/>
</group> </group>
</group> </group>
<notebook> <notebook>

View File

@@ -29,7 +29,7 @@ class Sf_Bf_Connect(http.Controller):
bfm_process_order_list = json.loads(kw['bfm_process_order_list']) bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create( order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'], company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
kw['delivery_end_date'], kw['payments_way'], kw['pay_way']) kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], model_display_version=kw.get('model_display_version'))
i = 1 i = 1
# 给sale_order的default_code字段赋值 # 给sale_order的default_code字段赋值
aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)]) aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -44,7 +44,7 @@ class ResProductTemplate(models.Model):
else: else:
return self.env.ref('sf_dlm.product_uom_cubic_millimeter') return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
# model_file = fields.Binary('模型文件') model_file = fields.Binary('模型文件')
# 胚料的库存路线设置 # 胚料的库存路线设置
# def _get_routes(self, route_type): # def _get_routes(self, route_type):

View File

@@ -16,15 +16,21 @@
<field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/> <field name='categ_id' class="custom_required" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name='is_bfm' invisible="1"/> <field name='is_bfm' invisible="1"/>
<field name='categ_type' invisible="1"/> <field name='categ_type' invisible="1"/>
<field name='glb_url' invisible="1"/>
<field name='part_name' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/> <field name='part_name' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/> <field name='part_number' attrs="{'invisible': [('categ_type', '!=', '成品')]}"/>
<field name='manual_quotation' attrs="{'invisible':[('upload_model_file', '=', [])]}"/> <field name='manual_quotation' attrs="{'invisible':[('glb_url', '=', False)]}"/>
<field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" /> <field name="is_customer_provided" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
<field name="upload_model_file" <field name="model_name" invisible="1"/>
widget="many2many_binary" <field name="upload_model_file" widget="many2many_binary" attrs="{'invisible': [('upload_model_file', '=', False)]}"/>
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/> <field name="model_url"
widget="binary_download"
filename_field="model_name"
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('model_url', '=', False)]}"/>
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1" <field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': ['|','|', ('categ_type', '!=', '成品'),('categ_type', '=', False),('model_file', '=', False)]}"/> attrs="{'invisible': ['|','|', ('categ_type', '!=', '成品'),('categ_type', '=', False),('model_file', '=', False)]}"/>
<field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': ['|','|', ('categ_type', '!=', '成品'),('categ_type', '=', False),('glb_url', '=', False)]}"/>
<field name='cutting_tool_type' invisible="1"/> <field name='cutting_tool_type' invisible="1"/>
<field name="fixture_material_type" invisible="1"/> <field name="fixture_material_type" invisible="1"/>
<field name="embryo_model_type_id" string="模型类型" options="{'no_create': True}" <field name="embryo_model_type_id" string="模型类型" options="{'no_create': True}"
@@ -68,6 +74,7 @@
</field> </field>
<xpath expr="//field[@name='uom_id']" position="before"> <xpath expr="//field[@name='uom_id']" position="before">
<field name="is_manual_processing" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" /> <field name="is_manual_processing" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
<field name="auto_machining" attrs="{'invisible': [('categ_type', 'not in', ['成品', '坯料'])], 'readonly': True}" />
</xpath> </xpath>
<xpath expr="//label[@for='volume']" position="before"> <xpath expr="//label[@for='volume']" position="before">
<label for="length" string="尺寸" <label for="length" string="尺寸"

View File

@@ -27,7 +27,8 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
bfm_process_order_list = json.loads(kw['bfm_process_order_list']) bfm_process_order_list = json.loads(kw['bfm_process_order_list'])
order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create( order_id = request.env['sale.order'].with_user(request.env.ref("base.user_admin")).sale_order_create(
company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'], company_id, kw['delivery_name'], kw['delivery_telephone'], kw['delivery_address'],
kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], kw['order_number'], state='draft') kw['delivery_end_date'], kw['payments_way'], kw['pay_way'], kw['order_number'], state='draft',
model_display_version=kw.get('model_display_version'))
i = 1 i = 1
# 给sale_order的default_code字段赋值 # 给sale_order的default_code字段赋值
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)]) # aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -5,6 +5,8 @@ import logging
import json import json
import os import os
import re import re
import traceback
import requests import requests
from itertools import groupby from itertools import groupby
from collections import defaultdict, namedtuple from collections import defaultdict, namedtuple
@@ -25,6 +27,7 @@ class MrpProduction(models.Model):
maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests") maintenance_count = fields.Integer(compute='_compute_maintenance_count', string="Number of maintenance requests")
request_ids = fields.One2many('maintenance.request', 'production_id') request_ids = fields.One2many('maintenance.request', 'production_id')
model_file = fields.Binary('模型文件', related='product_id.model_file') model_file = fields.Binary('模型文件', related='product_id.model_file')
glb_url = fields.Char('模型文件', related='product_id.glb_url')
schedule_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')], schedule_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
string='排程状态', default='未排') string='排程状态', default='未排')
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')], work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
@@ -256,14 +259,46 @@ class MrpProduction(models.Model):
], string='工序状态', default='待装夹') ], string='工序状态', default='待装夹')
# 零件图号 # 零件图号
part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True) part_number = fields.Char('零件图号', compute='_compute_part_info', store=True)
# 上传零件图纸 # 上传零件图纸
part_drawing = fields.Binary('零件图纸', related='product_id.machining_drawings', readonly=True) part_drawing = fields.Binary('零件图纸', related='product_id.machining_drawings', readonly=True)
quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True) quality_standard = fields.Binary('质检标准', related='product_id.quality_standard', readonly=True)
part_name = fields.Char(string='零件名称', related='product_id.part_name', readonly=True) part_name = fields.Char(string='零件名称', compute='_compute_part_info', store=True)
@api.depends('product_id')
def _compute_part_info(self):
try:
for production_id in self:
if production_id.product_id.categ_id.type == '成品':
production_id.part_number = production_id.product_id.part_number
production_id.part_name = production_id.product_id.part_name
elif production_id.product_id.categ_id.type == '坯料':
product_name = ''
match = re.search(r'(S\d{5}-\d)', production_id.product_id.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
if production_id.picking_id.sale_order_id:
sale_order = production_id.picking_id.sale_order_id
else:
sale_order_name = ''
match = re.search(r'(S\d+)', production_id.product_id.name)
if match:
sale_order_name = match.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
filtered_order_line = sale_order.order_line.filtered(
lambda production: re.search(f'{product_name}$', production.product_id.name)
)
if filtered_order_line:
production_id.part_number = filtered_order_line.part_number
production_id.part_name = filtered_order_line.part_name
except Exception as e:
traceback_error = traceback.format_exc()
logging.error("制造订单零件图号 零件名称获取失败:%s" % traceback_error)
# 判断制造的产品类型 # 判断制造的产品类型
production_product_type = fields.Selection([ production_product_type = fields.Selection([
@@ -720,14 +755,16 @@ class MrpProduction(models.Model):
'model_order_no': cnc.product_id.default_code, 'model_order_no': cnc.product_id.default_code,
'user': cnc.env.user.name, 'user': cnc.env.user.name,
'programme_way': programme_way, 'programme_way': programme_way,
'model_file': '' if not cnc.product_id.model_file else base64.b64encode( # 'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
cnc.product_id.model_file).decode('utf-8'), # cnc.product_id.model_file).decode('utf-8'),
# 'glb_url': cnc.product_id.glb_url,
'part_name': cnc.product_id.part_name, 'part_name': cnc.product_id.part_name,
'part_number': cnc.product_id.part_number, 'part_number': cnc.product_id.part_number,
'machining_drawings': base64.b64encode(cnc.product_id.machining_drawings).decode( 'machining_drawings': base64.b64encode(cnc.product_id.machining_drawings).decode(
'utf-8') if cnc.product_id.machining_drawings else '', 'utf-8') if cnc.product_id.machining_drawings else '',
'machining_drawings_name': cnc.product_id.machining_drawings_name, 'machining_drawings_name': cnc.product_id.machining_drawings_name,
'machining_drawings_mimetype': cnc.product_id.machining_drawings_mimetype, 'machining_drawings_mimetype': cnc.product_id.machining_drawings_mimetype,
# 'model_id': cnc.product_id.model_id,
} }
# 打印出除了 model_file 之外的所有键值对 # 打印出除了 model_file 之外的所有键值对
for key, value in res.items(): for key, value in res.items():

View File

@@ -289,6 +289,7 @@ class ResMrpWorkOrder(models.Model):
cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序") cmm_ids = fields.One2many("sf.cmm.program", 'workorder_id', string="CMM程序")
tray_code = fields.Char(string="托盘编码") tray_code = fields.Char(string="托盘编码")
glb_file = fields.Binary("glb模型文件", related='production_id.model_file') glb_file = fields.Binary("glb模型文件", related='production_id.model_file')
glb_url = fields.Char("glb模型文件", related='production_id.glb_url')
is_subcontract = fields.Boolean(string='是否外协') is_subcontract = fields.Boolean(string='是否外协')
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数") surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")

View File

@@ -30,6 +30,7 @@ class ResProductMo(models.Model):
model_width = fields.Float('模型宽(mm)', digits=(16, 3)) model_width = fields.Float('模型宽(mm)', digits=(16, 3))
model_height = fields.Float('模型高(mm)', digits=(16, 3)) model_height = fields.Float('模型高(mm)', digits=(16, 3))
model_volume = fields.Float('模型体积(m³)') model_volume = fields.Float('模型体积(m³)')
model_area = fields.Float('模型表面积(m²)')
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度') model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度')
model_processing_panel = fields.Char('模型加工面板') model_processing_panel = fields.Char('模型加工面板')
model_remark = fields.Char('模型备注说明') model_remark = fields.Char('模型备注说明')
@@ -777,11 +778,40 @@ class ResProductMo(models.Model):
manual_quotation = fields.Boolean('人工编程', default=False, readonly=True) manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
machining_drawings = fields.Binary('2D加工图纸', readonly=True) machining_drawings = fields.Binary('2D加工图纸', readonly=True)
quality_standard = fields.Binary('质检标准', readonly=True) quality_standard = fields.Binary('质检标准', readonly=True)
part_name = fields.Char(string='零件名称', readonly=True) part_name = fields.Char(string='零件名称', compute='_compute_related_product', readonly=True, store=True)
part_number = fields.Char(string='零件图号', readonly=True) part_number = fields.Char(string='零件图号', compute='_compute_related_product', readonly=True, store=True)
machining_drawings_name = fields.Char(string='零件图号名称', readonly=True) machining_drawings_name = fields.Char(string='零件图号名称', readonly=True)
machining_drawings_mimetype = fields.Char(string='零件图号类型', readonly=True) machining_drawings_mimetype = fields.Char(string='零件图号类型', readonly=True)
model_url = fields.Char('模型文件地址')
glb_url = fields.Char('glb文件地址')
area = fields.Float('表面积(m²)')
auto_machining = fields.Boolean('自动化加工(模型识别)', default=False)
model_id = fields.Char('模型id')
@api.depends('name')
def _compute_related_product(self):
for record in self:
if record.categ_id.name == '坯料':
product_name = ''
match = re.search(r'(S\d{5}-\d)', record.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
sale_order_name = ''
match_sale = re.search(r'S(\d+)', record.name)
if match_sale:
sale_order_name = match_sale.group(0)
sale_order = self.env['sale.order'].sudo().search(
[('name', '=', sale_order_name)])
if sale_order:
filtered_order_line = sale_order.order_line.filtered(
lambda order_line: re.search(f'{product_name}$', order_line.product_id.name)
)
record.part_number = filtered_order_line.product_id.part_number if filtered_order_line else None
record.part_name = filtered_order_line.product_id.part_name if filtered_order_line else None
@api.constrains('tool_length') @api.constrains('tool_length')
def _check_tool_length_size(self): def _check_tool_length_size(self):
if self.tool_length > 1000000: if self.tool_length > 1000000:
@@ -852,7 +882,7 @@ class ResProductMo(models.Model):
copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy() copy_product_id = product_id.with_user(self.env.ref("base.user_admin")).copy()
copy_product_id.product_tmpl_id.active = True copy_product_id.product_tmpl_id.active = True
model_type = self.env['sf.model.type'].search([], limit=1) model_type = self.env['sf.model.type'].search([], limit=1)
attachment = self.attachment_create(item['model_name'], item['model_data']) # attachment = self.attachment_create(item['model_name'], item['model_data'])
# 获取坯料冗余配置 # 获取坯料冗余配置
if not item.get('embryo_redundancy'): if not item.get('embryo_redundancy'):
embryo_redundancy_id = model_type.embryo_tolerance_id embryo_redundancy_id = model_type.embryo_tolerance_id
@@ -875,10 +905,14 @@ class ResProductMo(models.Model):
'length': item['model_long'], 'length': item['model_long'],
'width': item['model_width'], 'width': item['model_width'],
'height': item['model_height'], 'height': item['model_height'],
'volume': item['model_long'] * item['model_width'] * item['model_height'], 'volume': item['model_volume'],
'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']), 'area': item['model_area'],
'model_name': attachment.name if attachment else None, # 'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
'upload_model_file': [(6, 0, [attachment.id])] if attachment else None, 'model_url': item['model_url'],
'glb_url': item['glb_url'],
'model_name': item['model_name'],
'auto_machining': item['auto_machining'],
# 'upload_model_file': [(6, 0, [attachment.id])] if attachment else None,
'list_price': item['price'], 'list_price': item['price'],
'materials_id': self.env['sf.production.materials'].search( 'materials_id': self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])]).id, [('materials_no', '=', item['texture_code'])]).id,
@@ -898,6 +932,7 @@ class ResProductMo(models.Model):
'part_name': item.get('part_name') or '', 'part_name': item.get('part_name') or '',
'machining_drawings_name': item.get('machining_drawings_name') or '', 'machining_drawings_name': item.get('machining_drawings_name') or '',
'machining_drawings_mimetype': item.get('machining_drawings_mimetype') or '', 'machining_drawings_mimetype': item.get('machining_drawings_mimetype') or '',
'model_id': item['model_id'],
} }
tax_id = self.env['account.tax'].sudo().search( tax_id = self.env['account.tax'].sudo().search(
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')]) [('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
@@ -980,15 +1015,14 @@ class ResProductMo(models.Model):
vals = { vals = {
'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R', 'name': '%s-%s-%s [%s %s-%s * %s * %s]' % ('R',
order_id.name, i, materials_id.name, materials_type_id.name, order_id.name, i, materials_id.name, materials_type_id.name,
item['model_long'] + embryo_redundancy_id.long, self.format_float(item['model_long'] + embryo_redundancy_id.long),
item['model_width'] + embryo_redundancy_id.width, self.format_float(item['model_width'] + embryo_redundancy_id.width),
item['model_height'] + embryo_redundancy_id.height), self.format_float(item['model_height'] + embryo_redundancy_id.height)),
'length': item['model_long'] + embryo_redundancy_id.long, 'length': self.format_float(item['model_long'] + embryo_redundancy_id.long),
'width': item['model_width'] + embryo_redundancy_id.width, 'width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
'height': item['model_height'] + embryo_redundancy_id.height, 'height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
'volume': (item['model_long'] + embryo_redundancy_id.long) * ( 'volume': self.format_float(item['blank_volume']),
item['model_width'] + embryo_redundancy_id.width) * ( 'area': self.format_float(item['blank_area']),
item['model_height'] + embryo_redundancy_id.height),
'embryo_model_type_id': model_type.id, 'embryo_model_type_id': model_type.id,
'list_price': item['price'], 'list_price': item['price'],
'materials_id': materials_id.id, 'materials_id': materials_id.id,
@@ -1081,6 +1115,9 @@ class ResProductMo(models.Model):
base64_data = base64.b64encode(image_data) base64_data = base64.b64encode(image_data)
return base64_data return base64_data
# 增加产品表面积
class ResProductFixture(models.Model): class ResProductFixture(models.Model):
_inherit = 'product.template' _inherit = 'product.template'
@@ -1093,6 +1130,7 @@ class ResProductFixture(models.Model):
fixture_material_type = fields.Char(string="夹具物料类型", related='fixture_material_id.name') fixture_material_type = fields.Char(string="夹具物料类型", related='fixture_material_id.name')
multi_mounting_type_id = fields.Many2one('sf.multi_mounting.type', string="联装类型") multi_mounting_type_id = fields.Many2one('sf.multi_mounting.type', string="联装类型")
model_file = fields.Binary(string="3D模型图") model_file = fields.Binary(string="3D模型图")
glb_url = fields.Char(string="3D模型图")
# 夹具物料基本参数 # 夹具物料基本参数
diameter = fields.Float('直径(mm)', digits=(16, 2)) diameter = fields.Float('直径(mm)', digits=(16, 2))

View File

@@ -59,7 +59,7 @@ class QuickEasyOrder(models.Model):
product_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_default').sudo().with_context(active_test=False).product_variant_id product_id = self.env.ref('jikimo_sale_multiple_supply_methods.product_template_default').sudo().with_context(active_test=False).product_variant_id
# user_id = request.env.ref('base.user_admin').sudo() # user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX', order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝', state='draft') str(datetime.now()), '现结', '支付宝', state='draft', model_display_version='v2')
order_id.default_code = obj.name order_id.default_code = obj.name
i = 1 i = 1
for item in res['bfm_process_order_list']: for item in res['bfm_process_order_list']:

View File

@@ -1,5 +1,7 @@
import logging import logging
import json import json
import re
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from odoo.exceptions import UserError from odoo.exceptions import UserError
@@ -23,6 +25,9 @@ class SaleOrder(models.Model):
def confirm_to_supply_method(self): def confirm_to_supply_method(self):
self.state = 'supply method' self.state = 'supply method'
for line in self.order_line:
if line.product_id.auto_machining:
line.supply_method = 'automation'
def action_confirm(self): def action_confirm(self):
if self._get_forbidden_state_confirm() & set(self.mapped('state')): if self._get_forbidden_state_confirm() & set(self.mapped('state')):
@@ -57,18 +62,25 @@ class SaleOrder(models.Model):
order_id = self order_id = self
product = line.product_id product = line.product_id
# 拼接方法需要的item结构 # 拼接方法需要的item结构,成品的模型数据信息就是坯料的数据信息
item = { item = {
'texture_code': product.materials_id.materials_no, 'texture_code': product.materials_id.materials_no,
'texture_type_code': product.materials_type_id.materials_no, 'texture_type_code': product.materials_type_id.materials_no,
'model_long': product.length, 'model_long': product.length,
'model_width': product.width, 'model_width': product.width,
'model_height': product.height, 'model_height': product.height,
'blank_volume': product.model_volume,
'blank_area': product.model_area,
'price': product.list_price, 'price': product.list_price,
'embryo_redundancy_id': line.embryo_redundancy_id, 'embryo_redundancy_id': line.embryo_redundancy_id,
} }
product_name = ''
match = re.search(r'(S\d{5}-\d)', product.name)
# 如果匹配成功,提取结果
if match:
product_name = match.group(0)
# 获取成品名结尾-n的n # 获取成品名结尾-n的n
product_seria = int(product.name.split('-')[-1]) product_seria = int(product_name.split('-')[-1])
# 成品供货方式为采购则不生成bom # 成品供货方式为采购则不生成bom
if line.supply_method != 'purchase': if line.supply_method != 'purchase':
bom_data = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).get_bom(product) bom_data = self.env['mrp.bom'].with_user(self.env.ref("base.user_admin")).get_bom(product)
@@ -151,7 +163,7 @@ class SaleOrder(models.Model):
'purchase', 'purchase',
product_seria, product_seria,
product) product)
if purchase_embryo == -3: if purchase_embryo and purchase_embryo == -3:
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配') raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
else: else:
# 产品配置bom # 产品配置bom
@@ -198,3 +210,14 @@ class SaleOrderLine(models.Model):
if vals['supply_method'] == 'purchase' and line.is_incoming_material: if vals['supply_method'] == 'purchase' and line.is_incoming_material:
raise UserError('当前(%s)产品为客供料,不能选择外购' % ','.join(line.mapped('product_id.name'))) raise UserError('当前(%s)产品为客供料,不能选择外购' % ','.join(line.mapped('product_id.name')))
return super(SaleOrderLine, self).write(vals) return super(SaleOrderLine, self).write(vals)
cancel_auto_machining = fields.Boolean('是否取消自动化加工', compute='_compute_cancel_auto_machining', store=True)
cancel_auto_machining_reason = fields.Char('更改供货原因')
@api.depends('product_id', 'supply_method')
def _compute_cancel_auto_machining(self):
for line in self:
line.cancel_auto_machining = True if line.product_id.auto_machining \
and line.supply_method != 'automation' else False

View File

@@ -314,11 +314,13 @@ class StockRule(models.Model):
production_item.product_id.product_model_type_id.id)] production_item.product_id.product_model_type_id.id)]
product_routing_workcenter = self.env[model].search(domain, order='sequence asc') product_routing_workcenter = self.env[model].search(domain, order='sequence asc')
if production_item.production_type == '自动化产线加工': if production_item.production_type == '自动化产线加工':
for k in (production_item.product_id.model_processing_panel.split(',')): for k in ((production_item.product_id.model_processing_panel or "").split(',')):
for route in product_routing_workcenter: if k.strip():
i += 1 for route in product_routing_workcenter:
technology_design_values.append( i += 1
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False)) technology_design_values.append(
self.env['sf.technology.design'].json_technology_design_str(k, route, i, False))
elif production_item.production_type == '人工线下加工': elif production_item.production_type == '人工线下加工':
for route in product_routing_workcenter: for route in product_routing_workcenter:
i += 1 i += 1
@@ -799,7 +801,7 @@ class ReStockMove(models.Model):
continue continue
logging.info('制造订单的调拨单 %s', move.origin) logging.info('制造订单的调拨单 %s', move.origin)
production_id = self.env['mrp.production'].sudo().search( production_id = self.env['mrp.production'].sudo().search(
[('name', '=', move.origin)], limit=1) [('name', '=', move.origin.split(',')[0] if move.origin else '')], limit=1)
if not production_id: if not production_id:
continue continue
product_name = '' product_name = ''

View File

@@ -450,7 +450,9 @@
</button> </button>
</div> </div>
<field name="product_id" position="after"> <field name="product_id" position="after">
<field name="model_file" string="产品模型" readonly="1" widget="Viewer3D"/> <field name="model_file" string="产品模型" readonly="1" widget="Viewer3D" attrs="{'invisible': [('model_file', '=', False)]}"/>
<field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('glb_url', '=', False)]}"/>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -251,7 +251,8 @@
<field name="date_planned_finished" invisible="1"/> <field name="date_planned_finished" invisible="1"/>
<field name="duration" widget="mrp_timer" <field name="duration" widget="mrp_timer"
invisible="1" sum="real duration"/> invisible="1" sum="real duration"/>
<field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型"/> <field name="glb_file" readonly="1" widget="Viewer3D" string="加工模型" attrs="{'invisible': [('glb_file', '=', False)]}"/>
<field name="glb_url" readonly="1" widget="Viewer3D" string="加工模型" attrs="{'invisible': [('glb_url', '=', False)]}"/>
<field name="manual_quotation" readonly="1" <field name="manual_quotation" readonly="1"
attrs="{'invisible': [('routing_type', 'not in', ['CNC加工', '人工线下加工'])]}"/> attrs="{'invisible': [('routing_type', 'not in', ['CNC加工', '人工线下加工'])]}"/>
<field name="processing_panel" readonly="1" <field name="processing_panel" readonly="1"

View File

@@ -18,9 +18,13 @@
<xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before"> <xpath expr="//page/field[@name='order_line']/tree/field[@name='remark']" position="before">
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" /> <field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
</xpath> </xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='model_glb_file']" position="before"> <xpath expr="//field[@name='order_line']/tree/field[@name='glb_url']" position="before">
<field name="part_number" optional="show" class="section_and_note_text"/> <field name="part_number" optional="show" class="section_and_note_text"/>
</xpath> </xpath>
<!-- <xpath expr="//field[@name='order_line']/tree/field[@name='remark']" position="before"> -->
<!-- <field name="cancel_auto_machining" invisible="1"/> -->
<!-- <field name="cancel_auto_machining_reason" optional="show" attrs="{'required': [('cancel_auto_machining', '=', True),('state', 'not in', ['draft', 'sent'])]}"/> -->
<!-- </xpath> -->
<!-- <xpath expr="//header/button[@name='action_cancel']" position="attributes"> --> <!-- <xpath expr="//header/button[@name='action_cancel']" position="attributes"> -->
<!-- <attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute> --> <!-- <attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute> -->

View File

@@ -26,6 +26,7 @@ class QualityCheck(models.Model):
string='生产线') string='生产线')
equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备') equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备')
model_file = fields.Binary(related='workorder_id.glb_file', string='加工模型') model_file = fields.Binary(related='workorder_id.glb_file', string='加工模型')
glb_url = fields.Char(related='workorder_id.glb_url', string='加工模型')
detection_report = fields.Binary(related='workorder_id.detection_report', readonly=True, string='检测报告') detection_report = fields.Binary(related='workorder_id.detection_report', readonly=True, string='检测报告')
test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], string="检测结果", test_results = fields.Selection([("合格", "合格"), ("返工", "返工")], string="检测结果",

View File

@@ -12,6 +12,7 @@ class SfQualityCncTest(models.Model):
production_id = fields.Many2one(related='workorder_id.production_id', string='制造订单') production_id = fields.Many2one(related='workorder_id.production_id', string='制造订单')
product_id = fields.Many2one(related='workorder_id.product_id', string='产品') product_id = fields.Many2one(related='workorder_id.product_id', string='产品')
model_file = fields.Binary(related='workorder_id.glb_file', string='加工模型') model_file = fields.Binary(related='workorder_id.glb_file', string='加工模型')
glb_url = fields.Char(related='workorder_id.glb_url', string='加工模型')
processing_panel = fields.Char(related='workorder_id.processing_panel', string='加工面') processing_panel = fields.Char(related='workorder_id.processing_panel', string='加工面')
equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备') equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备')
production_line_id = fields.Many2one(related='workorder_id.production_line_id', production_line_id = fields.Many2one(related='workorder_id.production_line_id',

View File

@@ -13,6 +13,8 @@
<field name="equipment_id" attrs="{'invisible': [('production_id', '=', False)]}"/> <field name="equipment_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1" <field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': ['|',('model_file', '=', False), ('production_id', '=', False)]}"/> attrs="{'invisible': ['|',('model_file', '=', False), ('production_id', '=', False)]}"/>
<field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': ['|',('glb_url', '=', False), ('production_id', '=', False)]}"/>
</xpath> </xpath>
<xpath expr="//field[@name='partner_id']" position="after"> <xpath expr="//field[@name='partner_id']" position="after">
<field name="processing_panel" attrs="{'invisible': [('production_id', '=', False)]}"/> <field name="processing_panel" attrs="{'invisible': [('production_id', '=', False)]}"/>

View File

@@ -87,7 +87,8 @@
<field name="product_id"/> <field name="product_id"/>
<field name="production_line_id"/> <field name="production_line_id"/>
<field name="equipment_id"/> <field name="equipment_id"/>
<field name="model_file" widget="Viewer3D"/> <field name="model_file" widget="Viewer3D" attrs="{'invisible': [('model_file', '=', False)]}"/>
<field name="glb_url" widget="Viewer3D" attrs="{'invisible': [('glb_url', '=', False)]}"/>
</group> </group>
<group> <group>
<field name="part_name"/> <field name="part_name"/>

View File

@@ -314,7 +314,7 @@ class QuickEasyOrder(models.Model):
company_id = self.env.ref('base.main_company').sudo() company_id = self.env.ref('base.main_company').sudo()
# user_id = request.env.ref('base.user_admin').sudo() # user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX', order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝') str(datetime.now()), '现结', '支付宝', 'v2')
i = 1 i = 1
# 给sale_order的default_code字段赋值 # 给sale_order的default_code字段赋值
aa = self.env['sale.order'].sudo().search([('name', '=', order_id.name)]) aa = self.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -237,7 +237,7 @@ class QuickEasyOrder(models.Model):
company_id = self.env.ref('base.main_company').sudo() company_id = self.env.ref('base.main_company').sudo()
# user_id = request.env.ref('base.user_admin').sudo() # user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX', order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝') str(datetime.now()), '现结', '支付宝', 'v2')
i = 1 i = 1
# 给sale_order的default_code字段赋值 # 给sale_order的default_code字段赋值
aa = self.env['sale.order'].sudo().search([('name', '=', order_id.name)]) aa = self.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -61,9 +61,12 @@ class ReSaleOrder(models.Model):
order_code = fields.Char('平台订单号', readonly=True) order_code = fields.Char('平台订单号', readonly=True)
model_display_version = fields.Char('模型展示版本', default="v1")
# 业务平台分配工厂后在智能工厂先创建销售订单 # 业务平台分配工厂后在智能工厂先创建销售订单
def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address, def sale_order_create(self, company_id, delivery_name, delivery_telephone, delivery_address,
deadline_of_delivery, payments_way, pay_way, order_number, state='sale'): deadline_of_delivery, payments_way, pay_way, order_number, state='sale',
model_display_version='v1'):
now_time = datetime.datetime.now() now_time = datetime.datetime.now()
partner = self.get_customer() partner = self.get_customer()
data = { data = {
@@ -80,6 +83,7 @@ class ReSaleOrder(models.Model):
'payments_way': payments_way, 'payments_way': payments_way,
'pay_way': pay_way, 'pay_way': pay_way,
'order_code': order_number, 'order_code': order_number,
'model_display_version': model_display_version,
} }
if deadline_of_delivery: if deadline_of_delivery:
# deadline_of_delivery字段存在为false字符串情况 # deadline_of_delivery字段存在为false字符串情况
@@ -138,11 +142,14 @@ class ReSaleOrder(models.Model):
product.materials_id.name), product.materials_id.name),
'price_unit': product.list_price, 'price_unit': product.list_price,
'product_uom_qty': item['number'], 'product_uom_qty': item['number'],
'model_glb_file': base64.b64decode(item['model_file']) if item['model_file'] else None, # 'model_glb_file': base64.b64decode(item['model_file']) if item['model_file'] else None,
'model_url': item['model_url'],
'glb_url': item['glb_url'],
'remark': item.get('remark'), 'remark': item.get('remark'),
'embryo_redundancy_id': item.get('embryo_redundancy_id'), 'embryo_redundancy_id': item.get('embryo_redundancy_id'),
'is_incoming_material': True if item.get('embryo_redundancy_id') else False, 'is_incoming_material': True if item.get('embryo_redundancy_id') else False,
'manual_quotation': item.get('manual_quotation') 'manual_quotation': item.get('manual_quotation'),
'model_id': item['model_id'],
} }
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals) return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
@@ -245,6 +252,7 @@ class ResaleOrderLine(models.Model):
# part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True) # part_number = fields.Char('零件图号', related='product_id.part_number', readonly=True)
part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True) part_name = fields.Char('零件名称', related='product_id.part_name', readonly=True)
model_glb_file = fields.Binary('模型的glb文件', compute='_compute_model_glb_file', store=True) model_glb_file = fields.Binary('模型的glb文件', compute='_compute_model_glb_file', store=True)
glb_url = fields.Char('glb文件地址', compute='_compute_model_glb_file', store=True)
# product_template_id = fields.Many2one( # product_template_id = fields.Many2one(
# string="产品", # string="产品",
# comodel_name='product.template', # comodel_name='product.template',
@@ -261,6 +269,8 @@ class ResaleOrderLine(models.Model):
is_incoming_material = fields.Boolean('客供料', compute='_compute_is_incoming_material', store=True) is_incoming_material = fields.Boolean('客供料', compute='_compute_is_incoming_material', store=True)
embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余') embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余')
manual_quotation = fields.Boolean('人工编程', default=False) manual_quotation = fields.Boolean('人工编程', default=False)
model_url = fields.Char('模型文件地址')
model_id = fields.Char('模型id')
@api.depends('embryo_redundancy_id') @api.depends('embryo_redundancy_id')
def _compute_is_incoming_material(self): def _compute_is_incoming_material(self):
@@ -273,6 +283,8 @@ class ResaleOrderLine(models.Model):
if line.product_template_id: if line.product_template_id:
if not line.model_glb_file: if not line.model_glb_file:
line.model_glb_file = line.product_id.product_tmpl_id.model_file line.model_glb_file = line.product_id.product_tmpl_id.model_file
if not line.glb_url:
line.glb_url = line.product_id.product_tmpl_id.glb_url
if not line.price_unit: if not line.price_unit:
line.price_unit = line.product_id.product_tmpl_id.list_price line.price_unit = line.product_id.product_tmpl_id.list_price

View File

@@ -50,9 +50,13 @@
<group> <group>
<field name="customer_id" context="{'is_customer': True }" <field name="customer_id" context="{'is_customer': True }"
options="{'no_create': True}" required="1"/> options="{'no_create': True}" required="1"/>
<field name="upload_model_file" widget="many2many_binary"/> <field name="upload_model_file" widget="many2many_binary" attrs="{'invisible': [('upload_model_file', '=', False)]}"/>
<!-- <field name="model_url" widget="binary_download" filename_field="model_name" readonly="1" string="模型文件"
attrs="{'invisible': [('model_url', '=', False)]}"/> -->
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1" <field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('model_file', '=', False)]}"/> attrs="{'invisible': [('model_file', '=', False)]}"/>
<!-- <field name="glb_url" widget="Viewer3D" string="模型" readonly="1" force_save="1"
attrs="{'invisible': [('glb_url', '=', False)]}"/> -->
<label for="model_length" string="尺寸(mm)" <label for="model_length" string="尺寸(mm)"
attrs='{"invisible": [("model_file","=",False)]}'/> attrs='{"invisible": [("model_file","=",False)]}'/>
<div class="test_model" <div class="test_model"

View File

@@ -98,12 +98,15 @@
<field name="deadline_of_delivery" readonly="0"/> <field name="deadline_of_delivery" readonly="0"/>
<field name="payments_way" invisible="1"/> <field name="payments_way" invisible="1"/>
<field name="pay_way" invisible="1"/> <field name="pay_way" invisible="1"/>
<field name="model_display_version" invisible="1"/>
<!-- <field name="schedule_status" readonly="1"/> --> <!-- <field name="schedule_status" readonly="1"/> -->
</field> </field>
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="before"> <xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="before">
<field name="model_glb_file" widget="Viewer3D" optional="show" <field name="model_glb_file" widget="Viewer3D" optional="show"
string="模型文件" attrs="{'readonly': [('state', 'in', ['draft'])], 'isInList': True}"/> string="模型文件" readonly="1" attrs="{'column_invisible': [('parent.model_display_version', '!=', 'v1')]}"/>
<field name="part_name" optional="hide"/> <field name="glb_url" widget="Viewer3D" optional="show"
string="模型文件" readonly="1" attrs="{'column_invisible': [('parent.model_display_version', '!=', 'v2')]}"/>
<field name="part_name" optional="show"/>
</xpath> </xpath>
<xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after"> <xpath expr="//field[@name='order_line']/tree/field[@name='price_subtotal']" position="after">
<field name="remark"/> <field name="remark"/>

View File

@@ -94,7 +94,7 @@ class StockPicking(models.Model):
if send_move_ids: if send_move_ids:
for item in send_move_ids: for item in send_move_ids:
val = { val = {
'name': item.product_id.upload_model_file.display_name, 'name': item.product_id.upload_model_file.display_name if item.product_id.upload_model_file else item.product_id.model_name,
'quantity_done': item.quantity_done, 'quantity_done': item.quantity_done,
'date': date_utils.json_default(item.date) if item.date else None, 'date': date_utils.json_default(item.date) if item.date else None,
'description_picking': item.description_picking, 'description_picking': item.description_picking,

View File

@@ -27,7 +27,10 @@ export class StepViewer extends Component {
formatUrl() { formatUrl() {
var url = ''; var url = '';
if (this.props.value) { if (this.props.value) {
if (this.props.value.slice(-1) == 'b' && !isNaN(this.props.value.split(' ')[0])) { if (this.props.value.startsWith('http')) {
// 从url读取文件内容
url = this.props.value;
} else if (this.props.value.slice(-1) == 'b' && !isNaN(this.props.value.split(' ')[0])) {
var url_props = { var url_props = {
base_url: session['web.base.url'], base_url: session['web.base.url'],
model: this.props.record.resModel, model: this.props.record.resModel,
@@ -37,21 +40,19 @@ export class StepViewer extends Component {
url = url_props['base_url'].replace('http://', 'https://') + '/web/content/' + url_props['model'] + '/' + url_props['id'] + '/' + url_props['field'] + '?download=true'; 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' // url = 'http://localhost:8069'+'/web/content/'+url_props['model']+'/'+url_props['id']+'/'+url_props['field']+'?download=true'
// console.log('url111111', url) // console.log('url111111', url)
return url;
} else { } else {
url = "data:model/gltf-binary;base64," + this.props.value; url = "data:model/gltf-binary;base64," + this.props.value;
// console.log('url2', url) // console.log('url2', url)
return url;
// localStorage.setItem('url',url) // localStorage.setItem('url',url)
// let new_url = localStorage.getItem(('url')) // let new_url = localStorage.getItem(('url'))
// var oViewer = document.getElementsByTagName('model-viewer')[0]; // var oViewer = document.getElementsByTagName('model-viewer')[0];
// return new_url // return new_url
// url = "web_widget_model_viewer/static/src/images/not_model.png"; // url = "web_widget_model_viewer/static/src/images/not_model.png";
} }
} else { }
// var oImg = document.getElementsByClassName('test')[0] return url;
// console.log(oImg)
}
} }
} }