Merge branch 'develop' of https://e.coding.net/jikimo-hn/jikimo_sfs/jikimo_sf into feature/制造功能优化

This commit is contained in:
mgw
2025-03-28 14:01:17 +08:00
38 changed files with 271 additions and 78 deletions

View File

@@ -1,4 +1,5 @@
import re
import ast
from odoo import models, fields, api
@@ -20,6 +21,13 @@ class PurchaseRequest(models.Model):
if pr.state != 'draft' and pr.rule_new_add:
pr.rule_new_add = False
def action_view_purchase_order(self):
action = super(PurchaseRequest, self).action_view_purchase_order()
origin_context = ast.literal_eval(action['context'])
if 'search_default_draft' in origin_context:
origin_context.pop('search_default_draft')
action['context'] = origin_context
return action
class PurchaseRequestLine(models.Model):
_inherit = 'purchase.request.line'

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
{
'name': "机企猫 采购审批流程",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "https://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['purchase_request_tier_validation'],
# always loaded
'data': [
],
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,24 @@
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class PurchaseRequest(models.Model):
_inherit = 'purchase.request'
def _validate_tier(self, tiers=False):
res = super(PurchaseRequest, self)._validate_tier(tiers)
# 检查是否所有审批都已通过
all_approved = all(
tier_review.status == 'approved'
for tier_review in self.review_ids
)
if self.review_ids and all_approved: # 确保有审批记录
self.state = 'approved'
return res

View File

@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*-
{
'name': "机企猫 采购审批流程",
'name': "机企猫 采购申请审批流程",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
采购申请审批流程""",
'description': """
Long description of module's purpose
""",
采购申请审批流程""",
'author': "My Company",
'website': "https://www.yourcompany.com",

View File

@@ -9,5 +9,6 @@ class MrpBom(models.Model):
# 成品的供应商从模板中获取
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

View File

@@ -421,3 +421,4 @@ class EmbryoRedundancy(models.Model):
width = fields.Float('宽度(mm)', required=True)
height = fields.Float('高度(mm)', required=True)
active = fields.Boolean('有效', default=True)
remark = fields.Char('描述')

View File

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

View File

@@ -645,6 +645,7 @@
<field name="long"/>
<field name="width"/>
<field name="height"/>
<field name="remark"/>
</tree>
</field>
</record>

View File

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

View File

@@ -29,7 +29,7 @@ class Sf_Bf_Connect(http.Controller):
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(
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
# 给sale_order的default_code字段赋值
aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -44,7 +44,7 @@ class ResProductTemplate(models.Model):
else:
return self.env.ref('sf_dlm.product_uom_cubic_millimeter')
# model_file = fields.Binary('模型文件')
model_file = fields.Binary('模型文件')
# 胚料的库存路线设置
# 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='is_bfm' 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_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="upload_model_file"
widget="many2many_binary"
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('is_bfm','=', True)]}"/>
<field name="model_name" invisible="1"/>
<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"
attrs="{'invisible': ['|', '|',('categ_type', '!=', '成品'),('categ_type', '=', False),('model_url', '=', False)]}"/>
<field name="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
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="fixture_material_type" invisible="1"/>
<field name="embryo_model_type_id" string="模型类型" options="{'no_create': True}"
@@ -68,6 +74,7 @@
</field>
<xpath expr="//field[@name='uom_id']" position="before">
<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 expr="//label[@for='volume']" position="before">
<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'])
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'],
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
# 给sale_order的default_code字段赋值
# aa = request.env['sale.order'].sudo().search([('name', '=', order_id.name)])

View File

@@ -5,6 +5,8 @@ import logging
import json
import os
import re
import traceback
import requests
from itertools import groupby
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")
request_ids = fields.One2many('maintenance.request', 'production_id')
model_file = fields.Binary('模型文件', related='product_id.model_file')
glb_url = fields.Char('模型文件', related='product_id.glb_url')
schedule_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
string='排程状态', default='未排')
work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')],
@@ -256,14 +259,46 @@ class MrpProduction(models.Model):
], 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)
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.sale_order_id:
sale_order = production_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)])
logging.info("product_name is :%s" % product_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([
@@ -720,14 +755,16 @@ class MrpProduction(models.Model):
'model_order_no': cnc.product_id.default_code,
'user': cnc.env.user.name,
'programme_way': programme_way,
'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
cnc.product_id.model_file).decode('utf-8'),
# 'model_file': '' if not cnc.product_id.model_file else base64.b64encode(
# cnc.product_id.model_file).decode('utf-8'),
# 'glb_url': cnc.product_id.glb_url,
'part_name': cnc.product_id.part_name,
'part_number': cnc.product_id.part_number,
'machining_drawings': base64.b64encode(cnc.product_id.machining_drawings).decode(
'utf-8') if cnc.product_id.machining_drawings else '',
'machining_drawings_name': cnc.product_id.machining_drawings_name,
'machining_drawings_mimetype': cnc.product_id.machining_drawings_mimetype,
# 'model_id': cnc.product_id.model_id,
}
# 打印出除了 model_file 之外的所有键值对
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程序")
tray_code = fields.Char(string="托盘编码")
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='是否外协')
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_height = fields.Float('模型高(mm)', digits=(16, 3))
model_volume = fields.Float('模型体积(m³)')
model_area = fields.Float('模型表面积(m²)')
model_machining_precision = fields.Selection(selection=_get_machining_precision, string='加工精度')
model_processing_panel = fields.Char('模型加工面板')
model_remark = fields.Char('模型备注说明')
@@ -782,6 +783,13 @@ class ResProductMo(models.Model):
machining_drawings_name = 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:
@@ -874,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_tmpl_id.active = True
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'):
embryo_redundancy_id = model_type.embryo_tolerance_id
@@ -897,10 +905,14 @@ class ResProductMo(models.Model):
'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 if attachment else None,
'upload_model_file': [(6, 0, [attachment.id])] if attachment else None,
'volume': item['model_volume'],
'area': item['model_area'],
# 'model_file': '' if not item['model_file'] else base64.b64decode(item['model_file']),
'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'],
'materials_id': self.env['sf.production.materials'].search(
[('materials_no', '=', item['texture_code'])]).id,
@@ -920,6 +932,7 @@ class ResProductMo(models.Model):
'part_name': item.get('part_name') or '',
'machining_drawings_name': item.get('machining_drawings_name') or '',
'machining_drawings_mimetype': item.get('machining_drawings_mimetype') or '',
'model_id': item['model_id'],
}
tax_id = self.env['account.tax'].sudo().search(
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
@@ -1002,15 +1015,14 @@ class ResProductMo(models.Model):
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'] + embryo_redundancy_id.long,
item['model_width'] + embryo_redundancy_id.width,
item['model_height'] + embryo_redundancy_id.height),
'length': item['model_long'] + embryo_redundancy_id.long,
'width': item['model_width'] + embryo_redundancy_id.width,
'height': item['model_height'] + embryo_redundancy_id.height,
'volume': (item['model_long'] + embryo_redundancy_id.long) * (
item['model_width'] + embryo_redundancy_id.width) * (
item['model_height'] + embryo_redundancy_id.height),
self.format_float(item['model_long'] + embryo_redundancy_id.long),
self.format_float(item['model_width'] + embryo_redundancy_id.width),
self.format_float(item['model_height'] + embryo_redundancy_id.height)),
'length': self.format_float(item['model_long'] + embryo_redundancy_id.long),
'width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
'height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
'volume': self.format_float(item['blank_volume']),
'area': self.format_float(item['blank_area']),
'embryo_model_type_id': model_type.id,
'list_price': item['price'],
'materials_id': materials_id.id,
@@ -1103,6 +1115,9 @@ class ResProductMo(models.Model):
base64_data = base64.b64encode(image_data)
return base64_data
# 增加产品表面积
class ResProductFixture(models.Model):
_inherit = 'product.template'
@@ -1115,6 +1130,7 @@ class ResProductFixture(models.Model):
fixture_material_type = fields.Char(string="夹具物料类型", related='fixture_material_id.name')
multi_mounting_type_id = fields.Many2one('sf.multi_mounting.type', string="联装类型")
model_file = fields.Binary(string="3D模型图")
glb_url = fields.Char(string="3D模型图")
# 夹具物料基本参数
diameter = fields.Float('直径(mm)', digits=(16, 2))

View File

@@ -59,18 +59,15 @@ class PurchaseOrder(models.Model):
production_id = self.env['mrp.production'].search([('origin', 'in', origins)])
purchase.production_count = len(production_id)
# def button_confirm(self):
# super().button_confirm()
# workorders = self.env['mrp.workorder'].search([('purchase_id', '=', self.id), ('state', '!=', 'cancel')])
# for workorder in workorders:
# if workorder.routing_type == '表面工艺' and workorder.is_subcontract is True:
# move_out = workorder.move_subcontract_workorder_ids[1]
# for mo in move_out:
# if mo.state != 'done':
# mo.write({'state': 'assigned', 'production_id': False})
# if not mo.move_line_ids:
# self.env['stock.move.line'].create(mo.get_move_line(workorder.production_id, workorder))
# return True
def button_confirm(self):
for record in self:
for line in record.order_line:
if line.product_qty <= 0:
raise UserError('请对【产品】中的【数量】进行输入')
if line.price_unit <= 0:
raise UserError('请对【产品】中的【单价】进行输入')
return super(PurchaseOrder, self).button_confirm()
origin_sale_id = fields.Many2one('sale.order', string='销售订单号', store=True, compute='_compute_origin_sale_id')
origin_sale_ids = fields.Many2many('sale.order', string='销售订单号(多个)', store=True,

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
# user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝', state='draft')
str(datetime.now()), '现结', '支付宝', state='draft', model_display_version='v2')
order_id.default_code = obj.name
i = 1
for item in res['bfm_process_order_list']:

View File

@@ -1,5 +1,7 @@
import logging
import json
import re
from odoo import models, fields, api, _
from odoo.exceptions import UserError
@@ -23,6 +25,9 @@ class SaleOrder(models.Model):
def confirm_to_supply_method(self):
self.state = 'supply method'
for line in self.order_line:
if line.product_id.auto_machining:
line.supply_method = 'automation'
def action_confirm(self):
if self._get_forbidden_state_confirm() & set(self.mapped('state')):
@@ -57,18 +62,25 @@ class SaleOrder(models.Model):
order_id = self
product = line.product_id
# 拼接方法需要的item结构
# 拼接方法需要的item结构,成品的模型数据信息就是坯料的数据信息
item = {
'texture_code': product.materials_id.materials_no,
'texture_type_code': product.materials_type_id.materials_no,
'model_long': product.length,
'model_width': product.width,
'model_height': product.height,
'blank_volume': product.model_volume,
'blank_area': product.model_area,
'price': product.list_price,
'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
product_seria = int(product.name.split('-')[-1])
product_seria = int(product_name.split('-')[-1])
# 成品供货方式为采购则不生成bom
if line.supply_method != 'purchase':
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',
product_seria,
product)
if purchase_embryo == -3:
if purchase_embryo and purchase_embryo == -3:
raise UserError('该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配')
else:
# 产品配置bom
@@ -198,3 +210,14 @@ class SaleOrderLine(models.Model):
if vals['supply_method'] == 'purchase' and line.is_incoming_material:
raise UserError('当前(%s)产品为客供料,不能选择外购' % ','.join(line.mapped('product_id.name')))
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

@@ -562,6 +562,18 @@ class StockPicking(models.Model):
sale_order_id = fields.Many2one('sale.order', '销售单号', compute='_compute_move_ids', store=True)
picking_type_sequence_code = fields.Char(related='picking_type_id.sequence_code')
part_numbers = fields.Char(string="零件图号", compute='_compute_part_info', store=True, index=True)
part_names = fields.Char(string="零件名称", compute='_compute_part_info', store=True, index=True)
@api.depends('move_ids_without_package.part_number', 'move_ids_without_package.part_name')
def _compute_part_info(self):
for picking in self:
# 聚合所有关联行的 part_number 和 part_name
part_numbers = picking.move_ids_without_package.mapped('part_number')
part_names = picking.move_ids_without_package.mapped('part_name')
picking.part_numbers = ','.join(filter(None, part_numbers))
picking.part_names = ','.join(filter(None, part_names))
@api.depends('move_ids', 'move_ids.product_id')
def _compute_move_ids(self):
for item in self:
@@ -787,7 +799,7 @@ class ReStockMove(models.Model):
continue
logging.info('制造订单的调拨单 %s', move.origin)
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:
continue
product_name = ''

View File

@@ -450,7 +450,9 @@
</button>
</div>
<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>
</record>

View File

@@ -251,7 +251,8 @@
<field name="date_planned_finished" invisible="1"/>
<field name="duration" widget="mrp_timer"
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"
attrs="{'invisible': [('routing_type', 'not in', ['CNC加工', '人工线下加工'])]}"/>
<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">
<field name="supply_method" attrs="{'invisible': [('state', '=', 'draft')], 'required': [('state', '=', 'supply method')]}" />
</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"/>
</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"> -->
<!-- <attribute name="attrs">{'invisible': [('state', '!=', 'draft')]}</attribute> -->

View File

@@ -68,14 +68,8 @@
context="{'group_by': 'retrospect_ref'}"/>
</xpath>
<xpath expr="//field[@name='picking_type_id']" position="after">
<field name="product_id"
string="零件图号"
filter_domain="[('product_id.part_number', 'ilike', self)]"
/>
<field name="product_id"
string="零件名称"
filter_domain="[('product_id.part_name', 'ilike', self)]"
/>
<field name="part_numbers" string="零件图号" filter_domain="[('part_numbers', 'ilike', self)]"/>
<field name="part_names" string="零件名称" filter_domain="[('part_names', 'ilike', self)]"/>
</xpath>
</field>
</record>
@@ -97,7 +91,8 @@
<attribute name="invisible">True</attribute>
</xpath>
<xpath expr="//form//button[@name='action_assign_serial_show_details']" position="after">
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k" title="Assign Serial Numbers">
<button name="button_update_the_sequence_number" type="object" class="btn-link" data-hotkey="k"
title="Assign Serial Numbers">
<span>更新序列号</span>
</button>
</xpath>

View File

@@ -3214,6 +3214,7 @@ class EmbryoRedundancySync(models.Model):
embryo_redundancy.width = item['width']
embryo_redundancy.height = item['height']
embryo_redundancy.active = item['active']
embryo_redundancy.remark = item['remark']
else:
self.env['sf.embryo.redundancy'].sudo().create({
"name": item['name'],
@@ -3222,4 +3223,5 @@ class EmbryoRedundancySync(models.Model):
"width": item['width'],
"height": item['height'],
"active": item['active'],
"remark": item['remark'],
})

View File

@@ -26,6 +26,7 @@ class QualityCheck(models.Model):
string='生产线')
equipment_id = fields.Many2one(related='workorder_id.equipment_id', 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='检测报告')
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='制造订单')
product_id = fields.Many2one(related='workorder_id.product_id', 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='加工面')
equipment_id = fields.Many2one(related='workorder_id.equipment_id', string='加工设备')
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="model_file" widget="Viewer3D" string="模型" readonly="1" force_save="1"
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 expr="//field[@name='partner_id']" position="after">
<field name="processing_panel" attrs="{'invisible': [('production_id', '=', False)]}"/>

View File

@@ -87,7 +87,8 @@
<field name="product_id"/>
<field name="production_line_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>
<field name="part_name"/>

View File

@@ -314,7 +314,7 @@ class QuickEasyOrder(models.Model):
company_id = self.env.ref('base.main_company').sudo()
# user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝')
str(datetime.now()), '现结', '支付宝', 'v2')
i = 1
# 给sale_order的default_code字段赋值
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()
# user_id = request.env.ref('base.user_admin').sudo()
order_id = self.env['sale.order'].sale_order_create(company_id, 'XXXXX', 'XXXXX', 'XXXXX',
str(datetime.now()), '现结', '支付宝')
str(datetime.now()), '现结', '支付宝', 'v2')
i = 1
# 给sale_order的default_code字段赋值
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)
model_display_version = fields.Char('模型展示版本', default="v1")
# 业务平台分配工厂后在智能工厂先创建销售订单
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()
partner = self.get_customer()
data = {
@@ -80,6 +83,7 @@ class ReSaleOrder(models.Model):
'payments_way': payments_way,
'pay_way': pay_way,
'order_code': order_number,
'model_display_version': model_display_version,
}
if deadline_of_delivery:
# deadline_of_delivery字段存在为false字符串情况
@@ -138,11 +142,14 @@ 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']) 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'),
'embryo_redundancy_id': item.get('embryo_redundancy_id'),
'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)
@@ -245,6 +252,7 @@ class ResaleOrderLine(models.Model):
# part_number = fields.Char('零件图号', related='product_id.part_number', 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)
glb_url = fields.Char('glb文件地址', compute='_compute_model_glb_file', store=True)
# product_template_id = fields.Many2one(
# string="产品",
# comodel_name='product.template',
@@ -261,6 +269,8 @@ class ResaleOrderLine(models.Model):
is_incoming_material = fields.Boolean('客供料', compute='_compute_is_incoming_material', store=True)
embryo_redundancy_id = fields.Many2one('sf.embryo.redundancy', '坯料冗余')
manual_quotation = fields.Boolean('人工编程', default=False)
model_url = fields.Char('模型文件地址')
model_id = fields.Char('模型id')
@api.depends('embryo_redundancy_id')
def _compute_is_incoming_material(self):
@@ -273,6 +283,8 @@ class ResaleOrderLine(models.Model):
if line.product_template_id:
if not line.model_glb_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:
line.price_unit = line.product_id.product_tmpl_id.list_price

View File

@@ -50,9 +50,13 @@
<group>
<field name="customer_id" context="{'is_customer': True }"
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"
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)"
attrs='{"invisible": [("model_file","=",False)]}'/>
<div class="test_model"

View File

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

View File

@@ -94,7 +94,7 @@ class StockPicking(models.Model):
if send_move_ids:
for item in send_move_ids:
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,
'date': date_utils.json_default(item.date) if item.date else None,
'description_picking': item.description_picking,

View File

@@ -27,7 +27,10 @@ export class StepViewer extends Component {
formatUrl() {
var url = '';
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 = {
base_url: session['web.base.url'],
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 = '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('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";
}
} else {
// var oImg = document.getElementsByClassName('test')[0]
// console.log(oImg)
}
}
return url;
}
}