diff --git a/sf_dlm_management/views/product_template_management_view.xml b/sf_dlm_management/views/product_template_management_view.xml
index 2cdfd1a3..caefcb34 100644
--- a/sf_dlm_management/views/product_template_management_view.xml
+++ b/sf_dlm_management/views/product_template_management_view.xml
@@ -1,6 +1,11 @@
+
+
+ {"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
+
+
product.template.form.inherit.sf
product.template
@@ -88,7 +93,8 @@
-
+
-
-
-
-
-
+
+
+
+
+
diff --git a/sf_maintenance/views/equipment_maintenance_standards_views.xml b/sf_maintenance/views/equipment_maintenance_standards_views.xml
index 1389633b..e14acada 100644
--- a/sf_maintenance/views/equipment_maintenance_standards_views.xml
+++ b/sf_maintenance/views/equipment_maintenance_standards_views.xml
@@ -79,6 +79,7 @@
+
diff --git a/sf_manufacturing/views/mrp_workcenter_views.xml b/sf_manufacturing/views/mrp_workcenter_views.xml
index 37ff8af5..5ba5290d 100644
--- a/sf_manufacturing/views/mrp_workcenter_views.xml
+++ b/sf_manufacturing/views/mrp_workcenter_views.xml
@@ -43,7 +43,7 @@
- 台
+ 件
diff --git a/sf_message/__init__.py b/sf_message/__init__.py
new file mode 100644
index 00000000..9a7e03ed
--- /dev/null
+++ b/sf_message/__init__.py
@@ -0,0 +1 @@
+from . import models
\ No newline at end of file
diff --git a/sf_message/__manifest__.py b/sf_message/__manifest__.py
new file mode 100644
index 00000000..3a0f37d8
--- /dev/null
+++ b/sf_message/__manifest__.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': '机企猫智能工厂 消息提醒',
+ 'version': '1.0',
+ 'summary': '智能工厂消息提醒模块',
+ 'sequence': 1,
+ 'description': """
+
+ """,
+ 'category': 'sf',
+ 'website': 'https://www.sf.jikimo.com',
+ 'depends': ['base', 'sf_base'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/sf_message_template_view.xml',
+ ],
+ 'test': [
+ ],
+ 'license': 'LGPL-3',
+ 'installable': True,
+ 'auto_install': False,
+ 'application': False,
+}
diff --git a/sf_message/models/__init__.py b/sf_message/models/__init__.py
new file mode 100644
index 00000000..ec5b1c2f
--- /dev/null
+++ b/sf_message/models/__init__.py
@@ -0,0 +1 @@
+from . import sf_message_template
diff --git a/sf_message/models/sf_message_template.py b/sf_message/models/sf_message_template.py
new file mode 100644
index 00000000..4d89cc2a
--- /dev/null
+++ b/sf_message/models/sf_message_template.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+from odoo import models, fields, api
+
+
+class SfMessageTemplate(models.Model):
+ _name = "sf.message.template"
+ _description = u'消息模板'
+
+ name = fields.Char(string=u"名称", required=True)
+ type = fields.Selection([
+ ('待接单', '待接单'),
+ ('待排程', '待排程'),
+ ('坯料采购', '坯料采购'),
+ ('坯料发料', '坯料发料'),
+ ('待编程', '待编程'),
+ ('调拨入库', '调拨入库'),
+ ('功能刀具组装', '功能刀具组装'),
+ ('功能刀具寿命到期', '功能刀具寿命到期'),
+ ('程序用刀计划异常', '程序用刀计划异常'),
+ ('工单无CNC程序', '工单无CNC程序'),
+ ('生产线无功能刀具', '生产线无功能刀具'),
+ ('工单无定位数据', '工单无定位数据'),
+ ('工单FTP无文件', '工单FTP无文件'),
+ ('工单加工失败', '工单加工失败'),
+ ('设备故障及异常', '设备故障及异常'),
+ ('工单逾期预警', '工单逾期预警'),
+ ('工单已逾期', '工单已逾期'),
+ ('销售订单逾期', '销售订单逾期'),
+ ('销售订单已逾期', '销售订单已逾期'),
+ ('待质量判定', '待质量判定'),
+ ('生产完工待入库', '生产完工待入库'),
+ ('订单发货', '订单发货')
+ ], string='类型', required=True)
+ description = fields.Char(string=u"描述")
+ content = fields.Html(string=u"内容", render_engine='qweb', translate=True, prefetch=True, sanitize=False)
+ msgtype = fields.Selection(
+ [('text', u'文字'), ('markdown', u'Markdown')], u'消息类型',
+ required=True, default='markdown')
+ notification_department_id = fields.Many2one('hr.department', u'通知部门', required=True)
+ notification_employee_ids = fields.Many2many('hr.employee', string=u'员工',
+ domain="[('department_id', '=',notification_department_id)]",
+ required=True)
+ active = fields.Boolean(string=u"是否有效", default=True)
+
+ @api.onchange('notification_department_id')
+ def _clear_employee_ids(self):
+ if self.notification_department_id:
+ self.notification_employee_ids = False
diff --git a/sf_message/security/group_security.xml b/sf_message/security/group_security.xml
new file mode 100644
index 00000000..fdbc3ae5
--- /dev/null
+++ b/sf_message/security/group_security.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_message/security/ir.model.access.csv b/sf_message/security/ir.model.access.csv
new file mode 100644
index 00000000..dbb2d6af
--- /dev/null
+++ b/sf_message/security/ir.model.access.csv
@@ -0,0 +1,15 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_sf_message_template_group_sale_salemanager,sf_message_template,model_sf_message_template,sf_base.group_sale_salemanager,1,1,1,0
+access_sf_message_template_group_purchase,sf_message_template,model_sf_message_template,sf_base.group_purchase,1,1,1,0
+access_sf_message_template_group_sf_stock_user,sf_message_template,model_sf_message_template,sf_base.group_sf_stock_user,1,1,1,0
+access_sf_message_template_group_sf_order_user,sf_message_template,model_sf_message_template,sf_base.group_sf_order_user,1,1,1,0
+access_sf_message_template_group_sf_tool_user,sf_message_template,model_sf_message_template,sf_base.group_sf_tool_user,1,1,1,0
+
+
+
+
+
+
+
+
+
diff --git a/sf_message/views/sf_message_template_view.xml b/sf_message/views/sf_message_template_view.xml
new file mode 100644
index 00000000..ac412589
--- /dev/null
+++ b/sf_message/views/sf_message_template_view.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+ sf.message.template.view.form
+ sf.message.template
+
+
+
+
+
+
+ sf.message.template.view.tree
+ sf.message.template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.message.template.search.view
+ sf.message.template
+
+
+
+
+
+
+
+
+
+
+
+ 消息模板
+ sf.message.template
+ tree,form
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_sale/views/purchase_order_view.xml b/sf_sale/views/purchase_order_view.xml
index a45d8049..445bded6 100644
--- a/sf_sale/views/purchase_order_view.xml
+++ b/sf_sale/views/purchase_order_view.xml
@@ -184,5 +184,10 @@
+
+
+ {"search_default_categ_id":1,"search_default_filter_to_purchase":1, "purchase_product_template": 1}
+
+
\ No newline at end of file
diff --git a/sf_sale/views/quick_easy_order_view.xml b/sf_sale/views/quick_easy_order_view.xml
index 232bb1a3..7d6f6133 100644
--- a/sf_sale/views/quick_easy_order_view.xml
+++ b/sf_sale/views/quick_easy_order_view.xml
@@ -80,7 +80,7 @@
-
+
diff --git a/sf_sale/views/res_partner_view.xml b/sf_sale/views/res_partner_view.xml
index a680bd7c..cd83e950 100644
--- a/sf_sale/views/res_partner_view.xml
+++ b/sf_sale/views/res_partner_view.xml
@@ -100,8 +100,7 @@
+ widget="many2one_avatar_user"/>
{'readonly': [('id','!=', False)]}
diff --git a/sf_sale/views/sale_order_view.xml b/sf_sale/views/sale_order_view.xml
index 534d42e4..506f5b8d 100644
--- a/sf_sale/views/sale_order_view.xml
+++ b/sf_sale/views/sale_order_view.xml
@@ -225,5 +225,10 @@
+
+ {"search_default_categ_id":1,
+ "search_default_filter_to_sell":1,"sale_multi_pricelist_product_template": 1}
+
+
\ No newline at end of file
diff --git a/sf_stock/__manifest__.py b/sf_stock/__manifest__.py
index 40f9113b..1b5f44c4 100644
--- a/sf_stock/__manifest__.py
+++ b/sf_stock/__manifest__.py
@@ -3,7 +3,7 @@
'name': "sf_stock",
'summary': """
- 处理代发货业务""",
+ 处理仓库 -代发货业务""",
'description': """
Long description of module's purpose
@@ -25,6 +25,7 @@
'data': [
# 'security/ir.model.access.csv',
'views/stock_picking.xml',
+ 'views/stock_product_template.xml'
],
# only loaded in demonstration mode
'demo': [
diff --git a/sf_stock/models/stock_picking.py b/sf_stock/models/stock_picking.py
index 82ce7ea5..3a64f78c 100644
--- a/sf_stock/models/stock_picking.py
+++ b/sf_stock/models/stock_picking.py
@@ -13,8 +13,6 @@ _logger = logging.getLogger(__name__)
class StockPicking(models.Model):
_inherit = 'stock.picking'
- cancel_backorder_ids = fields.Boolean(default=False, string='是否取消后置单据')
-
# 重写验证,下发发货到bfm
def button_validate(self):
info = super(StockPicking, self).button_validate()
diff --git a/sf_stock/views/stock_picking.xml b/sf_stock/views/stock_picking.xml
index 7750ca76..5bf4d40e 100644
--- a/sf_stock/views/stock_picking.xml
+++ b/sf_stock/views/stock_picking.xml
@@ -1,3 +1,4 @@
+
diff --git a/sf_stock/views/stock_product_template.xml b/sf_stock/views/stock_product_template.xml
new file mode 100644
index 00000000..2d2a2fbe
--- /dev/null
+++ b/sf_stock/views/stock_product_template.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ {"search_default_categ_id":1,"search_default_consumable": 1, 'default_detailed_type': 'product'}
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_tool_management/controllers/controllers.py b/sf_tool_management/controllers/controllers.py
index 4b0909dc..b3ca0224 100644
--- a/sf_tool_management/controllers/controllers.py
+++ b/sf_tool_management/controllers/controllers.py
@@ -122,7 +122,8 @@ class Manufacturing_Connect(http.Controller):
tool_assembly.write({
'after_assembly_tool_loading_length': float(data_list[1] or "0"), # 高度(总长度)
'after_assembly_functional_tool_diameter': float(data_list[2] or "0") * 2, # 直径
- 'after_assembly_knife_tip_r_angle': float(data_list[3] or "0") # R角
+ 'after_assembly_knife_tip_r_angle': float(data_list[3] or "0"), # R角
+ 'bool_preset_parameter': True
})
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py
index 2876bc8f..76fb20ff 100644
--- a/sf_tool_management/models/base.py
+++ b/sf_tool_management/models/base.py
@@ -349,25 +349,100 @@ class CAMWorkOrderProgramKnifePlan(models.Model):
class FunctionalToolAssembly(models.Model):
_name = 'sf.functional.tool.assembly'
- _inherit = ['mail.thread']
+ _inherit = ['mail.thread', 'barcodes.barcode_events_mixin']
_description = '功能刀具组装'
- _order = 'assemble_status, use_tool_time asc'
+ _order = 'tool_loading_time desc, use_tool_time asc'
+
+ def on_barcode_scanned(self, barcode):
+ """
+ 智能工厂组装单处扫码校验刀具物料
+ """
+ for record in self:
+ tool_assembly_id = self.env['sf.functional.tool.assembly'].browse(self.ids)
+ lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
+ if lot_ids:
+ for lot_id in lot_ids:
+ if lot_id.tool_material_status != '可用':
+ raise ValidationError(f'Rfid为【{barcode}】的刀柄已被占用,请重新扫描!!')
+ if lot_id.product_id == record.handle_product_id:
+ record.handle_code_id = lot_id.id
+ tool_assembly_id.handle_code_id = lot_id.id
+ else:
+ raise ValidationError('刀柄选择错误,请重新确认!!!')
+ else:
+ location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode)])
+ if location:
+ if location == record.integral_freight_barcode_id:
+ tool_assembly_id.integral_verify = True
+ record.integral_verify = True
+ elif location == record.blade_freight_barcode_id:
+ tool_assembly_id.blade_verify = True
+ record.blade_verify = True
+ elif location == record.bar_freight_barcode_id:
+ tool_assembly_id.bar_verify = True
+ record.bar_verify = True
+ elif location == record.pad_freight_barcode_id:
+ tool_assembly_id.pad_verify = True
+ record.pad_verify = True
+ elif location == record.chuck_freight_barcode_id:
+ tool_assembly_id.chuck_verify = True
+ record.chuck_verify = True
+ else:
+ raise ValidationError('刀具选择错误,请重新确认!')
+ else:
+ raise ValidationError(f'扫描为【{barcode}】的货位不存在,请重新扫描!')
@api.depends('functional_tool_name')
def _compute_name(self):
for obj in self:
obj.name = obj.assembly_order_code
- code = fields.Char('功能刀具编码', readonly=True)
rfid = fields.Char('Rfid', readonly=True)
- tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=True)
+ tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', store=True, compute='_compute_inventory_num')
name = fields.Char(string='名称', readonly=True, compute='_compute_name')
assembly_order_code = fields.Char(string='组装单编码', readonly=True)
functional_tool_name_id = fields.Many2one('product.product', string='功能刀具', readonly=True)
- functional_tool_name = fields.Char(string='功能刀具名称', readonly=True)
- functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=True,
- group_expand='_read_group_functional_tool_type_ids')
+ functional_tool_name = fields.Char(string='功能刀具名称', readonly=True, required=True)
+ tool_inventory_id = fields.Many2one('sf.tool.inventory', string='功能刀具清单', store=True,
+ compute='_compute_functional_tool_name')
+
+ @api.depends('functional_tool_name')
+ def _compute_functional_tool_name(self):
+ for item in self:
+ if item.functional_tool_name:
+ inventory = self.env['sf.tool.inventory'].sudo().search([('name', '=', item.functional_tool_name)])
+ if inventory:
+ item.tool_inventory_id = inventory.id
+ item.after_assembly_functional_tool_name = item.functional_tool_name # 组装后名称
+ item.functional_tool_type_id = item.tool_inventory_id.functional_cutting_tool_model_id.id
+ item.tool_groups_id = item.tool_inventory_id.tool_groups_id.id # 刀具组
+ item.after_assembly_functional_tool_type_id = item.tool_inventory_id.functional_cutting_tool_model_id.id
+ item.after_assembly_functional_tool_diameter = item.tool_inventory_id.diameter # 直径
+ item.after_assembly_knife_tip_r_angle = item.tool_inventory_id.angle # R角
+ item.after_assembly_tool_loading_length = item.tool_inventory_id.tool_length # 总长度
+ item.after_assembly_functional_tool_length = item.tool_inventory_id.extension # 伸出长度
+ item.after_assembly_max_lifetime_value = item.tool_inventory_id.life_span # 最大寿命
+
+ @api.depends('tool_inventory_id', 'tool_inventory_id.functional_cutting_tool_model_id', 'tool_inventory_id.angle',
+ 'tool_inventory_id.tool_groups_id', 'tool_inventory_id.diameter', 'tool_inventory_id.tool_length',
+ 'tool_inventory_id.extension', 'tool_inventory_id.life_span')
+ def _compute_inventory_num(self):
+ for item in self:
+ if item.assemble_status != '01':
+ return True
+ if item.tool_inventory_id:
+ item.functional_tool_type_id = item.tool_inventory_id.functional_cutting_tool_model_id.id
+ item.tool_groups_id = item.tool_inventory_id.tool_groups_id.id # 刀具组
+ item.after_assembly_functional_tool_type_id = item.tool_inventory_id.functional_cutting_tool_model_id.id
+ item.after_assembly_functional_tool_diameter = item.tool_inventory_id.diameter # 直径
+ item.after_assembly_knife_tip_r_angle = item.tool_inventory_id.angle # R角
+ item.after_assembly_tool_loading_length = item.tool_inventory_id.tool_length # 总长度
+ item.after_assembly_max_lifetime_value = item.tool_inventory_id.life_span # 最大寿命
+
+ functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型',
+ group_expand='_read_group_functional_tool_type_ids', store=True,
+ compute='_compute_inventory_num')
functional_tool_diameter = fields.Float(string='功能刀具直径(mm)', readonly=True)
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=True)
coarse_middle_thin = fields.Selection([("1", "粗"), ('2', '中'), ('3', '精')], string='粗/中/精', readonly=True)
@@ -375,16 +450,17 @@ class FunctionalToolAssembly(models.Model):
tool_loading_length = fields.Float(string='总长度(mm)', readonly=True)
functional_tool_length = fields.Float(string='伸出长(mm)', readonly=True)
effective_length = fields.Float(string='有效长(mm)', readonly=True)
- loading_task_source = fields.Selection([('0', 'CAM装刀'), ('1', '机台换刀'), ('2', '按库存组装')],
- string='装刀任务来源', readonly=True)
+ loading_task_source = fields.Selection(
+ [('0', 'CAM装刀'), ('1', '机台换刀'), ('2', '按库存组装'), ('3', '寿命到期组装')],
+ string='装刀任务来源', readonly=True)
use_tool_time = fields.Datetime(string='用刀时间', readonly=True)
production_line_name_id = fields.Many2one('sf.production.line', string='申请产线', readonly=True)
machine_tool_name_id = fields.Many2one('maintenance.equipment', string='申请机台', readonly=True)
machine_tool_code = fields.Char(string='机台号', readonly=True)
applicant = fields.Char(string='申请人', readonly=True)
apply_time = fields.Datetime(string='申请时间', default=fields.Datetime.now(), readonly=True)
- assemble_status = fields.Selection([('0', '待组装'), ('1', '已组装')], string='组装状态', default='0',
- tracking=True, readonly=True)
+ assemble_status = fields.Selection([('0', '待组装'), ('01', '组装中'), ('1', '已组装'), ('3', '已取消')],
+ string='组装状态', default='0', tracking=True, readonly=True)
cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', readonly=True)
whether_standard_knife = fields.Boolean(string='是否标准刀', default=True, readonly=True)
reason_for_applying = fields.Char(string='申请原因', readonly=True)
@@ -392,7 +468,7 @@ class FunctionalToolAssembly(models.Model):
alarm_value = fields.Integer(string='报警值(min)', readonly=True)
used_value = fields.Integer(string='已使用值(min)', readonly=True)
- image = fields.Binary('图片', readonly=True)
+ image = fields.Binary('图片', readonly=False)
@api.model
def _read_group_functional_tool_type_ids(self, categories, domain, order):
@@ -402,8 +478,9 @@ class FunctionalToolAssembly(models.Model):
# 刀具物料信息
# ==============整体式刀具型号=============
- integral_freight_barcode_id = fields.Many2one('sf.shelf.location', string='整体式刀具货位')
- integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次')
+ integral_freight_barcode_id = fields.Many2one('sf.shelf.location', string='整体式刀具货位', readonly=True,
+ domain="[('product_id.cutting_tool_material_id.name', '=', '整体式刀具'),('product_num', '>', 0)]")
+ integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', readonly=True)
integral_product_id = fields.Many2one('product.product', string='整体式刀具名称',
compute='_compute_integral_product_id', store=True)
cutting_tool_integral_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='整体式刀具型号',
@@ -412,16 +489,20 @@ class FunctionalToolAssembly(models.Model):
related='integral_product_id.specification_id')
sf_tool_brand_id_1 = fields.Many2one('sf.machine.brand', string='整体式刀具品牌',
related='integral_product_id.brand_id')
+ integral_verify = fields.Boolean('整体刀校验', default=False)
@api.depends('integral_lot_id')
def _compute_integral_product_id(self):
for item in self:
if item.integral_lot_id:
item.integral_product_id = item.integral_lot_id.product_id.id
+ else:
+ item.integral_product_id = False
# =================刀片型号=============
- blade_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀片货位')
- blade_lot_id = fields.Many2one('stock.lot', string='刀片批次')
+ blade_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀片货位', readonly=True,
+ domain="[('product_id.cutting_tool_material_id.name', '=', '刀片'),('product_num', '>', 0)]")
+ blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', readonly=True)
blade_product_id = fields.Many2one('product.product', string='刀片名称', compute='_compute_blade_product_id',
store=True)
cutting_tool_blade_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀片型号',
@@ -429,16 +510,20 @@ class FunctionalToolAssembly(models.Model):
blade_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀片规格',
related='blade_product_id.specification_id')
sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_product_id.brand_id')
+ blade_verify = fields.Boolean('刀片校验', default=False)
@api.depends('blade_lot_id')
def _compute_blade_product_id(self):
for item in self:
if item.blade_lot_id:
item.blade_product_id = item.blade_lot_id.product_id.id
+ else:
+ item.blade_product_id = False
# ==============刀杆型号================
- bar_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀杆货位')
- bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次')
+ bar_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀杆货位', readonly=True,
+ domain="[('product_id.cutting_tool_material_id.name', '=', '刀杆'),('product_num', '>', 0)]")
+ bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', readonly=True)
bar_product_id = fields.Many2one('product.product', string='刀杆名称', compute='_compute_bar_product_id',
store=True)
cutting_tool_cutterbar_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀杆型号',
@@ -446,16 +531,20 @@ class FunctionalToolAssembly(models.Model):
bar_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀杆规格',
related='bar_product_id.specification_id')
sf_tool_brand_id_3 = fields.Many2one('sf.machine.brand', '刀杆品牌', related='bar_product_id.brand_id')
+ bar_verify = fields.Boolean('刀杆校验', default=False)
@api.depends('bar_lot_id')
def _compute_bar_product_id(self):
for item in self:
if item.bar_lot_id:
item.bar_product_id = item.bar_lot_id.product_id.id
+ else:
+ item.bar_product_id = False
# =============刀盘型号================
- pad_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀盘货位')
- pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次')
+ pad_freight_barcode_id = fields.Many2one('sf.shelf.location', string='刀盘货位', readonly=True,
+ domain="[('product_id.cutting_tool_material_id.name', '=', '刀盘'),('product_num', '>', 0)]")
+ pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', readonly=True)
pad_product_id = fields.Many2one('product.product', string='刀盘名称', compute='_compute_pad_product_id',
store=True)
cutting_tool_cutterpad_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀盘型号',
@@ -463,37 +552,42 @@ class FunctionalToolAssembly(models.Model):
pad_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀盘规格',
related='pad_product_id.specification_id')
sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_product_id.brand_id')
+ pad_verify = fields.Boolean('刀盘校验', default=False)
@api.depends('pad_lot_id')
def _compute_pad_product_id(self):
for item in self:
if item.pad_lot_id:
item.pad_product_id = item.pad_lot_id.product_id.id
+ else:
+ item.pad_product_id = False
# ==============刀柄型号==============
- handle_freight_rfid = fields.Char('刀柄Rfid', compute='_compute_handle_product_id', store=True)
- handle_code_id = fields.Many2one('stock.lot', '刀柄序列号')
- handle_product_id = fields.Many2one('product.product', string='刀柄名称', compute='_compute_handle_product_id',
- store=True)
+ handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', readonly=True,
+ domain="[('product_id', '=', handle_product_id)]")
+ handle_freight_rfid = fields.Char('刀柄Rfid', compute='_compute_handle_rfid', store=True)
+ handle_product_id = fields.Many2one('product.product', string='刀柄名称', readonly=True)
cutting_tool_cutterhandle_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀柄型号',
related='handle_product_id.cutting_tool_model_id')
handle_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀柄规格',
related='handle_product_id.specification_id')
sf_tool_brand_id_5 = fields.Many2one('sf.machine.brand', '刀柄品牌', related='handle_product_id.brand_id')
+ handle_verify = fields.Boolean('刀柄校验', default=False)
@api.depends('handle_code_id')
- def _compute_handle_product_id(self):
+ def _compute_handle_rfid(self):
for item in self:
if item.handle_code_id:
- item.handle_product_id = item.handle_code_id.product_id.id
item.handle_freight_rfid = item.handle_code_id.rfid
+ item.rfid = item.handle_freight_rfid
else:
- item.handle_product_id = False
item.handle_freight_rfid = False
+ item.rfid = False
# ==============夹头型号==============
- chuck_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位')
- chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次')
+ chuck_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位', readonly=True,
+ domain="[('product_id.cutting_tool_material_id.name', '=', '夹头'),('product_num', '>', 0)]")
+ chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', readonly=True)
chuck_product_id = fields.Many2one('product.product', string='夹头名称', compute='_compute_chuck_product_id',
store=True)
cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号',
@@ -501,12 +595,15 @@ class FunctionalToolAssembly(models.Model):
chuck_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='夹头规格',
related='chuck_product_id.specification_id')
sf_tool_brand_id_6 = fields.Many2one('sf.machine.brand', '夹头品牌', related='chuck_product_id.brand_id')
+ chuck_verify = fields.Boolean('夹头校验', default=False)
@api.depends('chuck_lot_id')
def _compute_chuck_product_id(self):
for item in self:
if item.chuck_lot_id:
item.chuck_product_id = item.chuck_lot_id.product_id.id
+ else:
+ item.chuck_product_id = False
# ==================待删除字段==================
integral_freight_barcode = fields.Char('整体式刀具货位')
@@ -525,30 +622,39 @@ class FunctionalToolAssembly(models.Model):
handle_name = fields.Char('')
chuck_code_id = fields.Many2one('stock.lot', '夹头序列号')
chuck_name = fields.Char('')
+ # ====================暂时无用字段=========================
+ after_assembly_used_value = fields.Integer(string='组装后已使用值(min)', readonly=True)
# ==============================================
# 组装功能刀具参数信息
start_preset_bool = fields.Boolean('开始预调', default=False)
barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True)
- after_assembly_functional_tool_name = fields.Char(string='组装后功能刀具名称', readonly=True)
- after_assembly_functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model',
- string='组装后功能刀具类型', readonly=True)
- after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)', readonly=True,
- digits=(10, 3))
- after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)', readonly=True, digits=(10, 3))
- after_assembly_new_former = fields.Selection([('0', '新'), ('1', '旧')], string='组装后新/旧', readonly=True)
+ after_assembly_functional_tool_name = fields.Char(string='组装后功能刀具名称', store=True,
+ compute='_compute_functional_tool_name')
+ after_assembly_functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', store=True,
+ string='组装后功能刀具类型',
+ compute='_compute_inventory_num')
+ after_assembly_functional_tool_diameter = fields.Float(string='组装后功能刀具直径(mm)', digits=(10, 3), store=True,
+ compute='_compute_inventory_num')
+ after_assembly_knife_tip_r_angle = fields.Float(string='组装后刀尖R角(mm)', digits=(10, 3), store=True,
+ compute='_compute_inventory_num')
+ after_assembly_new_former = fields.Selection([('0', '新'), ('1', '旧')], string='组装后新/旧', default='0',
+ store=True, compute='_compute_rota_tive')
cut_time = fields.Integer(string='已切削时间(min)', readonly=True)
cut_length = fields.Float(string='已切削长度(mm)', readonly=True)
cut_number = fields.Integer(string='已切削次数', readonly=True)
after_assembly_whether_standard_knife = fields.Boolean(string='组装后是否标准刀', default=True, readonly=True)
- after_assembly_coarse_middle_thin = fields.Selection([("1", "粗"), ('2', '中'), ('3', '精')],
- string='组装后粗/中/精', readonly=True)
- after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)', readonly=True)
- after_assembly_alarm_value = fields.Integer(string='组装后报警值(min)', readonly=True)
- after_assembly_used_value = fields.Integer(string='组装后已使用值(min)', readonly=True)
- after_assembly_tool_loading_length = fields.Float(string='组装后总长度(mm)', readonly=True, digits=(10, 3))
- after_assembly_handle_length = fields.Float(string='组装后刀柄长度(mm)', readonly=True, digits=(10, 3))
- after_assembly_functional_tool_length = fields.Float(string='组装后伸出长(mm)', readonly=True, digits=(10, 3))
+ after_assembly_coarse_middle_thin = fields.Selection([("1", "粗"), ('2', '中'), ('3', '精')], store=True,
+ string='组装后粗/中/精', default='3',
+ compute='_compute_rota_tive')
+ after_assembly_max_lifetime_value = fields.Integer(string='组装后最大寿命值(min)', store=True,
+ compute='_compute_inventory_num')
+ after_assembly_alarm_value = fields.Integer(string='组装后报警值(min)')
+ after_assembly_tool_loading_length = fields.Float(string='组装后总长度(mm)', digits=(10, 3), store=True,
+ compute='_compute_inventory_num')
+ after_assembly_handle_length = fields.Float(string='组装后刀柄长度(mm)', digits=(10, 3))
+ after_assembly_functional_tool_length = fields.Float(string='组装后伸出长(mm)', digits=(10, 3), store=True,
+ compute='_compute_length')
after_assembly_effective_length = fields.Float(string='组装后有效长(mm)', readonly=True)
L_D_number = fields.Float(string='L/D值(mm)', readonly=True)
hiding_length = fields.Float(string='避空长(mm)', readonly=True)
@@ -562,10 +668,74 @@ class FunctionalToolAssembly(models.Model):
sf_machine_table_tool_changing_apply_id = fields.Many2one('sf.machine.table.tool.changing.apply', '机床换刀申请',
readonly=True)
sf_cam_work_order_program_knife_plan_id = fields.Many2one('sf.cam.work.order.program.knife.plan',
- 'CAM工单程序用刀计划', readonly=True, )
+ 'CAM工单程序用刀计划', readonly=True)
active = fields.Boolean(string='已归档', default=True)
+ code = fields.Char('功能刀具编码', compute='_compute_code')
+
+ @api.depends('after_assembly_tool_loading_length', 'after_assembly_handle_length')
+ def _compute_length(self):
+ for item in self:
+ if item.after_assembly_tool_loading_length > item.after_assembly_handle_length:
+ item.after_assembly_functional_tool_length = (
+ item.after_assembly_tool_loading_length - item.after_assembly_handle_length)
+
+ @api.depends('integral_freight_barcode_id', 'blade_freight_barcode_id')
+ def _compute_rota_tive(self):
+ for item in self:
+ rota_tive_boolean = False
+ if item.integral_freight_barcode_id:
+ # 整体刀
+ if item.integral_freight_barcode_id.rotative_Boolean:
+ rota_tive_boolean = True
+ elif item.blade_freight_barcode_id:
+ # 刀片
+ if item.blade_freight_barcode_id.rotative_Boolean:
+ rota_tive_boolean = True
+ if rota_tive_boolean:
+ item.after_assembly_coarse_middle_thin = '1'
+ item.after_assembly_new_former = '1'
+ else:
+ item.after_assembly_coarse_middle_thin = '3'
+ item.after_assembly_new_former = '0'
+
+ @api.onchange('handle_product_id')
+ def _onchange_after_assembly_handle_length(self):
+ for item in self:
+ if item:
+ if item.handle_product_id:
+ item.after_assembly_handle_length = item.handle_product_id.cutting_tool_shank_length
+
+ @api.depends('after_assembly_functional_tool_type_id', 'cutting_tool_cutterhandle_model_id',
+ 'after_assembly_functional_tool_diameter', 'after_assembly_tool_loading_length',
+ 'after_assembly_knife_tip_r_angle', 'after_assembly_functional_tool_length',
+ 'after_assembly_handle_length')
+ def _compute_code(self):
+ for obj in self:
+ if obj.cutting_tool_cutterhandle_model_id:
+ code = obj.cutting_tool_cutterhandle_model_id.code.split('-', 1)[0]
+ str_1 = '%s-GNDJ-%s-D%sL%sR%sB%sH%s' % (
+ code, obj.after_assembly_functional_tool_type_id.code, obj.after_assembly_functional_tool_diameter,
+ obj.after_assembly_tool_loading_length, obj.after_assembly_knife_tip_r_angle,
+ round(obj.after_assembly_functional_tool_length, 3), obj.after_assembly_handle_length)
+ obj.code = '%s-%s' % (str_1, self._get_code_1(str_1))
+ else:
+ obj.code = ''
+
+ def _get_code_1(self, str_2):
+ functional_tool_assembly = self.env['sf.functional.cutting.tool.entity'].sudo().search(
+ [('code', 'like', str_2)],
+ limit=1,
+ order="id desc"
+ )
+ if not functional_tool_assembly:
+ num = "%03d" % 1
+ else:
+ m = int(functional_tool_assembly.code[-3:]) + 1
+ num = "%03d" % m
+ return num
+
def action_open_reference1(self):
self.ensure_one()
return {
@@ -575,41 +745,326 @@ class FunctionalToolAssembly(models.Model):
'res_id': self.id,
}
- def put_start_preset(self):
- self.search([('start_preset_bool', '=', True)]).write({'start_preset_bool': False})
+ def start_preset(self):
+ """
+ 开始组装
+ """
+ # 设置初始值
+ self.start_preset_bool = True
+ self.assemble_status = '01'
+ self.after_assembly_coarse_middle_thin = '3'
+ self.after_assembly_new_former = '0'
+
+ # 调用功能刀具名称对应的清单的BOM获取对应刀具物料信息
+ bom = self._get_inventory_bom(self.tool_inventory_id)
+ # 根据BOM自动配置物料的值
+ self._set_tool_material(bom)
+ logging.info('功能刀具开始组装初始化值成功!')
+
+ def _set_tool_material(self, bom):
+ """根据BOM对刀具物料进行初始配置"""
+ options = bom.get('options')
+ # 配置刀柄信息
+ for handle_id in bom.get('handle_ids'):
+ if handle_id:
+ if not self.handle_product_id:
+ self.handle_product_id = handle_id.id
+ break
+
+ # 刀柄之外的物料配置
+ if options == '刀柄+整体式刀具':
+ # 配置整体式刀具
+ integra_lot_id = self._get_old_tool_material_lot(bom.get('integral_ids'))
+ integra_location_lot_id = self._get_shelf_location_lot(integra_lot_id)
+ self.integral_freight_barcode_id = integra_location_lot_id.shelf_location_id.id
+ self.integral_lot_id = integra_location_lot_id.lot_id.id
+ else:
+ # 配置刀片
+ blade_lot_id = self._get_old_tool_material_lot(bom.get('blade_ids'))
+ blade_location_lot_id = self._get_shelf_location_lot(blade_lot_id)
+ self.blade_freight_barcode_id = blade_location_lot_id.shelf_location_id.id
+ self.blade_lot_id = blade_location_lot_id.lot_id.id
+
+ if options == '刀柄+刀杆+刀片':
+ # 配置刀杆
+ bar_lot_id = self._get_old_tool_material_lot(bom.get('bar_ids'))
+ bar_location_lot_id = self._get_shelf_location_lot(bar_lot_id)
+ self.bar_freight_barcode_id = bar_location_lot_id.shelf_location_id.id
+ self.bar_lot_id = bar_location_lot_id.lot_id.id
+ elif options == '刀柄+刀盘+刀片':
+ # 配置刀盘
+ pad_lot_id = self._get_old_tool_material_lot(bom.get('pad_ids'))
+ pad_location_lot_id = self._get_shelf_location_lot(pad_lot_id)
+ self.pad_freight_barcode_id = pad_location_lot_id.shelf_location_id.id
+ self.pad_lot_id = pad_location_lot_id.lot_id.id
+
+ def _get_old_tool_material_lot(self, material_ids):
+ """ 根据先进先出原则选择物料批次 """
+ location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
+ stock_quant = self.env['stock.quant'].sudo().search(
+ [('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')],
+ order='lot_id', limit=1)
+ if stock_quant:
+ return stock_quant.lot_id
+ else:
+ raise ValidationError(f'【{material_ids[0].cutting_tool_material_id.name}】物料库存不足,请先进行盘点或采购')
+
+ def _get_shelf_location_lot(self, lot_id):
+ """根据所给的刀具物料批次号,返回一个刀具物料货位、批次信息"""
+ location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id)])
+ if not location_lots:
+ raise ValidationError(f'没有查询到批次为【{lot_id.name}】物料的货位信息!')
+ else:
+ return location_lots[0]
+
+ def _get_inventory_bom(self, inventory_id):
+ """获取BOM的刀具物料产品信息"""
+ product_ids = inventory_id.jikimo_bom_ids.product_ids # BOM配置的物料产品
+ options = inventory_id.jikimo_bom_ids.options # BOM产品组装类型
+
+ if not product_ids or not options:
+ raise ValidationError('功能刀具清单的BOM未进行配置,请先配置BOM信息!')
+ handle_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '刀柄')
+ integral_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '整体式刀具')
+ blade_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '刀片')
+ bar_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '刀杆')
+ pad_ids = product_ids.filtered(lambda a: a.cutting_tool_material_id.name == '刀盘')
+
+ if not handle_ids:
+ raise ValidationError('功能刀具清单的BOM未配置[刀柄]信息,请先配置BOM再开始组装!')
+
+ if options == '刀柄+整体式刀具':
+ if not integral_ids:
+ raise ValidationError('功能刀具清单的BOM未配置[刀柄]信息,请先配置BOM再开始组装!')
+ return {'options': options, 'handle_ids': handle_ids, 'integral_ids': integral_ids}
+ elif options == '刀柄+刀杆+刀片':
+ if not blade_ids:
+ raise ValidationError('功能刀具清单的BOM未配置[刀片]信息,请先配置BOM再开始组装!')
+ if not bar_ids:
+ raise ValidationError('功能刀具清单的BOM未配置[刀杆]信息,请先配置BOM再开始组装!')
+ return {'options': options, 'handle_ids': handle_ids, 'blade_ids': blade_ids, 'bar_ids': bar_ids}
+ elif options == '刀柄+刀盘+刀片':
+ if not blade_ids:
+ raise ValidationError('功能刀具清单的BOM未配置[刀片]信息,请先配置BOM再开始组装!')
+ if not pad_ids:
+ raise ValidationError('功能刀具清单的BOM未配置[刀盘]信息,请先配置BOM再开始组装!')
+ return {'options': options, 'handle_ids': handle_ids, 'blade_ids': blade_ids, 'pad_ids': pad_ids}
+ else:
+ raise ValidationError(f'功能刀具清单BOM的组装方式错误:【{options}】')
+
+ def set_tool_lot(self):
+ # 获取BOM的刀具物料产品信息
+ tool_data = self._get_inventory_bom(self.tool_inventory_id)
+
+ # 获取刀具类型
+ tool_type = self.env.context.get('tool_type')
+
+ if tool_type == '刀柄':
+ tool_material_ids = tool_data.get('handle_ids')
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_tool_product_tree')
+ elif tool_type == '整体式刀具':
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('integral_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_1')
+ elif tool_type == '刀片':
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('blade_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_2')
+ elif tool_type == '刀杆':
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('bar_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_3')
+ else:
+ tool_material_ids = self._get_all_material_location_lot(tool_data.get('pad_ids'))
+ tool_material_tree_id = self.env.ref('sf_tool_management.view_shelf_location_lot_tree_4')
+
+ if tool_type == '刀柄':
+ return {
+ "type": "ir.actions.act_window",
+ "res_model": "product.product",
+ "views": [[tool_material_tree_id.id, "tree"],
+ [self.env.ref('sf_tool_management.view_tool_product_search').id, "search"]],
+ "target": "new",
+ "domain": [('id', 'in', tool_material_ids.ids)],
+ "context": {'tool_id': self.id}
+ }
+ elif tool_type in ['整体式刀具', '刀片', '刀杆', '刀盘']:
+ return {
+ "type": "ir.actions.act_window",
+ "res_model": "sf.shelf.location.lot",
+ "views": [[tool_material_tree_id.id, "tree"]],
+ "target": "new",
+ "domain": [('id', 'in', tool_material_ids.ids)],
+ "context": {'tool_id': self.id}
+ }
+
+ def _get_all_material_location_lot(self, material_ids):
+ """ 获取所有满足条件 """
+ location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
+ stock_quant_ids = self.env['stock.quant'].sudo().search(
+ [('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids if material_ids else []),
+ ('quantity', '>', '0')])
+ lot_ids = []
+ for stock_quant_id in stock_quant_ids:
+ lot_ids.append(stock_quant_id.lot_id.id)
+ location_lots = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', 'in', lot_ids)])
+ return location_lots
+
+ def functional_tool_assembly(self):
+ """
+ 功能刀具确认组装
+ :return:
+ """
+ logging.info('功能刀具开始组装!')
+ # 对物料做必填判断
+ self.materials_must_be_judged()
+
+ product_id = self.env['product.product']
+ # 创建组装入库单
+ # 创建功能刀具批次/序列号记录
+ stock_lot = product_id.create_assemble_warehouse_receipt(self)
+ # 封装功能刀具数据,用于创建功能刀具记录
+ desc_2 = self.get_desc(stock_lot, self)
+ # 创建功能刀具组装入库单
+ self.env['stock.picking'].create_tool_stocking_picking(stock_lot, self)
+ # 创建刀具物料出库单
+ self.env['stock.picking'].create_tool_stocking_picking1(self)
+
+ # ============================创建功能刀具列表、安全库存记录===============================
+ # 创建功能刀具列表记录
+ record_1 = self.env['sf.functional.cutting.tool.entity'].create(desc_2)
+ # 创建安全库存信息
+ self.env['sf.real.time.distribution.of.functional.tools'].create_or_edit_safety_stock({
+ 'functional_name_id': self.tool_inventory_id.id
+ }, record_1)
+
+ # =====================修改功能刀具组装单、机床换刀申请、CAM工单程序用刀计划的状态==============
+ if self.sf_machine_table_tool_changing_apply_id:
+ # 修改机床换刀申请的状态
+ self.env['sf.machine.table.tool.changing.apply'].sudo().search([
+ ('id', '=', self.sf_machine_table_tool_changing_apply_id.id)
+ ]).write({'status': '3'})
+ elif self.sf_cam_work_order_program_knife_plan_id:
+ # 修改CAM工单程序用刀计划状态
+ cam_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().search([
+ ('id', '=', self.sf_cam_work_order_program_knife_plan_id.id)
+ ])
+ cam_plan.write({'plan_execute_status': '2'})
+
+ # ============修改组装单状态为已组装=================================‘
self.write({
- 'after_assembly_tool_loading_length': 0,
- 'after_assembly_functional_tool_diameter': 0,
- 'after_assembly_knife_tip_r_angle': 0,
- 'start_preset_bool': True
+ 'assemble_status': '1',
+ 'start_preset_bool': False,
+ 'tool_loading_person': self.env.user.name,
+ 'tool_loading_time': fields.Datetime.now()
})
+
+ logging.info('功能刀具组装完成!')
+
+ def materials_must_be_judged(self):
+ """
+ 功能刀具组装必填判断
+ """
+ # 物料准确性校验
+
+ # 物料必填校验
+ if not self.handle_code_id:
+ raise ValidationError('缺少【刀柄】物料信息!')
+ if self.integral_lot_id:
+ if not self.integral_verify:
+ raise ValidationError('【整体式刀具】未进行验证!')
+ elif self.blade_lot_id:
+ if not self.blade_verify:
+ raise ValidationError('【刀片】未进行验证!')
+ if self.bar_lot_id:
+ if not self.bar_verify:
+ raise ValidationError('【刀杆】未进行验证!')
+ elif self.pad_lot_id:
+ if not self.pad_verify:
+ raise ValidationError('【刀盘】未进行验证!')
+ # 组装参数必填校验
+ if self.after_assembly_max_lifetime_value == 0:
+ raise ValidationError('组装参数信息【最大寿命值】不能为0!')
+ if self.after_assembly_functional_tool_diameter <= 0:
+ raise ValidationError('组装参数信息【刀具直径】不能小于等于0!')
+ if self.after_assembly_tool_loading_length == 0:
+ raise ValidationError('组装参数信息【总长度】不能为0!!!')
+ if self.after_assembly_handle_length == 0:
+ raise ValidationError('组装参数信息【刀柄长度】不能为0!')
+ if self.after_assembly_tool_loading_length < self.after_assembly_handle_length:
+ raise ValidationError('组装参数信息【刀柄长度】不能大于【总长度】!')
+
+ def get_desc(self, stock_lot, functional_tool_assembly_id):
return {
- 'type': 'ir.actions.act_window',
- 'res_model': 'sf.functional.tool.assembly.order',
- 'name': '功能刀具组装单',
- 'view_mode': 'form',
- 'target': 'new',
- 'context': {'default_name': self.name,
- 'default_assembly_order_code': self.assembly_order_code,
- 'default_production_line_name_id': self.production_line_name_id.id,
- 'default_machine_tool_name_id': self.machine_tool_name_id.id,
- 'default_cutter_spacing_code_id': self.cutter_spacing_code_id.id,
- 'default_functional_tool_name': self.functional_tool_name,
- 'default_functional_tool_type_id': self.functional_tool_type_id.id,
- 'default_tool_groups_id': self.tool_groups_id.id,
- 'default_functional_tool_diameter': self.functional_tool_diameter,
- 'default_knife_tip_r_angle': self.knife_tip_r_angle,
- 'default_tool_loading_length': self.tool_loading_length,
- 'default_functional_tool_length': self.functional_tool_length,
- 'default_effective_length': self.effective_length,
- 'default_whether_standard_knife': self.whether_standard_knife,
- 'default_coarse_middle_thin': self.coarse_middle_thin,
- 'default_new_former': self.new_former,
- 'default_use_tool_time': self.use_tool_time,
- 'default_reason_for_applying': self.reason_for_applying
- }
+ 'barcode_id': stock_lot.id,
+ 'code': self.code,
+ 'name': self.tool_inventory_id.name,
+ 'tool_name_id': self.tool_inventory_id.id,
+ 'rfid': self.rfid,
+ 'tool_groups_id': self.tool_groups_id.id,
+ 'functional_tool_name_id': functional_tool_assembly_id.id,
+ 'sf_cutting_tool_type_id': self.after_assembly_functional_tool_type_id.id,
+ 'cutting_tool_integral_model_id': self.integral_product_id.id,
+ 'cutting_tool_blade_model_id': self.blade_product_id.id,
+ 'cutting_tool_cutterbar_model_id': self.bar_product_id.id,
+ 'cutting_tool_cutterpad_model_id': self.pad_product_id.id,
+ 'cutting_tool_cutterhandle_model_id': self.handle_product_id.id,
+ 'cutting_tool_cutterhead_model_id': self.chuck_product_id.id,
+
+ 'functional_tool_diameter': self.after_assembly_functional_tool_diameter,
+ 'knife_tip_r_angle': self.after_assembly_knife_tip_r_angle,
+ 'coarse_middle_thin': self.after_assembly_coarse_middle_thin,
+ 'new_former': self.after_assembly_new_former,
+ 'tool_loading_length': self.after_assembly_tool_loading_length,
+ 'handle_length': self.after_assembly_handle_length,
+ 'functional_tool_length': self.after_assembly_functional_tool_length,
+ 'effective_length': self.after_assembly_effective_length,
+
+ 'max_lifetime_value': self.after_assembly_max_lifetime_value,
+ 'alarm_value': self.after_assembly_alarm_value,
+ 'used_value': self.after_assembly_used_value,
+ 'whether_standard_knife': self.after_assembly_whether_standard_knife,
+ 'L_D_number': self.L_D_number,
+ 'hiding_length': self.hiding_length,
+ 'cut_time': self.cut_time,
+ 'cut_length': self.cut_length,
+ 'cut_number': self.cut_number,
+ 'image': self.image,
}
+ # def put_start_preset(self):
+ # # 打开组装弹窗开始组装
+ # self.search([('start_preset_bool', '=', True)]).write({'start_preset_bool': False})
+ # self.write({
+ # 'after_assembly_tool_loading_length': 0,
+ # 'after_assembly_functional_tool_diameter': 0,
+ # 'after_assembly_knife_tip_r_angle': 0,
+ # 'start_preset_bool': True
+ # })
+ # return {
+ # 'type': 'ir.actions.act_window',
+ # 'res_model': 'sf.functional.tool.assembly.order',
+ # 'name': '功能刀具组装单',
+ # 'view_mode': 'form',
+ # 'target': 'new',
+ # 'context': {'default_name': self.name,
+ # 'default_assembly_order_code': self.assembly_order_code,
+ # 'default_production_line_name_id': self.production_line_name_id.id,
+ # 'default_machine_tool_name_id': self.machine_tool_name_id.id,
+ # 'default_cutter_spacing_code_id': self.cutter_spacing_code_id.id,
+ # 'default_functional_tool_name': self.functional_tool_name,
+ # 'default_functional_tool_type_id': self.functional_tool_type_id.id,
+ # 'default_tool_groups_id': self.tool_groups_id.id,
+ # 'default_functional_tool_diameter': self.functional_tool_diameter,
+ # 'default_knife_tip_r_angle': self.knife_tip_r_angle,
+ # 'default_tool_loading_length': self.tool_loading_length,
+ # 'default_functional_tool_length': self.functional_tool_length,
+ # 'default_effective_length': self.effective_length,
+ # 'default_whether_standard_knife': self.whether_standard_knife,
+ # 'default_coarse_middle_thin': self.coarse_middle_thin,
+ # 'default_new_former': self.new_former,
+ # 'default_use_tool_time': self.use_tool_time,
+ # 'default_reason_for_applying': self.reason_for_applying
+ # }
+ # }
+
def _get_code(self, loading_task_source):
"""
自动生成组装单编码
@@ -622,6 +1077,8 @@ class FunctionalToolAssembly(models.Model):
code = 'J' + datetime
elif loading_task_source == '2':
code = 'K' + datetime
+ elif loading_task_source == '3':
+ code = 'S' + datetime
else:
code = False
functional_tool_assembly = self.env['sf.functional.tool.assembly'].sudo().search(
@@ -648,6 +1105,13 @@ class FunctionalToolAssembly(models.Model):
return functional_tool
return False
+ bool_preset_parameter = fields.Boolean('', default=False)
+
+ def get_tool_preset_parameter(self):
+ if not self.bool_preset_parameter:
+ raise ValidationError('没有获取到测量数据, 请确认刀具预调仪操作是否正确!')
+ return True
+
def assemble_single_print(self):
"""
todo 组装单打印
@@ -1032,6 +1496,50 @@ class FunctionalToolDismantle(models.Model):
})
logging.info('【%s】刀具拆解成功!' % self.name)
+ # ==================根据条件创建功能刀具组装单=======================
+ # 如果报废原因为【寿命到期报废】并且刀柄不报废时, 创建功能刀具组装单
+ if self.dismantle_cause in ['寿命到期报废'] and not self.scrap_boolean:
+ # 创建组装单
+ assembly_id = self.env['sf.functional.tool.assembly'].sudo().create({
+ 'functional_tool_name': self.functional_tool_id.name,
+ 'handle_code_id': self.handle_lot_id.id,
+ 'handle_product_id': self.handle_product_id.id,
+ 'loading_task_source': '3',
+ 'reason_for_applying': '刀具寿命到期'
+ })
+ action = self.env.ref('sf_tool_management.sf_functional_tool_assembly_form')
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': '组装单创建完成',
+ 'message': '请组装同名称的功能刀具',
+ 'type': 'info',
+ 'links': [{
+ 'label': '组装单',
+ 'url': f'#action={action.id}&id={assembly_id.id}&model=sf.functional.tool.assembly',
+ }],
+ },
+ }
+ # {
+ # 'type': 'ir.actions.act_window',
+ # 'res_model': 'sf.functional.tool.assembly',
+ # 'view_type': 'form',
+ # 'view_mode': 'form',
+ # 'res_id': assembly_id.id,
+ # }
+
+ # 'params': {
+ # 'title': _('The following replenishment order has been generated'),
+ # 'message': '%s',
+ # 'links': [{
+ # 'label': order.display_name,
+ # 'url': f'#action={action.id}&id={order.id}&model=purchase.order',
+ # }],
+ # 'sticky': False,
+ # }
+
def create_tool_picking_scrap(self, datas):
scrap_data = datas['scrap']
picking_data = datas['picking']
diff --git a/sf_tool_management/models/stock.py b/sf_tool_management/models/stock.py
index a179c7d7..32719520 100644
--- a/sf_tool_management/models/stock.py
+++ b/sf_tool_management/models/stock.py
@@ -1,4 +1,9 @@
+import logging
+
+from datetime import timedelta, datetime, date
+
from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
class ShelfLocation(models.Model):
@@ -58,3 +63,289 @@ class StockPicking(models.Model):
if move_lines:
self.env['stock.move.line'].sudo().button_function_tool_use_verify(move_lines)
return res
+
+ def create_tool_stocking_picking(self, stock_lot, obj):
+ """
+ 创建功能刀具组装入库单
+ """
+ # 获取名称为刀具组装入库的作业类型
+ picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '刀具组装入库')])
+ # 创建刀具组装入库单
+ picking_id = self.env['stock.picking'].create({
+ 'name': self._get_name_stock(picking_type_id),
+ 'picking_type_id': picking_type_id.id,
+ 'location_id': picking_type_id.default_location_src_id.id,
+ 'location_dest_id': picking_type_id.default_location_dest_id.id,
+ 'origin': obj.assembly_order_code
+ })
+ # 创建作业详情对象记录,并绑定到刀具组装入库单
+ self.env['stock.move.line'].create({
+ 'picking_id': picking_id.id,
+ 'product_id': stock_lot.product_id.id,
+ 'location_id': picking_id.location_id.id,
+ 'location_dest_id': picking_id.location_dest_id.id,
+ 'lot_id': stock_lot.id,
+ 'install_tool_time': fields.Datetime.now(),
+ 'qty_done': 1,
+ 'functional_tool_name_id': obj.id,
+ 'functional_tool_type_id': obj.functional_tool_type_id.id,
+ 'diameter': obj.after_assembly_functional_tool_diameter,
+ 'knife_tip_r_angle': obj.after_assembly_knife_tip_r_angle,
+ 'code': obj.code,
+ 'rfid': obj.rfid,
+ 'functional_tool_name': obj.after_assembly_functional_tool_name,
+ 'tool_groups_id': obj.tool_groups_id.id
+ })
+ # 将刀具组装入库单的状态更改为就绪
+ picking_id.action_confirm()
+ picking_id.button_validate()
+
+ def _get_name_stock(self, picking_type_id):
+ name = picking_type_id.sequence_id.prefix + str(
+ datetime.strptime(str(fields.Date.today()), "%Y-%m-%d").strftime("%Y%m%d"))
+ stock_id = self.env['stock.picking'].sudo().search(
+ [('name', 'like', name), ('picking_type_id', '=', picking_type_id.id)],
+ limit=1,
+ order="id desc"
+ )
+ if not stock_id:
+ num = "%03d" % 1
+ else:
+ m = int(stock_id.name[-3:]) + 1
+ num = "%03d" % m
+ return name + str(num)
+
+ def tool_location_num(self, freight_barcode_id, lot_id):
+ location_lot = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id), (
+ 'shelf_location_id', '=', freight_barcode_id.id)])
+ if not location_lot:
+ raise ValidationError(
+ f'[{freight_barcode_id.barcode}]货位的[{lot_id.name}]批次物料已用完,请重新选择!')
+
+ def create_tool_stocking_picking1(self, obj):
+ """
+ 创建刀具物料出库单
+ """
+ # 获取名称为内部调拨的作业类型
+ picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')])
+ # 创建刀具物料出库单
+ picking_id = self.env['stock.picking'].create({
+ 'name': self._get_name_stock1(picking_type_id),
+ 'picking_type_id': picking_type_id.id,
+ 'location_id': self.env['stock.location'].search([('name', '=', '刀具房')]).id,
+ 'location_dest_id': self.env['stock.location'].search([('name', '=', '刀具组装位置')]).id,
+ 'origin': obj.assembly_order_code
+ })
+ # =============刀具物料出库===================
+ stock_move_id = self.env['stock.move']
+ datas = {'data': [], 'picking_id': picking_id}
+ if obj.handle_code_id:
+ if obj.handle_code_id.tool_material_status == '在用':
+ raise ValidationError(f'Rfid为{obj.handle_code_id.rfid}的刀柄已被使用,请重新选择!')
+ # 修改刀柄序列号状态为【在用】
+ obj.handle_code_id.sudo().write({'tool_material_status': '在用'})
+ datas['data'].append(
+ {'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
+ if obj.integral_product_id:
+ self.tool_location_num(obj.integral_freight_barcode_id, obj.integral_lot_id)
+ datas['data'].append(
+ {'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_lot_id})
+ if obj.blade_product_id:
+ self.tool_location_num(obj.blade_freight_barcode_id, obj.blade_lot_id)
+ datas['data'].append(
+ {'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_lot_id})
+ if obj.bar_product_id:
+ self.tool_location_num(obj.bar_freight_barcode_id, obj.bar_lot_id)
+ datas['data'].append(
+ {'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_lot_id})
+ if obj.pad_product_id:
+ self.tool_location_num(obj.pad_freight_barcode_id, obj.pad_lot_id)
+ datas['data'].append(
+ {'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_lot_id})
+ if obj.chuck_product_id:
+ self.tool_location_num(obj.chuck_freight_barcode_id, obj.chuck_lot_id)
+ datas['data'].append(
+ {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_lot_id})
+ # 创建刀具物料出库库存移动记录
+ stock_move_id.create_tool_material_stock_moves(datas)
+ # 将刀具物料出库库单的状态更改为就绪
+ picking_id.action_confirm()
+ # 修改刀具物料出库移动历史记录
+ stock_move_id.write_tool_material_stock_move_lines(datas)
+ # 设置数量,并验证完成
+ picking_id.action_set_quantities_to_reservation()
+ picking_id.button_validate()
+ logging.info(f'刀具物料调拨单状态:{picking_id.state}')
+
+ def _get_name_stock1(self, picking_type_id):
+ name = f'{picking_type_id.sequence_id.prefix}DJ/{date.today().strftime("%y")}'
+ stock_id = self.env['stock.picking'].sudo().search(
+ [('name', 'like', name), ('picking_type_id', '=', picking_type_id.id)],
+ limit=1,
+ order="id desc"
+ )
+ if not stock_id:
+ num = "%05d" % 1
+ else:
+ m = int(stock_id.name[-5:]) + 1
+ num = "%05d" % m
+ return name + str(num)
+
+
+class StockMove(models.Model):
+ _inherit = 'stock.move'
+
+ def create_tool_material_stock_moves(self, datas):
+ picking_id = datas['picking_id']
+ data = datas['data']
+ stock_move_ids = []
+ for res in data:
+ if res:
+ if res['lot_id'].product_qty <= 0:
+ raise ValidationError(
+ f'[{res["lot_id"].product_id.name}产品的{res["lot_id"].name}]批次/序列号库存不足!')
+ # 创建库存移动记录
+ stock_move_id = self.env['stock.move'].sudo().create({
+ 'name': picking_id.name,
+ 'picking_id': picking_id.id,
+ 'product_id': res['lot_id'].product_id.id,
+ 'location_id': picking_id.location_id.id,
+ 'location_dest_id': picking_id.location_dest_id.id,
+ 'product_uom_qty': 1.00,
+ 'reserved_availability': 1.00
+ })
+ stock_move_ids.append(stock_move_id)
+ return stock_move_ids
+
+ def write_tool_material_stock_move_lines(self, datas):
+ picking_id = datas['picking_id']
+ data = datas['data']
+ move_line_ids = picking_id.move_line_ids
+ for move_line_id in move_line_ids:
+ for res in data:
+ if move_line_id.lot_id.product_id == res['lot_id'].product_id:
+ move_line_id.write({
+ 'current_location_id': res.get('current_location_id').id,
+ 'lot_id': res.get('lot_id').id
+ })
+ return True
+
+
+class ProductProduct(models.Model):
+ _inherit = 'product.product'
+
+ def create_assemble_warehouse_receipt(self, obj):
+ """
+ 创建功能刀具批次/序列号记录
+ """
+ product_id = self.env['product.product'].search([('categ_type', '=', '功能刀具'), ('tracking', '=', 'serial')])
+ if not product_id:
+ logging.info('没有搜索到功能刀具产品:%s' % product_id)
+ raise ValidationError('没有找到按唯一序列号追溯的功能刀具产品信息!')
+ stock_lot = self.env['stock.lot'].create({
+ 'name': self.get_stock_lot_name(obj),
+ 'product_id': product_id[0].id,
+ 'company_id': self.env.company.id
+ })
+ return stock_lot
+
+ def get_stock_lot_name(self, obj):
+ """
+ 生成功能刀具序列号
+ """
+ company = obj.cutting_tool_cutterhandle_model_id.code.split('-', 1)[0]
+ new_time = datetime.strptime(str(fields.Date.today()), "%Y-%m-%d").strftime("%Y%m%d")
+ code = '%s-GNDJ-%s-%s' % (company, obj.after_assembly_functional_tool_type_id.code, new_time)
+ stock_lot_id = self.env['stock.lot'].sudo().search(
+ [('name', 'like', code)], limit=1, order="id desc")
+ if not stock_lot_id:
+ num = "%03d" % 1
+ else:
+ m = int(stock_lot_id.name[-3:]) + 1
+ num = "%03d" % m
+ return '%s-%s' % (code, num)
+
+ def set_tool_material(self):
+ tool_id = self.env.context.get('tool_id')
+ tool_assembly_id = self.env['sf.functional.tool.assembly'].sudo().search([('id', '=', tool_id)])
+ if len(self) > 1:
+ raise ValidationError('请不要多选')
+ else:
+ tool_assembly_id.handle_product_id = self.id
+ tool_assembly_id.handle_code_id = False
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'message': '刀柄信息更改成功',
+ 'type': 'success',
+ 'next': {'type': 'ir.actions.act_window_close'}
+ }
+ }
+
+
+class SfShelfLocationLot(models.Model):
+ _inherit = 'sf.shelf.location.lot'
+
+ product_id = fields.Many2one('product.product', '产品', compute='_compute_product_id', store=True)
+ cutting_tool_type = fields.Char(string="刀具物料类型", compute='_compute_product_id', store=True)
+ cutting_tool_type_id = fields.Many2one('sf.cutting.tool.type', string='类型',
+ related='product_id.cutting_tool_type_id')
+ cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='型号名称',
+ related='product_id.cutting_tool_model_id')
+ specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='物料号',
+ related='product_id.specification_id')
+ brand_id = fields.Many2one('sf.machine.brand', '品牌', related='product_id.brand_id')
+
+ cutting_tool_blade_diameter = fields.Float('刃部直径(mm)', related='product_id.cutting_tool_blade_diameter')
+ cutting_tool_blade_tip_working_size = fields.Char('刀尖R角(mm)',
+ related='product_id.cutting_tool_blade_tip_working_size')
+ cutting_tool_blade_radius = fields.Char('刀尖圆弧半径(mm)',
+ related='product_id.cutting_tool_blade_tip_circular_arc_radius')
+ cutting_tool_cutter_arbor_diameter = fields.Float('刀杆直径(mm)',
+ related='product_id.cutting_tool_cutter_arbor_diameter')
+ cutting_tool_cutter_head_diameter = fields.Float('刀盘直径(mm)',
+ related='product_id.cutting_tool_cutter_head_diameter')
+
+ fit_blade_shape_id = fields.Many2one('maintenance.equipment.image', '适配刀片形状',
+ related='product_id.fit_blade_shape_id')
+
+ @api.depends('lot_id')
+ def _compute_product_id(self):
+ for item in self:
+ if item.lot_id:
+ item.product_id = item.lot_id.product_id.id
+ item.cutting_tool_type = item.lot_id.product_id.cutting_tool_type
+
+ def set_tool_material(self):
+ tool_type = self.env.context.get('tool_type')
+ tool_id = self.env.context.get('tool_id')
+ tool_assembly_id = self.env['sf.functional.tool.assembly'].sudo().search([('id', '=', tool_id)])
+ if len(self) > 1:
+ raise ValidationError('请不要多选')
+ if tool_type == '整体式刀具':
+ tool_assembly_id.integral_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.integral_lot_id = self.lot_id.id
+ tool_assembly_id.integral_verify = False
+ elif tool_type == '刀片':
+ tool_assembly_id.blade_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.blade_lot_id = self.lot_id.id
+ tool_assembly_id.blade_verify = False
+ elif tool_type == '刀杆':
+ tool_assembly_id.bar_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.bar_lot_id = self.lot_id.id
+ tool_assembly_id.bar_verify = False
+ elif tool_type == '刀盘':
+ tool_assembly_id.pad_freight_barcode_id = self.shelf_location_id.id
+ tool_assembly_id.pad_lot_id = self.lot_id.id
+ tool_assembly_id.pad_verify = False
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'message': f'[{tool_type}]物料信息更改成功',
+ 'type': 'success',
+ 'next': {'type': 'ir.actions.act_window_close'}
+ }
+ }
diff --git a/sf_tool_management/views/stock.xml b/sf_tool_management/views/stock.xml
index 81783631..c0942282 100644
--- a/sf_tool_management/views/stock.xml
+++ b/sf_tool_management/views/stock.xml
@@ -11,4 +11,131 @@
+
+
+ 刀柄
+ product.product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ product.product
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.shelf.location.lot.tree
+ sf.shelf.location.lot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml
index 390fcb15..ab4912ce 100644
--- a/sf_tool_management/views/tool_base_views.xml
+++ b/sf_tool_management/views/tool_base_views.xml
@@ -446,19 +446,23 @@
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -467,19 +471,23 @@
功能刀具组装
sf.functional.tool.assembly
-