157 lines
7.3 KiB
Python
157 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
||
from collections import Counter
|
||
from xml import etree
|
||
|
||
from odoo import models, fields, api, Command
|
||
from odoo.exceptions import UserError, AccessError
|
||
from odoo.http import request
|
||
|
||
|
||
class jikimo_bom(models.Model):
|
||
_name = 'jikimo.bom'
|
||
_description = '功能刀具物料清单'
|
||
tool_inventory_id = fields.Many2one('sf.tool.inventory', '功能刀具清单')
|
||
tool_name = fields.Char(related="tool_inventory_id.name", string='功能刀具名称')
|
||
functional_cutting_tool_model_id = fields.Many2one(related='tool_inventory_id.functional_cutting_tool_model_id',
|
||
string='功能刀具类型')
|
||
tool_groups_id = fields.Many2one(related='tool_inventory_id.tool_groups_id', string='刀具组')
|
||
tool_length = fields.Float(related='tool_inventory_id.tool_length', string='刀具总长(mm)')
|
||
diameter = fields.Float(related='tool_inventory_id.diameter', string='直径(mm)')
|
||
angle = fields.Float(related='tool_inventory_id.angle', string='R角(mm)')
|
||
extension = fields.Float(related='tool_inventory_id.extension', string='伸出长度(mm)')
|
||
product_ids = fields.Many2many('product.product', string='产品')
|
||
knife_handle_model = fields.Selection(related='tool_inventory_id.knife_handle_model', string='使用刀柄型号')
|
||
options = fields.Char('产品清单')
|
||
|
||
def name_get(self):
|
||
result = []
|
||
for bom in self:
|
||
result.append((bom.id, '功能刀具物料清单'))
|
||
return result
|
||
|
||
def check_types_in_list(self):
|
||
"""
|
||
检查产品列表中的元素是否包含了所有指定的类型,并且每种类型至少出现一次。
|
||
:return: 如果条件满足返回True,否则返回False
|
||
"""
|
||
if not self.product_ids:
|
||
return False
|
||
try:
|
||
# 统计每个类型的出现次数
|
||
type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
|
||
|
||
# 检查是否每种类型的出现次数都大于0,并且类型的数量与选项字符串中的数量相等
|
||
return all(count > 0 for count in type_counts.values()) and len(type_counts) == len(self.options.split('+'))
|
||
except AttributeError:
|
||
# 如果出现属性错误,说明产品列表中的元素可能缺少必要的属性
|
||
return False
|
||
# type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
|
||
# return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
|
||
|
||
def write(self, vals):
|
||
# 在更新模型时记录旧的 Many2many ID 列表
|
||
if 'product_ids' in vals and not self.env.context.get('is_assembly_options'):
|
||
old_product_counter = Counter(self.product_ids.ids)
|
||
super(jikimo_bom, self).write(vals)
|
||
new_product_counter = Counter(self.product_ids.ids)
|
||
delete_product_counter = old_product_counter - new_product_counter
|
||
if delete_product_counter:
|
||
# 删除操作
|
||
if self.check_types_in_list():
|
||
return True
|
||
else:
|
||
raise UserError('每种物料最少要有一个')
|
||
return True
|
||
return super(jikimo_bom, self).write(vals)
|
||
|
||
def bom_product_domains(self, assembly_options):
|
||
"""
|
||
根据装配选项生成产品域列表
|
||
:param assembly_options: 装配选项字符串,各选项以'+'分隔
|
||
:return: 动态生成的产品搜索条件
|
||
"""
|
||
self.options = assembly_options
|
||
cutting_tool_materials = self.env['sf.cutting.tool.material'].search(
|
||
[('name', 'in', assembly_options.split('+'))])
|
||
domains = []
|
||
for index, option in enumerate(cutting_tool_materials):
|
||
domain = ['&', ('cutting_tool_material_id', '=', option.id),
|
||
("cutting_tool_type_id", "in",
|
||
self.tool_inventory_id.functional_cutting_tool_model_id.cutting_tool_type_ids.ids)]
|
||
if option.name == '刀柄':
|
||
domain = ['&'] + domain + [
|
||
("cutting_tool_taper_shank_model", "=", self.tool_inventory_id.knife_handle_model)]
|
||
|
||
if option.name == '整体式刀具':
|
||
domain = ['&'] + domain + [
|
||
'|',
|
||
# 刀具直径
|
||
('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter),
|
||
|
||
# r角
|
||
('cutting_tool_blade_tip_working_size', '=', self.tool_inventory_id.angle)]
|
||
if option.name == '刀杆':
|
||
domain = ['&'] + domain + [
|
||
("cutting_tool_cutter_arbor_diameter", "=", self.tool_inventory_id.diameter)]
|
||
if option.name == '刀片':
|
||
domain = ['&'] + domain + [
|
||
("cutting_tool_blade_tip_circular_arc_radius", "=", self.tool_inventory_id.angle)]
|
||
if option.name == '刀盘':
|
||
domain = ['&'] + domain + [
|
||
("cutting_tool_cutter_head_diameter", "=", self.tool_inventory_id.diameter)]
|
||
domains = domains + domain
|
||
if index != 0:
|
||
domains = ['|'] + domains
|
||
domains = domains + [('stock_move_ids', '!=',False)]
|
||
return domains
|
||
|
||
def generate_bill_materials(self, assembly_options):
|
||
"""
|
||
生成物料清单
|
||
|
||
根据装配选项生成物料清单,首先获取产品领域,然后搜索相关产品,并设置产品ID。
|
||
|
||
:param assembly_options: 组装方式
|
||
:type assembly_options: 装配选项字符串,各选项以'+'分隔
|
||
"""
|
||
domains = self.bom_product_domains(assembly_options)
|
||
products = self.env['product.product'].search(domains)
|
||
if products:
|
||
new_context = dict(self.env.context)
|
||
new_context['is_assembly_options'] = True
|
||
self.with_context(new_context).write({'product_ids': [Command.set(products.ids)]})
|
||
# self.product_ids = [Command.set(products.ids)]
|
||
|
||
|
||
class jikimo_bom_line(models.Model):
|
||
_name = 'jikimo.bom.line'
|
||
_description = 'jikimo.bom.line'
|
||
|
||
name = fields.Char()
|
||
|
||
|
||
class ProductProduct(models.Model):
|
||
_inherit = 'product.product'
|
||
_order = 'cutting_tool_material_id, cutting_tool_type_id'
|
||
# stock_move_count = fields.Integer(string='stock_move count', compute='_compute_stock_move_count')
|
||
#
|
||
# @api.depends('stock_move_ids')
|
||
# def _compute_stock_move_count(self):
|
||
# for record in self:
|
||
# if record.stock_move_ids:
|
||
# record.stock_move_count = len(record.stock_move_ids)
|
||
# else:
|
||
# record.stock_move_count = 0
|
||
|
||
def search(self, args, offset=0, limit=None, order=None, count=False):
|
||
# 你可以在这里修改 `args` 以调整搜索条件
|
||
# 例如,添加额外的搜索条件
|
||
if self.env.context.get('jikimo_bom_product'):
|
||
bom_id = self.env['jikimo.bom'].browse(request.session.get('jikimo_bom_product').get('bom_id'))
|
||
|
||
if not bom_id.options:
|
||
raise UserError('请先选择组装方式')
|
||
domains = bom_id.bom_product_domains(bom_id.options)
|
||
args = args + domains
|
||
return super(ProductProduct, self).search(args, offset=offset, limit=limit, order=order, count=count)
|