Compare commits

..

9 Commits

10 changed files with 221 additions and 255 deletions

View File

@@ -62,7 +62,7 @@ patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
const dom1 = buttonsDom.children('.o_form_button_save') const dom1 = buttonsDom.children('.o_form_button_save')
const dom2 = buttonsDom.children('.o_form_button_cancel') const dom2 = buttonsDom.children('.o_form_button_cancel')
dom1.append('保存') dom1.append('保存')
dom2.append('保存') dom2.append('放弃保存')
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)

View File

@@ -596,6 +596,9 @@ class Manufacturing_Connect(http.Controller):
if panel_workorder: if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'}) panel_workorder.write({'production_line_state': '已下产线'})
workorder.write({'state': 'to be detected'}) workorder.write({'state': 'to be detected'})
workorder.check_ids.filtered(
lambda ch: ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
else: else:
res = {'Succeed': False, 'ErrorCode': 204, res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']} 'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}

View File

@@ -1,27 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio
import base64 import base64
import cProfile
import concurrent
import datetime import datetime
import io
import logging import logging
import json import json
import os import os
import pstats
import re import re
import threading
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import requests import requests
from itertools import groupby from itertools import groupby
from collections import defaultdict, namedtuple from collections import defaultdict, namedtuple
from odoo import api, fields, models, SUPERUSER_ID, _, tools from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
class MrpProduction(models.Model): class MrpProduction(models.Model):
_inherit = 'mrp.production' _inherit = 'mrp.production'
_description = "制造订单" _description = "制造订单"
@@ -766,21 +759,24 @@ class MrpProduction(models.Model):
# if self.product_id.tracking == 'serial': # if self.product_id.tracking == 'serial':
# self._set_qty_producing() # self._set_qty_producing()
# 重载根据工序生成工单的程序如果产品BOM中没有工序时 # 重载根据工序生成工单的程序如果产品BOM中没有工序时
# 根据产品对应的模板类型中工序,去生成工单; # 根据产品对应的模板类型中工序,去生成工单;
# CNC加工工序的选取规则 # CNC加工工序的选取规则
# 如果自动报价有带过来预分配的机床, # 如果自动报价有带过来预分配的机床,
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制; # 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心; # 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
def process_production(self):
def _create_workorder3(self, item):
for production in self:
if not production.bom_id or not production.product_id:
continue
workorders_values = [] workorders_values = []
# production = self.env['mrp.production'].browse(production_id) product_qty = production.product_uom_id._compute_quantity(production.product_qty,
product_qty = self.product_uom_id._compute_quantity(self.product_qty, production.bom_id.product_uom_id)
self.bom_id.product_uom_id) exploded_boms, dummy = production.bom_id.explode(production.product_id,
exploded_boms, dummy = self.bom_id.explode(self.product_id, product_qty / production.bom_id.product_qty,
product_qty / self.bom_id.product_qty, picking_type=production.bom_id.picking_type_id)
picking_type=self.bom_id.picking_type_id)
for bom, bom_data in exploded_boms: for bom, bom_data in exploded_boms:
# If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders. # If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders.
if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[ if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
@@ -791,80 +787,36 @@ class MrpProduction(models.Model):
continue continue
workorders_values += [{ workorders_values += [{
'name': operation.name, 'name': operation.name,
'production_id': self.id, 'production_id': production.id,
'workcenter_id': operation.workcenter_id.id, 'workcenter_id': operation.workcenter_id.id,
'product_uom_id': self.product_uom_id.id, 'product_uom_id': production.product_uom_id.id,
'operation_id': operation.id, 'operation_id': operation.id,
'state': 'pending', 'state': 'pending',
}] }]
if self.product_id.categ_id.type in ['成品', '坯料']: if production.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单 # # 根据工序设计生成工单
technology_design_ids = sorted(self.technology_design_ids, key=lambda x: x.sequence) technology_design_ids = sorted(production.technology_design_ids, key=lambda x: x.sequence)
for route in technology_design_ids: for route in technology_design_ids:
workorder_has = self.env['mrp.workorder'].search( workorder_has = self.env['mrp.workorder'].search(
[('technology_design_id', '=', route.id), ('production_id', '=', self.id)]) [('technology_design_id', '=', route.id), ('production_id', '=', production.id)])
if not workorder_has: if not workorder_has:
if route.route_id.routing_type not in ['表面工艺']: if route.route_id.routing_type not in ['表面工艺']:
workorders_values.append( workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(self, route)) self.env['mrp.workorder'].json_workorder_str(production, route))
else: else:
product_production_process = self.env['product.template'].search( product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)]) [('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
workorders_values.append( workorders_values.append(
self.env[ self.env[
'mrp.workorder']._json_workorder_surface_process_str( 'mrp.workorder']._json_workorder_surface_process_str(
self, route, product_production_process.seller_ids[0].partner_id.id)) production, route, product_production_process.seller_ids[0].partner_id.id))
return workorders_values production.workorder_ids = workorders_values
def _set_workorder_duration_expected(self):
try:
# 在每个线程中创建独立的 ORM 环境
with api.Environment.manage():
new_cr = self.pool.cursor()
self = self.with_env(self.env(cr=new_cr))
# program_ids = self.sudo().env['loyalty.program'].browse(program_ids.ids)
# 使用独立的环境来处理数据库事务
# 在独立的环境中对 workorder 进行操作
production = self.sudo().env['mrp.production'].browse(self.id)
workorders_values = production.process_production()
production.write({'workorder_ids': workorders_values})
for workorder in production.workorder_ids: for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected() workorder.duration_expected = workorder._get_duration_expected()
# 可以进行其他与工作单相关的操作
# workorder_env.write({'...'})
return production
except Exception as e:
logging.error(f"Error processing workorder {workorder.id}: {e}")
# workorders_values = self.process_production(self)
# print('_set_workorder_duration_expected wqio ', self)
# self.write({'workorder_ids': workorders_values})
# for workorder in self.workorder_ids:
# workorder.duration_expected = workorder._get_duration_expected()
def _create_workorder3(self, item):
with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
futures = []
for production in self:
if not production.bom_id or not production.product_id:
continue
# 提交每个生产任务到线程池
futures.append(executor.submit(production._set_workorder_duration_expected))
# 等待所有线程完成任务
results = []
for future in futures:
try:
result = future.result()
if result:
results.append(result)
except Exception as e:
logging.error(f"Error processing production: {e}")
return results
# 外协出入库单处理 # 外协出入库单处理
def get_subcontract_pick_purchase(self,productions): def get_subcontract_pick_purchase(self):
production_all = productions.sorted(lambda x: x.id) production_all = self.sorted(lambda x: x.id)
product_id_to_production_names = {} product_id_to_production_names = {}
grouped_product_ids = {k: list(g) for k, g in grouped_product_ids = {k: list(g) for k, g in
groupby(production_all, key=lambda x: x.product_id.id)} groupby(production_all, key=lambda x: x.product_id.id)}
@@ -873,10 +825,9 @@ class MrpProduction(models.Model):
sorted_workorders = None sorted_workorders = None
for production in production_all: for production in production_all:
proc_workorders = [] proc_workorders = []
process_parameter_workorder=production.workorder_ids.filtered(lambda w: w.surface_technics_parameters_id and w.is_subcontract and w.state!='cancel') process_parameter_workorder = self.env['mrp.workorder'].search(
# process_parameter_workorder = self.env['mrp.workorder'].search( [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
# [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
# ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
if process_parameter_workorder: if process_parameter_workorder:
# 将这些特殊表面工艺工单的采购单与调拨单置为失效 # 将这些特殊表面工艺工单的采购单与调拨单置为失效
for workorder in process_parameter_workorder: for workorder in process_parameter_workorder:
@@ -991,7 +942,11 @@ class MrpProduction(models.Model):
if purchase_order_line: if purchase_order_line:
line.unlink() line.unlink()
def _process_reset_work_order_sequence(self,rec): def _reset_work_order_sequence(self):
"""
工单工序排序方法(新)
"""
for rec in self:
workorder_ids = rec.workorder_ids workorder_ids = rec.workorder_ids
technology_design_ids = rec.technology_design_ids technology_design_ids = rec.technology_design_ids
if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')): if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')):
@@ -1028,12 +983,7 @@ class MrpProduction(models.Model):
key=lambda w: w.sequence).sequence key=lambda w: w.sequence).sequence
for cw in cancel_work_ids: for cw in cancel_work_ids:
cw.sequence = sequence + 1 cw.sequence = sequence + 1
def _reset_work_order_sequence(self,productions):
"""
工单工序排序方法(新)
"""
for rec in productions:
self._process_reset_work_order_sequence(rec)
def _reset_work_order_sequence_1(self): def _reset_work_order_sequence_1(self):
""" """
工单工序排序方法(旧) 工单工序排序方法(旧)
@@ -1129,9 +1079,9 @@ class MrpProduction(models.Model):
# 创建工单并进行排序 # 创建工单并进行排序
def _create_workorder(self, item): def _create_workorder(self, item):
productions = self._create_workorder3(item) self._create_workorder3(item)
self._reset_work_order_sequence(productions) self._reset_work_order_sequence()
return productions return True
def production_process(self, pro_plan): def production_process(self, pro_plan):
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False} type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
@@ -1724,13 +1674,13 @@ class MrpProduction(models.Model):
url = '/api/intelligent_programming/reset_state_again' url = '/api/intelligent_programming/reset_state_again'
config_url = configsettings['sf_url'] + url config_url = configsettings['sf_url'] + url
ret = requests.post(config_url, json=res, data=None, headers=config_header) ret = requests.post(config_url, json=res, data=None, headers=config_header)
ret = ret.json() # ret = ret.json()
result = json.loads(ret['result']) # result = json.loads(ret['result'])
logging.info('update_programming_state-ret:%s' % result) # logging.info('update_programming_state-ret:%s' % result)
if result['status'] == 1: # if result['status'] == 1:
self.write({'is_rework': True}) # self.write({'is_rework': True})
else: # else:
raise UserError(ret['message']) # raise UserError(ret['message'])
except Exception as e: except Exception as e:
logging.info('update_programming_state error:%s' % e) logging.info('update_programming_state error:%s' % e)
raise UserError("更新编程单状态失败,请联系管理员") raise UserError("更新编程单状态失败,请联系管理员")
@@ -1806,5 +1756,3 @@ class sf_processing_panel(models.Model):
name = fields.Char('加工面') name = fields.Char('加工面')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)

View File

@@ -1543,7 +1543,8 @@ class ResMrpWorkOrder(models.Model):
# 修改工单状态 # 修改工单状态
self.write({'state': 'to be detected'}) self.write({'state': 'to be detected'})
# 若关联的【质量检查_需送检】=true则质量检查单的状态从“等待”更新为“待处理” # 若关联的【质量检查_需送检】=true则质量检查单的状态从“等待”更新为“待处理”
self.check_ids.filtered(lambda ch: ch.is_inspect is True).write({'quality_state': 'none'}) self.check_ids.filtered(lambda ch: ch.is_inspect is True and ch.quality_state == 'waiting').write(
{'quality_state': 'none'})
class CNCprocessing(models.Model): class CNCprocessing(models.Model):

View File

@@ -74,7 +74,9 @@
<xpath expr="//field[@name='production_real_duration']" position="attributes"> <xpath expr="//field[@name='production_real_duration']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>
</xpath> </xpath>
<xpath expr="//field[@name='state']" position="after">
<field name="programming_state" optional="hide"/>
</xpath>
</field> </field>
</record> </record>

View File

@@ -182,7 +182,7 @@
<button name="button_pending" type="object" string="暂停" class="btn-warning" <button name="button_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/> attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工" <button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/> attrs="{'invisible': ['|', '|', '|',('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False),'&amp;','&amp;',('state', 'in', ('progress')), ('is_inspect', '=', True), ('routing_type','!=','CNC加工')]}"/>
<button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞" <button name="%(mrp.act_mrp_block_workcenter_wo)d" type="action" string="阻塞"
context="{'default_workcenter_id': workcenter_id}" class="btn-danger" context="{'default_workcenter_id': workcenter_id}" class="btn-danger"
@@ -533,7 +533,7 @@
<page string="后置三元检测" attrs='{"invisible": [("individuation_page_PTD", "=", False)]}'> <page string="后置三元检测" attrs='{"invisible": [("individuation_page_PTD", "=", False)]}'>
<group> <group>
<field name="test_results" <field name="test_results"
attrs='{"readonly":[("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True)], attrs='{"readonly":["&amp;","|",("state","!=","to be detected"), "|",("routing_type","=","CNC加工"),("is_inspect", "=", True),("state","in",["done","rework"])],
"invisible":[("results","!=",False)]}'/> "invisible":[("results","!=",False)]}'/>
<!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>--> <!-- <field name="is_remanufacture" attrs='{"invisible":[("test_results","!=","报废")]}'/>-->
<!-- <field name="is_fetchcnc"--> <!-- <field name="is_fetchcnc"-->

View File

@@ -1,9 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details. # Part of YiZuo. See LICENSE file for full copyright and licensing details.
import cProfile import logging
import io from itertools import groupby
import pstats
from concurrent.futures import ThreadPoolExecutor
from odoo import models, api, fields, _ from odoo import models, api, fields, _
@@ -15,12 +13,17 @@ class ProductionTechnologyWizard(models.TransientModel):
origin = fields.Char(string='源单据') origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=True) is_technology_confirm = fields.Boolean(default=True)
def _process_production(self,productions,technology_designs): def confirm(self):
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
# technology_designs = self.production_id.technology_design_ids
productions = self.env['mrp.production'].search(domain)
for production in productions: for production in productions:
# self._process_production_special_design(production,technology_designs)
with ThreadPoolExecutor(max_workers=4) as executor:
executor.submit(self._process_production_special_design, production,technology_designs)
def _process_production_special_design(self,production,technology_designs):
if production != self.production_id: if production != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production) production)
@@ -35,8 +38,7 @@ class ProductionTechnologyWizard(models.TransientModel):
# 工单采购单外协出入库单皆需取消 # 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)] domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id: if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')]
('state', '!=', 'cancel')]
else: else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')] domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain) workorder = self.env['mrp.workorder'].search(domain)
@@ -58,8 +60,7 @@ class ProductionTechnologyWizard(models.TransientModel):
else: else:
if special.production_id.workorder_ids: if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search( workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), [('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')])
('state', '!=', 'cancel')])
if not workorder: if not workorder:
if special.route_id.routing_type == '表面工艺': if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search( product_production_process = self.env['product.template'].search(
@@ -80,34 +81,14 @@ class ProductionTechnologyWizard(models.TransientModel):
else: else:
if workorder.blocked_by_workorder_ids: if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0] workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
def confirm(self): productions._create_workorder(False)
if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']:
domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.env['sf.technology.design'].sudo().search(
[('production_id', '=', self.production_id.id), ('active', 'in', [True, False])])
# technology_designs = self.production_id.technology_design_ids
productions = self.env['mrp.production'].search(domain)
pr = cProfile.Profile()
pr.enable()
self._process_production(productions,technology_designs)
productions = productions._create_workorder(False)
if self.production_id.product_id.categ_id.type == '成品': if self.production_id.product_id.categ_id.type == '成品':
self.production_id.get_subcontract_pick_purchase(productions) productions.get_subcontract_pick_purchase()
productions.is_adjust = False
for item in productions: for item in productions:
item.is_adjust = False
workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted( workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
key=lambda a: a.sequence) key=lambda a: a.sequence)
if workorder[0].state in ['pending']: if workorder[0].state in ['pending']:
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程': if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorder[0].state = 'waiting' workorder[0].state = 'waiting'
pr.disable() # 停止性能分析
# 将结果输出到 StringIO
s = io.StringIO()
ps = pstats.Stats(pr, stream=s)
ps.strip_dirs().sort_stats('cumulative').print_stats()
analysis_output = s.getvalue()
return productions return productions

View File

@@ -48,7 +48,7 @@ class QualityCheck(models.Model):
@api.depends('point_id.is_inspect') @api.depends('point_id.is_inspect')
def _compute_quality_state(self): def _compute_quality_state(self):
for qc in self: for qc in self:
if qc.point_id.is_inspect and qc.quality_state == 'none': if qc.point_id.is_inspect and qc.quality_state == 'none' and qc.workorder_id.state != 'to be detected':
qc.quality_state = 'waiting' qc.quality_state = 'waiting'
elif not qc.point_id.is_inspect and qc.quality_state == 'waiting': elif not qc.point_id.is_inspect and qc.quality_state == 'waiting':
qc.quality_state = 'none' qc.quality_state = 'none'
@@ -62,7 +62,9 @@ class QualityCheck(models.Model):
def do_pass(self): def do_pass(self):
self.ensure_one() self.ensure_one()
super().do_pass() super().do_pass()
if self.workorder_id and self.individuation_page_PTD is True: if self.workorder_id:
if self.workorder_id.state in ('pending', 'waiting'):
raise ValidationError('工单未就绪!')
# 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】 # 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】
if self.test_results in ['返工', '报废']: if self.test_results in ['返工', '报废']:
raise ValidationError('请重新选择【判定结果】-【检测结果】') raise ValidationError('请重新选择【判定结果】-【检测结果】')
@@ -74,7 +76,9 @@ class QualityCheck(models.Model):
def do_fail(self): def do_fail(self):
self.ensure_one() self.ensure_one()
super().do_fail() super().do_fail()
if self.workorder_id and self.individuation_page_PTD is True: if self.workorder_id:
if self.workorder_id.state in ('pending', 'waiting'):
raise ValidationError('工单未就绪!')
# 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】 # 1将页签“判定结果”的检测结果值同步到【工单_后置三元检测_检测结果】
if not self.test_results: if not self.test_results:
raise ValidationError('请填写【判定结果】里的信息') raise ValidationError('请填写【判定结果】里的信息')

View File

@@ -9,22 +9,24 @@
<field name="production_id" invisible="1"/> <field name="production_id" invisible="1"/>
<field name="work_state" invisible="1"/> <field name="work_state" invisible="1"/>
<field name="individuation_page_PTD" invisible="1"/> <field name="individuation_page_PTD" invisible="1"/>
<field name="production_line_id" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> <field name="production_line_id" attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="equipment_id" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', 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), ('individuation_page_PTD', '=', False)]}"/> attrs="{'invisible': ['|',('model_file', '=', 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), ('individuation_page_PTD', '=', False)]}"/> <field name="processing_panel" attrs="{'invisible': [('production_id', '=', False)]}"/>
<!-- <field name="production_id" string="制造订单" readonly="1"-->
<!-- attrs="{'invisible': [('production_id', '=', False)]}"/>-->
<field name="workorder_id" string="工单号" readonly="1" <field name="workorder_id" string="工单号" readonly="1"
attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> attrs="{'invisible': [('production_id', '=', False)]}"/>
<field name="is_inspect" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"/> <field name="is_inspect" attrs="{'invisible': [('production_id', '=', False)]}"/>
</xpath> </xpath>
<xpath expr="//page[@name='notes']" position="before"> <xpath expr="//page[@name='notes']" position="before">
<page string="检测报告" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="检测报告" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="detection_report" string="" widget="pdf_viewer"/> <field name="detection_report" string="" widget="pdf_viewer"/>
</page> </page>
<page string="判定结果" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="判定结果" attrs="{'invisible': [('production_id', '=', False)]}">
<group> <group>
<group> <group>
<field name="test_results" attrs="{'readonly': [('quality_state','in', ['pass', 'fail'])]}"/> <field name="test_results" attrs="{'readonly': [('quality_state','in', ['pass', 'fail'])]}"/>
@@ -35,25 +37,33 @@
</group> </group>
</group> </group>
</page> </page>
<page string="2D图纸" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="2D图纸" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="machining_drawings" string="" widget="adaptive_viewer"/> <field name="machining_drawings" string="" widget="adaptive_viewer"/>
</page> </page>
<page string="客户质量标准" attrs="{'invisible': ['|',('production_id', '=', False), ('individuation_page_PTD', '=', False)]}"> <page string="客户质量标准" attrs="{'invisible': [('production_id', '=', False)]}">
<field name="quality_standard" string="" widget="adaptive_viewer"/> <field name="quality_standard" string="" widget="adaptive_viewer"/>
</page> </page>
<page string="其他" <page string="其他"
attrs="{'invisible': ['|','|', ('quality_state', 'not in', ['pass', 'fail']), ('production_id', '=', False),('individuation_page_PTD', '=', False)]}"> attrs="{'invisible': ['|',('quality_state', 'not in', ['pass', 'fail']), ('production_id', '=', False)]}">
<group> <group>
<field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/> <field name="write_uid" widget='many2one_avatar_user' string="判定人" readonly="1"/>
<field name="write_date" string="判定时间" readonly="1"/> <field name="write_date" string="判定时间" readonly="1"/>
</group> </group>
</page> </page>
</xpath> </xpath>
<xpath expr="//header//button[@name='do_pass'][1]" position="attributes">
<attribute name="string">合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_pass'][2]" position="attributes"> <xpath expr="//header//button[@name='do_pass'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'fail'),('work_state','in', ('done', 'rework'))]}</attribute> <attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'fail'),('work_state','in', ('done', 'rework'))]}</attribute>
<attribute name="string">合格</attribute>
</xpath>
<xpath expr="//header//button[@name='do_fail'][1]" position="attributes">
<attribute name="string">不合格</attribute>
</xpath> </xpath>
<xpath expr="//header//button[@name='do_fail'][2]" position="attributes"> <xpath expr="//header//button[@name='do_fail'][2]" position="attributes">
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework'))]}</attribute> <attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework'))]}</attribute>
<attribute name="string">不合格</attribute>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -130,7 +130,10 @@ class ReSaleOrder(models.Model):
'order_id': self.id, 'order_id': self.id,
'product_id': product.id, 'product_id': product.id,
'name': '%s/%s/%s/%s/%s/%s' % ( 'name': '%s/%s/%s/%s/%s/%s' % (
product.model_long, product.model_width, product.model_height, product.model_volume, self.format_float(product.model_long),
self.format_float(product.model_width),
self.format_float(product.model_height),
self.format_float(product.model_volume),
machining_accuracy_name, machining_accuracy_name,
product.materials_id.name), product.materials_id.name),
'price_unit': product.list_price, 'price_unit': product.list_price,
@@ -143,6 +146,20 @@ class ReSaleOrder(models.Model):
} }
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)
def format_float(self, value):
# 将浮点数转换为字符串
value_str = str(value)
# 检查小数点的位置
if '.' in value_str:
# 获取小数部分
decimal_part = value_str.split('.')[1]
# 判断小数位数是否超过2位
if len(decimal_part) > 2:
# 超过2位则保留2位小数
return "{:.2f}".format(value)
# 否则保持原来的位数
return float(value_str)
@api.constrains('order_line') @api.constrains('order_line')
def check_order_line(self): def check_order_line(self):
for item in self: for item in self: