From 7f6444cc890309824ef3318f0cd51433e3c2ecc1 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Wed, 10 Apr 2024 17:32:35 +0800 Subject: [PATCH 01/20] =?UTF-8?q?1=E3=80=81=E4=BC=98=E5=8C=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=88=80=E5=85=B7=E7=BB=84=E8=A3=85=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E6=9E=84=E5=88=80=E5=85=B7=E7=89=A9=E6=96=99?= =?UTF-8?q?=E7=9A=84=E7=BB=84=E6=88=90=E7=BB=93=E6=9E=84=EF=BC=9B2?= =?UTF-8?q?=E3=80=81=E4=BC=98=E5=8C=96=E5=88=80=E5=85=B7=E7=89=A9=E6=96=99?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=A8=A1=E5=9E=8B=EF=BC=8C=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=88=80=E5=85=B7=E7=89=A9=E6=B5=81=E5=BA=93=E5=AD=98=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E6=96=B9=E6=B3=95=EF=BC=9B3=E3=80=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7=E7=BB=84=E8=A3=85?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=EF=BC=8C=E9=87=8D=E6=9E=84=E5=88=80=E5=85=B7?= =?UTF-8?q?=E7=89=A9=E6=96=99=E5=87=BA=E5=BA=93=E6=96=B9=E6=B3=95=EF=BC=9B?= =?UTF-8?q?4=E3=80=81=E9=87=8D=E6=9E=84=E6=89=AB=E7=A0=81=E5=BD=95?= =?UTF-8?q?=E5=85=A5=E5=88=80=E5=85=B7=E7=89=A9=E6=96=99=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=9B5=E3=80=81=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7=E5=88=97=E8=A1=A8=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/__manifest__.py | 1 + sf_tool_management/models/__init__.py | 1 + sf_tool_management/models/base.py | 624 ++++-------------- sf_tool_management/models/functional_tool.py | 488 ++++++++++++++ .../models/tool_material_search.py | 55 +- .../views/functional_tool_views.xml | 511 ++++++++++++++ sf_tool_management/views/tool_base_views.xml | 592 ++--------------- .../views/tool_material_search.xml | 2 +- sf_tool_management/wizard/wizard.py | 146 ++-- sf_tool_management/wizard/wizard_view.xml | 11 +- 10 files changed, 1302 insertions(+), 1129 deletions(-) create mode 100644 sf_tool_management/models/functional_tool.py create mode 100644 sf_tool_management/views/functional_tool_views.xml diff --git a/sf_tool_management/__manifest__.py b/sf_tool_management/__manifest__.py index a7750f69..f2389f48 100644 --- a/sf_tool_management/__manifest__.py +++ b/sf_tool_management/__manifest__.py @@ -16,6 +16,7 @@ 'security/ir.model.access.csv', 'wizard/wizard_view.xml', 'views/tool_base_views.xml', + 'views/functional_tool_views.xml', 'views/mrp_workcenter_views.xml', 'views/sf_maintenance_equipment.xml', 'views/menu_view.xml', diff --git a/sf_tool_management/models/__init__.py b/sf_tool_management/models/__init__.py index f73f5e3d..b589e095 100644 --- a/sf_tool_management/models/__init__.py +++ b/sf_tool_management/models/__init__.py @@ -1,4 +1,5 @@ from . import base +from . import functional_tool from . import tool_material_search from . import maintenance_equipment from . import mrp_workorder diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index d90c65bd..0b444a98 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -1,477 +1,8 @@ # -*- coding: utf-8 -*- -import re -import json -import requests from datetime import timedelta from odoo import SUPERUSER_ID from odoo import fields, models, api from odoo.exceptions import ValidationError -from odoo.addons.sf_base.commons.common import Common - - -class FunctionalCuttingToolEntity(models.Model): - _name = 'sf.functional.cutting.tool.entity' - _description = '功能刀具列表' - - tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id') - code = fields.Char('编码') - rfid = fields.Char('Rfid', readonly=True) - name = fields.Char('名称') - functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称', readonly=True) - barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True) - sf_cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀具型号') - sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', - group_expand='_read_group_mrs_cutting_tool_type_id', compute_sudo=True) - - functional_tool_diameter = fields.Integer(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) - new_former = fields.Selection([('0', '新'), ('1', '旧')], string='新/旧', readonly=True) - 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) - tool_room_num = fields.Integer(string='刀具房数量', readonly=True) - line_edge_knife_library_num = fields.Integer(string='线边刀库数量', readonly=True) - machine_knife_library_num = fields.Integer(string='机内刀库数量', readonly=True) - max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True) - alarm_value = fields.Integer(string='报警值(min)', readonly=True) - used_value = fields.Integer(string='已使用值(min)', readonly=True) - functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], - string='状态', store=True, default='正常') - current_location_id = fields.Many2one('stock.location', string='当前位置', readonly=True) - current_location = fields.Selection( - [('组装后', '组装后'), ('刀具房', '刀具房'), ('线边刀库', '线边刀库'), ('机内刀库', '机内刀库')], - string='位置', compute='_compute_current_location_id', store=True) - image = fields.Binary('图片', readonly=True) - - active = fields.Boolean(string='已归档', default=True) - - @api.depends('barcode_id.quant_ids') - def _compute_current_location_id(self): - for record in self: - if record.barcode_id.quant_ids: - for quant_id in record.barcode_id.quant_ids: - if quant_id.inventory_quantity_auto_apply > 0: - record.current_location_id = quant_id.location_id - if quant_id.location_id.name == '制造前': - record.current_location = '机内刀库' - else: - record.current_location = quant_id.location_id.name - if record.current_location_id: - record.sudo().get_location_num() - else: - record.current_location_id = False - record.current_location = False - - def get_location_num(self): - """ - 计算库存位置数量 - """ - for obj in self: - if obj.current_location_id: - obj.tool_room_num = 0 - obj.line_edge_knife_library_num = 0 - obj.machine_knife_library_num = 0 - if obj.current_location in ['刀具房']: - obj.tool_room_num = 1 - elif "线边刀库" in obj.current_location: - obj.line_edge_knife_library_num = 1 - elif "机内刀库" in obj.current_location: - obj.machine_knife_library_num = 1 - - @api.model - def _read_group_mrs_cutting_tool_type_id(self, categories, domain, order): - mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) - return categories.browse(mrs_cutting_tool_type_ids) - - # 整体式刀具型号 - cutting_tool_integral_model_id = fields.Many2one('product.product', string='整体式刀具型号', readonly=True, - domain=[('cutting_tool_material_id', '=', '整体式刀具')]) - # 刀片型号 - cutting_tool_blade_model_id = fields.Many2one('product.product', string='刀片型号', readonly=True, - domain=[('cutting_tool_material_id', '=', '刀片')]) - # 刀杆型号 - cutting_tool_cutterbar_model_id = fields.Many2one('product.product', string='刀杆型号', readonly=True, - domain=[('cutting_tool_material_id', '=', '刀杆')]) - # 刀盘型号 - cutting_tool_cutterpad_model_id = fields.Many2one('product.product', string='刀盘型号', readonly=True, - domain=[('cutting_tool_material_id', '=', '刀盘')]) - # 刀柄型号 - cutting_tool_cutterhandle_model_id = fields.Many2one('product.product', string='刀柄型号', readonly=True, - domain=[('cutting_tool_material_id', '=', '刀柄')]) - # 夹头型号 - cutting_tool_cutterhead_model_id = fields.Many2one('product.product', string='夹头型号', readonly=True, - domain=[('cutting_tool_material_id', '=', '夹头')]) - - whether_standard_knife = fields.Boolean(string='是否标准刀', default=True, readonly=True) - L_D_number = fields.Float(string='L/D值(mm)', readonly=True) - hiding_length = fields.Float(string='避空长(mm)', readonly=True) - cut_time = fields.Integer(string='已切削时间(min)', readonly=True) - cut_length = fields.Float(string='已切削长度(mm)', readonly=True) - cut_number = fields.Integer(string='已切削次数', readonly=True) - - suitable_machining_method_ids = fields.Many2many( - 'maintenance.equipment.image', 'rel_machining_product_template_tool_entity', '适合加工方式', - domain=[('type', '=', '加工能力')], compute='_compute_maintenance_equipment_image') - blade_tip_characteristics_id = fields.Many2one( - 'maintenance.equipment.image', '刀尖特征', - domain=[('type', '=', '刀尖特征')]) - handle_type_id = fields.Many2one( - 'maintenance.equipment.image', '柄部类型', - domain=[('type', '=', '柄部类型')]) - cutting_direction_ids = fields.Many2many( - 'maintenance.equipment.image', 'rel_cutting_product_template_tool_entity', '走刀方向', - domain=[('type', '=', '走刀方向')]) - suitable_coolant_ids = fields.Many2many( - 'maintenance.equipment.image', 'rel_coolants_product_template_tool_entity', '适合冷却方式', - domain=[('type', '=', '冷却方式')]) - - @api.depends('cutting_tool_integral_model_id', 'cutting_tool_blade_model_id') - def _compute_maintenance_equipment_image(self): - for record in self: - if record.cutting_tool_integral_model_id: - print(record.cutting_tool_integral_model_id) - record.sudo().suitable_machining_method_ids = record.cutting_tool_integral_model_id.suitable_machining_method_ids.ids - record.sudo().blade_tip_characteristics_id = record.cutting_tool_integral_model_id.blade_tip_characteristics_id.id - record.sudo().handle_type_id = record.cutting_tool_integral_model_id.handle_type_id.id - record.sudo().cutting_direction_ids = record.cutting_tool_integral_model_id.cutting_direction_ids.ids - record.sudo().suitable_coolant_ids = record.cutting_tool_integral_model_id.suitable_coolant_ids.ids - print(record.cutting_tool_integral_model_id.blade_tip_characteristics_id.ids) - elif record.cutting_tool_blade_model_id: - record.sudo().suitable_machining_method_ids = record.cutting_tool_blade_model_id.suitable_machining_method_ids.ids - record.sudo().blade_tip_characteristics_id = record.cutting_tool_blade_model_id.blade_tip_characteristics_id.id - record.sudo().handle_type_id = record.cutting_tool_blade_model_id.handle_type_id.id - record.sudo().cutting_direction_ids = record.cutting_tool_blade_model_id.cutting_direction_ids.ids - record.sudo().suitable_coolant_ids = record.cutting_tool_blade_model_id.suitable_coolant_ids.ids - else: - record.sudo().suitable_machining_method_ids = [] - record.sudo().blade_tip_characteristics_id = None - record.sudo().handle_type_id = None - record.sudo().cutting_direction_ids = [] - record.sudo().suitable_coolant_ids = [] - - def _get_functional_tool_model_ids(self, functional_tool_model_code): - functional_tool_model_ids = [] - for item in functional_tool_model_code: - functional_tool_model = self.env['sf.cutting_tool.standard.library'].search([('code', '=', item)]) - functional_tool_model_ids.append(functional_tool_model.id) - return [(6, 0, functional_tool_model_ids)] - - def open_functional_tool_warning(self): - action = self.env.ref('sf_tool_management.action_sf_functional_tool_warning') - result = action.read()[0] - result['domain'] = [('functional_tool_name_id', '=', self.functional_tool_name_id.id)] - return result - - def open_stock_move_line(self): - action = self.env.ref('sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_act') - result = action.read()[0] - result['domain'] = [('lot_id', '=', self.barcode_id.id), ('qty_done', '>', 0)] - return result - - def open_safety_stock(self): - action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act') - result = action.read()[0] - result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter), - ('knife_tip_r_angle', '=', self.knife_tip_r_angle), - ('coarse_middle_thin', '=', self.coarse_middle_thin)] - return result - - def tool_inventory_displacement_out(self): - """ - 机床当前刀库实时信息接口,功能刀具出库 - """ - # 获取位置对象 - location_inventory_id = self.current_location_id - stock_location_id = self.env['stock.location'].search([('name', '=', '制造前')]) - # 创建功能刀具该批次/序列号 库存移动和移动历史 - self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id, - self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id) - - # ==========刀具组接口========== - # def _register_functional_tool_groups(self, obj): - # create_url = '/AutoDeviceApi/ToolGroup' - # sf_sync_config = self.env['res.config.settings'].get_values() - # token = sf_sync_config['token'] - # sf_secret_key = sf_sync_config['sf_secret_key'] - # headers = Common.get_headers(obj, token, sf_secret_key) - # strurl = sf_sync_config['sf_url'] + create_url - # val = { - # 'ToolName': obj.name, - # 'GroupName': obj.tool_groups_id.name, - # 'ToolId': obj.code - # } - # kw = json.dumps(val, ensure_ascii=False) - # r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers) - # ret = r.json() - # if r == 200: - # return "刀具组发送成功" - # else: - # raise ValidationError("刀具组发送失败") - - # @api.model_create_multi - # def create(self, vals): - # obj = super(FunctionalCuttingToolEntity, self).create(vals) - # # 调用刀具组接口 - # self._register_functional_tool_groups(obj) - # return obj - - -class FunctionalToolWarning(models.Model): - _name = 'sf.functional.tool.warning' - _description = '功能刀具预警' - - code = fields.Char('编码', related='functional_tool_name_id.code') - rfid = fields.Char('Rfid', related='functional_tool_name_id.rfid') - tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id') - name = fields.Char('名称', invisible=True, readonly=True, related='functional_tool_name_id.name') - # 机床信息 - production_line_id = fields.Many2one('sf.production.line', string='生产线', - group_expand='_read_group_machine_table_name_ids') - maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='CNC机床') - machine_tool_code = fields.Char(string='机台号', related='maintenance_equipment_id.name') - machine_table_type_id = fields.Many2one('maintenance.equipment.category', string='机床类型', - related='maintenance_equipment_id.category_id') - cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', - domain="[('equipment_id', '=', maintenance_equipment_id)]") - # 功能刀具信息 - functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称') - barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', related='functional_tool_name_id.barcode_id') - mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型') - diameter = fields.Integer(string='刀具直径(mm)') - knife_tip_r_angle = fields.Float(string='刀尖R角(mm)') - # 其他信息 - install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time') - on_board_time = fields.Datetime('上机装刀时间') - max_lifetime_value = fields.Integer(string='最大寿命值(min)') - alarm_value = fields.Integer(string='报警值(min)') - used_value = fields.Integer(string='已使用值(min)') - functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], string='状态') - alarm_time = fields.Datetime('报警时间') - dispose_user = fields.Char('处理人') - dispose_time = fields.Char('处理时间') - dispose_func = fields.Char('处理方法/措施', readonly=False) - - active = fields.Boolean(string='已归档', default=True) - - @api.model - def _read_group_machine_table_name_ids(self, categories, domain, order): - machine_table_name_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) - return categories.browse(machine_table_name_ids) - - def create_tool_warning_record(self, obj): - """ - 机台换刀申请报警状态时,创建功能刀具预警记录 - """ - if obj: - for tool in obj.get('tool_changing_apply_id'): - self.env['sf.functional.tool.warning'].create({ - 'production_line_id': tool.production_line_id.id, - 'maintenance_equipment_id': tool.maintenance_equipment_id.id, - 'machine_tool_code': tool.machine_tool_code, - 'machine_table_type_id': tool.machine_table_type_id.id, - 'cutter_spacing_code_id': tool.cutter_spacing_code_id.id, - 'functional_tool_name_id': tool.functional_tool_name_id.id, - 'barcode_id': tool.barcode_id.id, - 'diameter': tool.diameter, - 'knife_tip_r_angle': tool.knife_tip_r_angle, - 'max_lifetime_value': tool.max_lifetime_value, - 'alarm_value': tool.alarm_value, - 'used_value': tool.used_value, - 'functional_tool_status': tool.functional_tool_status, - 'alarm_time': fields.Datetime.now(), - }) - - -class StockMoveLine(models.Model): - _inherit = 'stock.move.line' - _description = '功能刀具出入库记录' - _order = 'date desc' - - functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称') - functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, - group_expand='_read_group_functional_tool_type_id') - functional_tool_name = fields.Char('刀具名称') - diameter = fields.Integer(string='刀具直径(mm)') - knife_tip_r_angle = fields.Float(string='刀尖R角(mm)') - install_tool_time = fields.Datetime("刀具组装时间", default=fields.Datetime.now()) - code = fields.Char('编码') - rfid = fields.Char('Rfid') - tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组') - - @api.model - def _read_group_functional_tool_type_id(self, categories, domain, order): - names = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) - return categories.browse(names) - - -class RealTimeDistributionOfFunctionalTools(models.Model): - _name = 'sf.real.time.distribution.of.functional.tools' - _description = '功能刀具安全库存' - - name = fields.Char('名称', readonly=True, compute='_compute_name', store=True) - functional_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True) - tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=False, required=True) - sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False, - group_expand='_read_mrs_cutting_tool_type_ids', store=True) - diameter = fields.Integer(string='刀具直径(mm)', readonly=False) - knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False) - tool_stock_num = fields.Integer(string='刀具房数量') - side_shelf_num = fields.Integer(string='线边刀库数量') - on_tool_stock_num = fields.Integer(string='机内刀库数量') - tool_stock_total = fields.Integer(string='当前库存量', readonly=True) - min_stock_num = fields.Integer('最低库存量') - max_stock_num = fields.Integer('最高库存量') - batch_replenishment_num = fields.Integer('批次补货量', readonly=True, compute='_compute_batch_replenishment_num') - unit = fields.Char('单位') - image = fields.Binary('图片', readonly=False) - - coarse_middle_thin = fields.Selection([("1", "粗"), ('2', '中'), ('3', '精')], string='粗/中/精', readonly=False) - whether_standard_knife = fields.Boolean(string='是否标准刀', default=True, readonly=False) - # 能力特征信息 - suitable_machining_method_ids = fields.Many2many( - 'maintenance.equipment.image', 'rel_machining_product_template_distribution', '适合加工方式', - domain=[('type', '=', '加工能力')], - related='sf_functional_cutting_tool_entity_ids.suitable_machining_method_ids') - blade_tip_characteristics_id = fields.Many2one( - 'maintenance.equipment.image', '刀尖特征', - domain=[('type', '=', '刀尖特征')], - related='sf_functional_cutting_tool_entity_ids.blade_tip_characteristics_id') - handle_type_id = fields.Many2one( - 'maintenance.equipment.image', '柄部类型', - domain=[('type', '=', '柄部类型')], related='sf_functional_cutting_tool_entity_ids.handle_type_id') - cutting_direction_ids = fields.Many2many( - 'maintenance.equipment.image', 'rel_cutting_product_template_distribution', '走刀方向', - domain=[('type', '=', '走刀方向')], related='sf_functional_cutting_tool_entity_ids.cutting_direction_ids') - suitable_coolant_ids = fields.Many2many( - 'maintenance.equipment.image', 'rel_coolants_product_template_distribution', '适合冷却方式', - domain=[('type', '=', '冷却方式')], related='sf_functional_cutting_tool_entity_ids.suitable_coolant_ids') - - sf_functional_cutting_tool_entity_ids = fields.Many2many('sf.functional.cutting.tool.entity', - 'sf_functional_cutting_tool_entity_ref', - string='功能刀具列表信息', readonly=True) - - sf_functional_tool_assembly_ids = fields.Many2many('sf.functional.tool.assembly', 'sf_functional_tool_assembly_ref', - '功能刀具组装单', readonly=True) - - active = fields.Boolean(string='已归档', default=True) - - @api.depends('functional_name_id') - def _compute_name(self): - for obj in self: - if obj.tool_groups_id: - obj.name = obj.functional_name_id.name - else: - obj.sudo().name = '' - - @api.constrains('min_stock_num', 'max_stock_num') - def _check_stock_num(self): - for obj in self: - if obj.min_stock_num > obj.max_stock_num: - raise ValidationError('【最低安全库存】不能高于【最高安全库存】!!!') - - @api.model - def _read_mrs_cutting_tool_type_ids(self, categories, domain, order): - mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) - return categories.browse(mrs_cutting_tool_type_ids) - - @api.depends('sf_functional_cutting_tool_entity_ids', 'min_stock_num', 'max_stock_num') - def _compute_batch_replenishment_num(self): - for tool in self: - if tool: - # 判断功能刀具组装单是否已经完成 - tool.sudo().estimate_functional_tool_assembly_ids(tool) - tool.sudo().get_stock_num(tool) - # 计算当前库存量 - tool.sudo().tool_stock_total = tool.tool_stock_num + tool.side_shelf_num + tool.on_tool_stock_num - # 如果当前库存量小于最低库存量,计算批次补货量 - tool.sudo().open_batch_replenishment_num(tool) - - def open_batch_replenishment_num(self, tool): - """ - 计算批次补货量 - """ - if tool.tool_stock_total < tool.min_stock_num: - tool.sudo().batch_replenishment_num = tool.max_stock_num - tool.tool_stock_total - # 根据判断创建功能刀具组装单 - if not tool.sf_functional_tool_assembly_ids and re.match(r'^\d+$', str(tool.id)): - for i in range(tool.batch_replenishment_num): - tool.sudo().create_functional_tool_assembly(tool) - print(i, ": ", tool.sf_functional_tool_assembly_ids) - else: - tool.sudo().batch_replenishment_num = 0 - - def create_functional_tool_assembly(self, tool): - """ - 创建功能刀具组装单 - """ - functional_tool_assembly = tool.env['sf.functional.tool.assembly'].sudo().create({ - 'functional_tool_name': tool.name, - 'functional_tool_type_id': tool.sf_cutting_tool_type_id.id, - 'tool_groups_id': tool.tool_groups_id.id, - 'functional_tool_diameter': tool.diameter, - 'knife_tip_r_angle': tool.knife_tip_r_angle, - 'coarse_middle_thin': tool.coarse_middle_thin, - 'loading_task_source': '2', - 'use_tool_time': fields.Datetime.now() + timedelta(hours=4), - 'applicant': '系统自动', - 'apply_time': fields.Datetime.now(), - 'whether_standard_knife': tool.whether_standard_knife, - 'reason_for_applying': '安全库存', - }) - tool.sudo().sf_functional_tool_assembly_ids = [(4, functional_tool_assembly.id)] - - def estimate_functional_tool_assembly_ids(self, tool): - """ - 判断功能刀具组装单是否完成,如果全部完成清空sf_functional_tool_assembly_ids的数据 - """ - for sf_functional_tool_assembly_id in tool.sf_functional_tool_assembly_ids: - if sf_functional_tool_assembly_id.assemble_status == '0': - return False - tool.sudo().sf_functional_tool_assembly_ids = [] - - def get_stock_num(self, tool): - """ - 计算刀具房数量、线边刀库数量、机内刀库数量 - """ - if tool: - tool.tool_stock_num = 0 - tool.side_shelf_num = 0 - tool.on_tool_stock_num = 0 - if tool.sf_functional_cutting_tool_entity_ids: - for cutting_tool in tool.sf_functional_cutting_tool_entity_ids: - if cutting_tool.tool_room_num > 0: - tool.tool_stock_num += 1 - elif cutting_tool.line_edge_knife_library_num > 0: - tool.side_shelf_num += 1 - elif cutting_tool.machine_knife_library_num > 0: - tool.on_tool_stock_num += 1 - - def create_or_edit_safety_stock(self, vals, sf_functional_cutting_tool_entity_ids): - """ - 根据传入的信息新增或者更新功能刀具安全库存的信息 - """ - # 根据功能刀具名称、刀具组、直径或尖刀R角、粗/中/精查询该功能刀具是否已经存在 - record = self.env['sf.real.time.distribution.of.functional.tools'].search( - [('functional_name_id', '=', vals['functional_name_id']), - ('sf_cutting_tool_type_id', '=', vals['sf_cutting_tool_type_id']), - ('diameter', '=', vals['diameter']), ('knife_tip_r_angle', '=', vals['knife_tip_r_angle']), - ('coarse_middle_thin', '=', vals['coarse_middle_thin']), ('tool_groups_id', '=', vals['tool_groups_id'])]) - if len(record) > 0: - for obj in record: - obj.write({'sf_functional_cutting_tool_entity_ids': [(4, sf_functional_cutting_tool_entity_ids.id)]}) - else: - vals['sf_functional_cutting_tool_entity_ids'] = sf_functional_cutting_tool_entity_ids.ids - self.env['sf.real.time.distribution.of.functional.tools'].create(vals) - - status_create = fields.Boolean('是否是新增状态', default=True) - - @api.model_create_multi - def create(self, vals_list): - for vals in vals_list: - vals['status_create'] = False - records = super(RealTimeDistributionOfFunctionalTools, self).create(vals_list) - return records class MachineTableToolChangingApply(models.Model): @@ -819,12 +350,12 @@ class FunctionalToolAssembly(models.Model): @api.depends('functional_tool_name') def _compute_name(self): for obj in self: - obj.name = obj.after_assembly_functional_tool_name + 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) - name = fields.Char(string='名称', readonly=True, compute='_compute_name', store=True) + 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) @@ -864,56 +395,137 @@ class FunctionalToolAssembly(models.Model): return categories.browse(functional_tool_type_ids) # 刀具物料信息 - # 整体式刀具型号 - integral_code_id = fields.Many2one('stock.lot', string='整体式刀具序列号', readonly=True) + # ==============整体式刀具型号============ + integral_freight_barcode = fields.Char('整体式刀具货位') + 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='整体式刀具型号', - related='integral_code_id.product_id.cutting_tool_model_id') - integral_name = fields.Char('整体式刀具名称', related='integral_code_id.product_id.name') + related='integral_product_id.cutting_tool_model_id') integral_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='整体式刀具规格', - related='integral_code_id.product_id.specification_id') + related='integral_product_id.specification_id') sf_tool_brand_id_1 = fields.Many2one('sf.machine.brand', string='整体式刀具品牌', - related='integral_code_id.product_id.brand_id') - # 刀片型号 - blade_code_id = fields.Many2one('stock.lot', '刀片序列号', readonly=True) + related='integral_product_id.brand_id') + + @api.depends('integral_freight_barcode') + def _compute_integral_product_id(self): + for item in self: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.integral_freight_barcode)]) + if location: + item.integral_product_id = location.product_id.id + else: + item.integral_product_id = False + + # =================刀片型号============= + blade_freight_barcode = fields.Char('刀片货位') + 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='刀片型号', - related='blade_code_id.product_id.cutting_tool_model_id') - blade_name = fields.Char('刀片名称', related='blade_code_id.product_id.name') + related='blade_product_id.cutting_tool_model_id') blade_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀片规格', - related='blade_code_id.product_id.specification_id') - sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_code_id.product_id.brand_id') - # 刀杆型号 - bar_code_id = fields.Many2one('stock.lot', '刀杆序列号', readonly=True) + related='blade_product_id.specification_id') + sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_product_id.brand_id') + + @api.depends('blade_freight_barcode') + def _compute_blade_product_id(self): + for item in self: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.blade_freight_barcode)]) + if location: + item.blade_product_id = location.product_id.id + else: + item.blade_product_id = False + + # ==============刀杆型号================ + bar_freight_barcode = fields.Char('刀杆货位') + 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='刀杆型号', - related='bar_code_id.product_id.cutting_tool_model_id') - bar_name = fields.Char('刀杆名称', related='bar_code_id.product_id.name') + related='bar_product_id.cutting_tool_model_id') bar_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀杆规格', - related='bar_code_id.product_id.specification_id') - sf_tool_brand_id_3 = fields.Many2one('sf.machine.brand', '刀杆品牌', related='bar_code_id.product_id.brand_id') - # 刀盘型号 - pad_code_id = fields.Many2one('stock.lot', '刀盘序列号', readonly=True) + related='bar_product_id.specification_id') + sf_tool_brand_id_3 = fields.Many2one('sf.machine.brand', '刀杆品牌', related='bar_product_id.brand_id') + + @api.depends('bar_freight_barcode') + def _compute_bar_product_id(self): + for item in self: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.bar_freight_barcode)]) + if location: + item.bar_product_id = location.product_id.id + else: + item.bar_product_id = False + + # =============刀盘型号================ + pad_freight_barcode = fields.Char('刀盘货位') + 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='刀盘型号', - related='pad_code_id.product_id.cutting_tool_model_id') - pad_name = fields.Char('刀盘名称', related='pad_code_id.product_id.name') + related='pad_product_id.cutting_tool_model_id') pad_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀盘规格', - related='pad_code_id.product_id.specification_id') - sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_code_id.product_id.brand_id') - # 刀柄型号 - handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', readonly=True) + related='pad_product_id.specification_id') + sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_product_id.brand_id') + + @api.depends('pad_freight_barcode') + def _compute_pad_product_id(self): + for item in self: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.pad_freight_barcode)]) + if location: + item.pad_product_id = location.product_id.id + else: + item.pad_product_id = False + + # ==============刀柄型号============== + handle_freight_barcode = fields.Char('刀柄货位') + handle_code_id = fields.Many2one('stock.lot', '刀柄序列号', compute='_compute_handle_product_id', store=True) + handle_product_id = fields.Many2one('product.product', string='刀柄名称', compute='_compute_handle_product_id', + store=True) cutting_tool_cutterhandle_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀柄型号', related='handle_code_id.product_id.cutting_tool_model_id') - handle_name = fields.Char('刀柄名称', related='handle_code_id.product_id.name') handle_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='刀柄规格', related='handle_code_id.product_id.specification_id') sf_tool_brand_id_5 = fields.Many2one('sf.machine.brand', '刀柄品牌', related='handle_code_id.product_id.brand_id') - # 夹头型号 - chuck_code_id = fields.Many2one('stock.lot', '夹头序列号', readonly=True) - cutting_tool_cutterhead_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='夹头型号', - related='chuck_code_id.product_id.cutting_tool_model_id') - chuck_name = fields.Char('夹头名称', related='chuck_code_id.product_id.name') - chuck_specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='夹头规格', - related='chuck_code_id.product_id.specification_id') - sf_tool_brand_id_6 = fields.Many2one('sf.machine.brand', '夹头品牌', related='chuck_code_id.product_id.brand_id') + @api.depends('handle_freight_barcode') + def _compute_handle_product_id(self): + for item in self: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.handle_freight_barcode)]) + if location: + item.handle_code_id = location.product_sn_id.id + item.handle_product_id = location.product_id.id + else: + item.handle_code_id = False + item.handle_product_id = False + + # ==============夹头型号============== + chuck_freight_barcode = fields.Char('夹头货位') + 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='夹头型号', + related='chuck_product_id.cutting_tool_model_id') + 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') + + @api.depends('chuck_freight_barcode') + def _compute_chuck_product_id(self): + for item in self: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', item.chuck_freight_barcode)]) + if location: + item.chuck_product_id = location.product_id.id + else: + item.chuck_product_id = False + + # ==================待删除字段================== + blade_name = fields.Char('') + integral_name = fields.Char('') + blade_code_id = fields.Many2one('stock.lot', '刀片序列号') + integral_code_id = fields.Many2one('stock.lot', '整体式刀具序列号') + bar_code_id = fields.Many2one('stock.lot', '刀杆序列号') + bar_name = fields.Char('') + pad_code_id = fields.Many2one('stock.lot', '刀盘序列号') + pad_name = fields.Char('') + handle_name = fields.Char('') + chuck_code_id = fields.Many2one('stock.lot', '夹头序列号') + chuck_name = fields.Char('') + # ============================================== # 组装功能刀具参数信息 barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True) after_assembly_functional_tool_name = fields.Char(string='组装后功能刀具名称', readonly=True) diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py new file mode 100644 index 00000000..334511a6 --- /dev/null +++ b/sf_tool_management/models/functional_tool.py @@ -0,0 +1,488 @@ +# -*- coding: utf-8 -*- +import re +from datetime import timedelta +from odoo import SUPERUSER_ID +from odoo import fields, models, api +from odoo.exceptions import ValidationError + + +class FunctionalCuttingToolEntity(models.Model): + _name = 'sf.functional.cutting.tool.entity' + _description = '功能刀具列表' + + functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True) + + @api.depends('functional_tool_name_id') + def _compute_name(self): + for item in self: + if item.functional_tool_name_id: + name = item.functional_tool_name_id.after_assembly_functional_tool_name + inventory = self.env['sf.tool.inventory'].sudo().search([('name', '=', name)]) + item.name = name + if inventory: + item.tool_name_id = inventory.id + else: + item.tool_name_id = False + else: + item.name = '' + item.tool_name_id = False + + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id') + code = fields.Char('编码') + rfid = fields.Char('Rfid', readonly=True) + name = fields.Char('名称', compute='_compute_name', stroe=True) + tool_name_id = fields.Many2one('sf.tool.inventory', '功能刀具名称', compute='_compute_name', store=True) + sf_cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='刀具型号') + barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', readonly=True) + sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', + group_expand='_read_group_mrs_cutting_tool_type_id', compute_sudo=True) + + functional_tool_diameter = fields.Integer(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) + new_former = fields.Selection([('0', '新'), ('1', '旧')], string='新/旧', readonly=True) + 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) + tool_room_num = fields.Integer(string='刀具房数量', readonly=True) + line_edge_knife_library_num = fields.Integer(string='线边刀库数量', readonly=True) + machine_knife_library_num = fields.Integer(string='机内刀库数量', readonly=True) + max_lifetime_value = fields.Integer(string='最大寿命值(min)', readonly=True) + alarm_value = fields.Integer(string='报警值(min)', readonly=True) + used_value = fields.Integer(string='已使用值(min)', readonly=True) + functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], + string='状态', store=True, default='正常') + current_location_id = fields.Many2one('stock.location', string='当前位置', readonly=True) + current_location = fields.Selection( + [('组装后', '组装后'), ('刀具房', '刀具房'), ('线边刀库', '线边刀库'), ('机内刀库', '机内刀库')], + string='位置', compute='_compute_current_location_id', store=True) + image = fields.Binary('图片', readonly=True) + + active = fields.Boolean(string='已归档', default=True) + + @api.depends('barcode_id.quant_ids') + def _compute_current_location_id(self): + for record in self: + if record.barcode_id.quant_ids: + for quant_id in record.barcode_id.quant_ids: + if quant_id.inventory_quantity_auto_apply > 0: + record.current_location_id = quant_id.location_id + if quant_id.location_id.name == '制造前': + record.current_location = '机内刀库' + else: + record.current_location = quant_id.location_id.name + if record.current_location_id: + record.sudo().get_location_num() + else: + record.current_location_id = False + record.current_location = False + + def get_location_num(self): + """ + 计算库存位置数量 + """ + for obj in self: + if obj.current_location_id: + obj.tool_room_num = 0 + obj.line_edge_knife_library_num = 0 + obj.machine_knife_library_num = 0 + if obj.current_location in ['刀具房']: + obj.tool_room_num = 1 + elif "线边刀库" in obj.current_location: + obj.line_edge_knife_library_num = 1 + elif "机内刀库" in obj.current_location: + obj.machine_knife_library_num = 1 + + @api.model + def _read_group_mrs_cutting_tool_type_id(self, categories, domain, order): + mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) + return categories.browse(mrs_cutting_tool_type_ids) + + # 整体式刀具型号 + cutting_tool_integral_model_id = fields.Many2one('product.product', string='整体式刀具型号', readonly=True, + domain=[('cutting_tool_material_id', '=', '整体式刀具')]) + # 刀片型号 + cutting_tool_blade_model_id = fields.Many2one('product.product', string='刀片型号', readonly=True, + domain=[('cutting_tool_material_id', '=', '刀片')]) + # 刀杆型号 + cutting_tool_cutterbar_model_id = fields.Many2one('product.product', string='刀杆型号', readonly=True, + domain=[('cutting_tool_material_id', '=', '刀杆')]) + # 刀盘型号 + cutting_tool_cutterpad_model_id = fields.Many2one('product.product', string='刀盘型号', readonly=True, + domain=[('cutting_tool_material_id', '=', '刀盘')]) + # 刀柄型号 + cutting_tool_cutterhandle_model_id = fields.Many2one('product.product', string='刀柄型号', readonly=True, + domain=[('cutting_tool_material_id', '=', '刀柄')]) + # 夹头型号 + cutting_tool_cutterhead_model_id = fields.Many2one('product.product', string='夹头型号', readonly=True, + domain=[('cutting_tool_material_id', '=', '夹头')]) + + whether_standard_knife = fields.Boolean(string='是否标准刀', default=True, readonly=True) + L_D_number = fields.Float(string='L/D值(mm)', readonly=True) + hiding_length = fields.Float(string='避空长(mm)', readonly=True) + cut_time = fields.Integer(string='已切削时间(min)', readonly=True) + cut_length = fields.Float(string='已切削长度(mm)', readonly=True) + cut_number = fields.Integer(string='已切削次数', readonly=True) + + suitable_machining_method_ids = fields.Many2many( + 'maintenance.equipment.image', 'rel_machining_product_template_tool_entity', '适合加工方式', + domain=[('type', '=', '加工能力')], compute='_compute_maintenance_equipment_image') + blade_tip_characteristics_id = fields.Many2one( + 'maintenance.equipment.image', '刀尖特征', + domain=[('type', '=', '刀尖特征')]) + handle_type_id = fields.Many2one( + 'maintenance.equipment.image', '柄部类型', + domain=[('type', '=', '柄部类型')]) + cutting_direction_ids = fields.Many2many( + 'maintenance.equipment.image', 'rel_cutting_product_template_tool_entity', '走刀方向', + domain=[('type', '=', '走刀方向')]) + suitable_coolant_ids = fields.Many2many( + 'maintenance.equipment.image', 'rel_coolants_product_template_tool_entity', '适合冷却方式', + domain=[('type', '=', '冷却方式')]) + + @api.depends('cutting_tool_integral_model_id', 'cutting_tool_blade_model_id') + def _compute_maintenance_equipment_image(self): + for record in self: + if record.cutting_tool_integral_model_id: + print(record.cutting_tool_integral_model_id) + record.sudo().suitable_machining_method_ids = record.cutting_tool_integral_model_id.suitable_machining_method_ids.ids + record.sudo().blade_tip_characteristics_id = record.cutting_tool_integral_model_id.blade_tip_characteristics_id.id + record.sudo().handle_type_id = record.cutting_tool_integral_model_id.handle_type_id.id + record.sudo().cutting_direction_ids = record.cutting_tool_integral_model_id.cutting_direction_ids.ids + record.sudo().suitable_coolant_ids = record.cutting_tool_integral_model_id.suitable_coolant_ids.ids + print(record.cutting_tool_integral_model_id.blade_tip_characteristics_id.ids) + elif record.cutting_tool_blade_model_id: + record.sudo().suitable_machining_method_ids = record.cutting_tool_blade_model_id.suitable_machining_method_ids.ids + record.sudo().blade_tip_characteristics_id = record.cutting_tool_blade_model_id.blade_tip_characteristics_id.id + record.sudo().handle_type_id = record.cutting_tool_blade_model_id.handle_type_id.id + record.sudo().cutting_direction_ids = record.cutting_tool_blade_model_id.cutting_direction_ids.ids + record.sudo().suitable_coolant_ids = record.cutting_tool_blade_model_id.suitable_coolant_ids.ids + else: + record.sudo().suitable_machining_method_ids = [] + record.sudo().blade_tip_characteristics_id = None + record.sudo().handle_type_id = None + record.sudo().cutting_direction_ids = [] + record.sudo().suitable_coolant_ids = [] + + def _get_functional_tool_model_ids(self, functional_tool_model_code): + functional_tool_model_ids = [] + for item in functional_tool_model_code: + functional_tool_model = self.env['sf.cutting_tool.standard.library'].search([('code', '=', item)]) + functional_tool_model_ids.append(functional_tool_model.id) + return [(6, 0, functional_tool_model_ids)] + + def open_functional_tool_warning(self): + action = self.env.ref('sf_tool_management.action_sf_functional_tool_warning') + result = action.read()[0] + result['domain'] = [('functional_tool_name_id', '=', self.functional_tool_name_id.id)] + return result + + def open_stock_move_line(self): + action = self.env.ref('sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_act') + result = action.read()[0] + result['domain'] = [('lot_id', '=', self.barcode_id.id), ('qty_done', '>', 0)] + return result + + def open_safety_stock(self): + action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act') + result = action.read()[0] + result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter), + ('knife_tip_r_angle', '=', self.knife_tip_r_angle), + ('coarse_middle_thin', '=', self.coarse_middle_thin)] + return result + + def tool_inventory_displacement_out(self): + """ + 机床当前刀库实时信息接口,功能刀具出库 + """ + # 获取位置对象 + location_inventory_id = self.current_location_id + stock_location_id = self.env['stock.location'].search([('name', '=', '制造前')]) + # 创建功能刀具该批次/序列号 库存移动和移动历史 + self.barcode_id.create_stock_quant(location_inventory_id, stock_location_id, + self.functional_tool_name_id.id, '机床装刀', self.functional_tool_name_id) + + # ==========刀具组接口========== + # def _register_functional_tool_groups(self, obj): + # create_url = '/AutoDeviceApi/ToolGroup' + # sf_sync_config = self.env['res.config.settings'].get_values() + # token = sf_sync_config['token'] + # sf_secret_key = sf_sync_config['sf_secret_key'] + # headers = Common.get_headers(obj, token, sf_secret_key) + # strurl = sf_sync_config['sf_url'] + create_url + # val = { + # 'ToolName': obj.name, + # 'GroupName': obj.tool_groups_id.name, + # 'ToolId': obj.code + # } + # kw = json.dumps(val, ensure_ascii=False) + # r = requests.post(strurl, json={}, data={'kw': kw, 'token': token}, headers=headers) + # ret = r.json() + # if r == 200: + # return "刀具组发送成功" + # else: + # raise ValidationError("刀具组发送失败") + + # @api.model_create_multi + # def create(self, vals): + # obj = super(FunctionalCuttingToolEntity, self).create(vals) + # # 调用刀具组接口 + # self._register_functional_tool_groups(obj) + # return obj + + +class FunctionalToolWarning(models.Model): + _name = 'sf.functional.tool.warning' + _description = '功能刀具预警' + + code = fields.Char('编码', related='functional_tool_name_id.code') + rfid = fields.Char('Rfid', related='functional_tool_name_id.rfid') + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id') + name = fields.Char('名称', invisible=True, readonly=True, related='functional_tool_name_id.name') + # 机床信息 + production_line_id = fields.Many2one('sf.production.line', string='生产线', + group_expand='_read_group_machine_table_name_ids') + maintenance_equipment_id = fields.Many2one('maintenance.equipment', string='CNC机床') + machine_tool_code = fields.Char(string='机台号', related='maintenance_equipment_id.name') + machine_table_type_id = fields.Many2one('maintenance.equipment.category', string='机床类型', + related='maintenance_equipment_id.category_id') + cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', + domain="[('equipment_id', '=', maintenance_equipment_id)]") + # 功能刀具信息 + functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称') + barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', related='functional_tool_name_id.barcode_id') + mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型') + diameter = fields.Integer(string='刀具直径(mm)') + knife_tip_r_angle = fields.Float(string='刀尖R角(mm)') + # 其他信息 + install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time') + on_board_time = fields.Datetime('上机装刀时间') + max_lifetime_value = fields.Integer(string='最大寿命值(min)') + alarm_value = fields.Integer(string='报警值(min)') + used_value = fields.Integer(string='已使用值(min)') + functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], string='状态') + alarm_time = fields.Datetime('报警时间') + dispose_user = fields.Char('处理人') + dispose_time = fields.Char('处理时间') + dispose_func = fields.Char('处理方法/措施', readonly=False) + + active = fields.Boolean(string='已归档', default=True) + + @api.model + def _read_group_machine_table_name_ids(self, categories, domain, order): + machine_table_name_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) + return categories.browse(machine_table_name_ids) + + def create_tool_warning_record(self, obj): + """ + 机台换刀申请报警状态时,创建功能刀具预警记录 + """ + if obj: + for tool in obj.get('tool_changing_apply_id'): + self.env['sf.functional.tool.warning'].create({ + 'production_line_id': tool.production_line_id.id, + 'maintenance_equipment_id': tool.maintenance_equipment_id.id, + 'machine_tool_code': tool.machine_tool_code, + 'machine_table_type_id': tool.machine_table_type_id.id, + 'cutter_spacing_code_id': tool.cutter_spacing_code_id.id, + 'functional_tool_name_id': tool.functional_tool_name_id.id, + 'barcode_id': tool.barcode_id.id, + 'diameter': tool.diameter, + 'knife_tip_r_angle': tool.knife_tip_r_angle, + 'max_lifetime_value': tool.max_lifetime_value, + 'alarm_value': tool.alarm_value, + 'used_value': tool.used_value, + 'functional_tool_status': tool.functional_tool_status, + 'alarm_time': fields.Datetime.now(), + }) + + +class StockMoveLine(models.Model): + _inherit = 'stock.move.line' + _description = '功能刀具出入库记录' + _order = 'date desc' + + functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称') + functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, + group_expand='_read_group_functional_tool_type_id') + functional_tool_name = fields.Char('刀具名称') + diameter = fields.Integer(string='刀具直径(mm)') + knife_tip_r_angle = fields.Float(string='刀尖R角(mm)') + install_tool_time = fields.Datetime("刀具组装时间", default=fields.Datetime.now()) + code = fields.Char('编码') + rfid = fields.Char('Rfid') + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组') + + @api.model + def _read_group_functional_tool_type_id(self, categories, domain, order): + names = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) + return categories.browse(names) + + +class RealTimeDistributionOfFunctionalTools(models.Model): + _name = 'sf.real.time.distribution.of.functional.tools' + _description = '功能刀具安全库存' + + name = fields.Char('名称', readonly=True, compute='_compute_name', store=True) + functional_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True) + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=False, required=True) + sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False, + group_expand='_read_mrs_cutting_tool_type_ids', store=True) + diameter = fields.Integer(string='刀具直径(mm)', readonly=False) + knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False) + tool_stock_num = fields.Integer(string='刀具房数量') + side_shelf_num = fields.Integer(string='线边刀库数量') + on_tool_stock_num = fields.Integer(string='机内刀库数量') + tool_stock_total = fields.Integer(string='当前库存量', readonly=True) + min_stock_num = fields.Integer('最低库存量') + max_stock_num = fields.Integer('最高库存量') + batch_replenishment_num = fields.Integer('批次补货量', readonly=True, compute='_compute_batch_replenishment_num') + unit = fields.Char('单位') + image = fields.Binary('图片', readonly=False) + + coarse_middle_thin = fields.Selection([("1", "粗"), ('2', '中'), ('3', '精')], string='粗/中/精', readonly=False) + whether_standard_knife = fields.Boolean(string='是否标准刀', default=True, readonly=False) + # 能力特征信息 + suitable_machining_method_ids = fields.Many2many( + 'maintenance.equipment.image', 'rel_machining_product_template_distribution', '适合加工方式', + domain=[('type', '=', '加工能力')], + related='sf_functional_cutting_tool_entity_ids.suitable_machining_method_ids') + blade_tip_characteristics_id = fields.Many2one( + 'maintenance.equipment.image', '刀尖特征', + domain=[('type', '=', '刀尖特征')], + related='sf_functional_cutting_tool_entity_ids.blade_tip_characteristics_id') + handle_type_id = fields.Many2one( + 'maintenance.equipment.image', '柄部类型', + domain=[('type', '=', '柄部类型')], related='sf_functional_cutting_tool_entity_ids.handle_type_id') + cutting_direction_ids = fields.Many2many( + 'maintenance.equipment.image', 'rel_cutting_product_template_distribution', '走刀方向', + domain=[('type', '=', '走刀方向')], related='sf_functional_cutting_tool_entity_ids.cutting_direction_ids') + suitable_coolant_ids = fields.Many2many( + 'maintenance.equipment.image', 'rel_coolants_product_template_distribution', '适合冷却方式', + domain=[('type', '=', '冷却方式')], related='sf_functional_cutting_tool_entity_ids.suitable_coolant_ids') + + sf_functional_cutting_tool_entity_ids = fields.Many2many('sf.functional.cutting.tool.entity', + 'sf_functional_cutting_tool_entity_ref', + string='功能刀具列表信息', readonly=True) + + sf_functional_tool_assembly_ids = fields.Many2many('sf.functional.tool.assembly', 'sf_functional_tool_assembly_ref', + '功能刀具组装单', readonly=True) + + active = fields.Boolean(string='已归档', default=True) + + @api.depends('functional_name_id') + def _compute_name(self): + for obj in self: + if obj.tool_groups_id: + obj.name = obj.functional_name_id.name + else: + obj.sudo().name = '' + + @api.constrains('min_stock_num', 'max_stock_num') + def _check_stock_num(self): + for obj in self: + if obj.min_stock_num > obj.max_stock_num: + raise ValidationError('【最低安全库存】不能高于【最高安全库存】!!!') + + @api.model + def _read_mrs_cutting_tool_type_ids(self, categories, domain, order): + mrs_cutting_tool_type_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) + return categories.browse(mrs_cutting_tool_type_ids) + + @api.depends('sf_functional_cutting_tool_entity_ids', 'min_stock_num', 'max_stock_num') + def _compute_batch_replenishment_num(self): + for tool in self: + if tool: + # 判断功能刀具组装单是否已经完成 + tool.sudo().estimate_functional_tool_assembly_ids(tool) + tool.sudo().get_stock_num(tool) + # 计算当前库存量 + tool.sudo().tool_stock_total = tool.tool_stock_num + tool.side_shelf_num + tool.on_tool_stock_num + # 如果当前库存量小于最低库存量,计算批次补货量 + tool.sudo().open_batch_replenishment_num(tool) + + def open_batch_replenishment_num(self, tool): + """ + 计算批次补货量 + """ + if tool.tool_stock_total < tool.min_stock_num: + tool.sudo().batch_replenishment_num = tool.max_stock_num - tool.tool_stock_total + # 根据判断创建功能刀具组装单 + if not tool.sf_functional_tool_assembly_ids and re.match(r'^\d+$', str(tool.id)): + for i in range(tool.batch_replenishment_num): + tool.sudo().create_functional_tool_assembly(tool) + print(i, ": ", tool.sf_functional_tool_assembly_ids) + else: + tool.sudo().batch_replenishment_num = 0 + + def create_functional_tool_assembly(self, tool): + """ + 创建功能刀具组装单 + """ + functional_tool_assembly = tool.env['sf.functional.tool.assembly'].sudo().create({ + 'functional_tool_name': tool.name, + 'functional_tool_type_id': tool.sf_cutting_tool_type_id.id, + 'tool_groups_id': tool.tool_groups_id.id, + 'functional_tool_diameter': tool.diameter, + 'knife_tip_r_angle': tool.knife_tip_r_angle, + 'coarse_middle_thin': tool.coarse_middle_thin, + 'loading_task_source': '2', + 'use_tool_time': fields.Datetime.now() + timedelta(hours=4), + 'applicant': '系统自动', + 'apply_time': fields.Datetime.now(), + 'whether_standard_knife': tool.whether_standard_knife, + 'reason_for_applying': '安全库存', + }) + tool.sudo().sf_functional_tool_assembly_ids = [(4, functional_tool_assembly.id)] + + def estimate_functional_tool_assembly_ids(self, tool): + """ + 判断功能刀具组装单是否完成,如果全部完成清空sf_functional_tool_assembly_ids的数据 + """ + for sf_functional_tool_assembly_id in tool.sf_functional_tool_assembly_ids: + if sf_functional_tool_assembly_id.assemble_status == '0': + return False + tool.sudo().sf_functional_tool_assembly_ids = [] + + def get_stock_num(self, tool): + """ + 计算刀具房数量、线边刀库数量、机内刀库数量 + """ + if tool: + tool.tool_stock_num = 0 + tool.side_shelf_num = 0 + tool.on_tool_stock_num = 0 + if tool.sf_functional_cutting_tool_entity_ids: + for cutting_tool in tool.sf_functional_cutting_tool_entity_ids: + if cutting_tool.tool_room_num > 0: + tool.tool_stock_num += 1 + elif cutting_tool.line_edge_knife_library_num > 0: + tool.side_shelf_num += 1 + elif cutting_tool.machine_knife_library_num > 0: + tool.on_tool_stock_num += 1 + + def create_or_edit_safety_stock(self, vals, sf_functional_cutting_tool_entity_ids): + """ + 根据传入的信息新增或者更新功能刀具安全库存的信息 + """ + # 根据功能刀具名称、刀具组、直径或尖刀R角、粗/中/精查询该功能刀具是否已经存在 + record = self.env['sf.real.time.distribution.of.functional.tools'].search( + [('functional_name_id', '=', vals['functional_name_id']), + ('sf_cutting_tool_type_id', '=', vals['sf_cutting_tool_type_id']), + ('diameter', '=', vals['diameter']), ('knife_tip_r_angle', '=', vals['knife_tip_r_angle']), + ('coarse_middle_thin', '=', vals['coarse_middle_thin']), ('tool_groups_id', '=', vals['tool_groups_id'])]) + if len(record) > 0: + for obj in record: + obj.write({'sf_functional_cutting_tool_entity_ids': [(4, sf_functional_cutting_tool_entity_ids.id)]}) + else: + vals['sf_functional_cutting_tool_entity_ids'] = sf_functional_cutting_tool_entity_ids.ids + self.env['sf.real.time.distribution.of.functional.tools'].create(vals) + + status_create = fields.Boolean('是否是新增状态', default=True) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + vals['status_create'] = False + records = super(RealTimeDistributionOfFunctionalTools, self).create(vals_list) + return records diff --git a/sf_tool_management/models/tool_material_search.py b/sf_tool_management/models/tool_material_search.py index 75fc02e0..1338d758 100644 --- a/sf_tool_management/models/tool_material_search.py +++ b/sf_tool_management/models/tool_material_search.py @@ -27,28 +27,49 @@ class ToolMaterial(models.Model): barcode_ids = fields.One2many('stock.lot', 'tool_material_search_id', string='序列号', readonly=True) - @api.depends('barcode_ids') + @api.depends('product_id') def _compute_number(self): for record in self: usable_num = 0 have_been_used_num = 0 scrap_num = 0 - if record.barcode_ids: - record.number = len(record.barcode_ids) - for barcode_id in record.barcode_ids: - if barcode_id.quant_ids: - if barcode_id.quant_ids[-1].location_id.name == '刀具组装位置': - have_been_used_num = have_been_used_num + 1 - else: - usable_num = usable_num + 1 - record.usable_num = usable_num - record.have_been_used_num = have_been_used_num - record.scrap_num = scrap_num - else: - record.number = 0 - record.usable_num = 0 - record.have_been_used_num = 0 - record.scrap_num = 0 + for quant in record.product_id.stock_quant_ids: + location = quant.location_id.name + if location == '刀具房': + usable_num += quant.quantity + elif location == '刀具组装位置': + have_been_used_num += quant.quantity + elif location == '进货': + pass + elif location != 'Vendors': + scrap_num += quant.quantity + record.usable_num = usable_num + record.have_been_used_num = have_been_used_num + record.scrap_num = scrap_num + record.number = usable_num + have_been_used_num + scrap_num + + # @api.depends('barcode_ids') + # def _compute_number(self): + # for record in self: + # usable_num = 0 + # have_been_used_num = 0 + # scrap_num = 0 + # if record.barcode_ids: + # record.number = len(record.barcode_ids) + # for barcode_id in record.barcode_ids: + # if barcode_id.quant_ids: + # if barcode_id.quant_ids[-1].location_id.name == '刀具组装位置': + # have_been_used_num = have_been_used_num + 1 + # else: + # usable_num = usable_num + 1 + # record.usable_num = usable_num + # record.have_been_used_num = have_been_used_num + # record.scrap_num = scrap_num + # else: + # record.number = 0 + # record.usable_num = 0 + # record.have_been_used_num = 0 + # record.scrap_num = 0 @api.model def _read_group_cutting_tool_material_id(self, categories, domain, order): diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml new file mode 100644 index 00000000..1e30dc41 --- /dev/null +++ b/sf_tool_management/views/functional_tool_views.xml @@ -0,0 +1,511 @@ + + + + + + sf.functional.cutting.tool.entity.list.tree + sf.functional.cutting.tool.entity + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sf.functional.cutting.tool.entity.list.form + sf.functional.cutting.tool.entity + +
+
+ + + +
+ +
+ + + +
+
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + sf.functional.cutting.tool.entity.list.tree + sf.functional.cutting.tool.entity + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 功能刀具列表 + ir.actions.act_window + sf.functional.cutting.tool.entity + tree,form,search + + + + + + sf.functional.tool.warning.tree + sf.functional.tool.warning + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sf.functional.tool.warning.search + sf.functional.tool.warning + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 功能刀具预警 + ir.actions.act_window + sf.functional.tool.warning + tree,search + + + + + + 功能刀具安全库存 + sf.real.time.distribution.of.functional.tools + + + + + + + + + + + + + + + + + + + + + + + 功能刀具安全库存 + sf.real.time.distribution.of.functional.tools + +
+ + + + + +
+

+ +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + 功能刀具安全库存 + sf.real.time.distribution.of.functional.tools + + + + + + + + + + + + + + + + + + + + + + + + 功能刀具安全库存 + ir.actions.act_window + sf.real.time.distribution.of.functional.tools + tree,form,search + + + + + + + 功能刀具出入库记录 + stock.move.line + + + + + + + + + + + + + + + + + + + + + + 功能刀具出入库记录 + stock.move.line + + + + + + + + + + + + + + + + + + + + + + + 功能刀具出入库记录 + ir.actions.act_window + stock.move.line + tree,search + + + [('functional_tool_name_id', '!=', False)] + + +
+
\ 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 4dd23a4e..68b6c9a4 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -1,513 +1,6 @@ - - - sf.functional.cutting.tool.entity.list.tree - sf.functional.cutting.tool.entity - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sf.functional.cutting.tool.entity.list.form - sf.functional.cutting.tool.entity - -
-
- - - -
- -
- - - -
-
-

- -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - sf.functional.cutting.tool.entity.list.tree - sf.functional.cutting.tool.entity - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 功能刀具列表 - ir.actions.act_window - sf.functional.cutting.tool.entity - tree,form,search - - - - - - sf.functional.tool.warning.tree - sf.functional.tool.warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sf.functional.tool.warning.search - sf.functional.tool.warning - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 功能刀具预警 - ir.actions.act_window - sf.functional.tool.warning - tree,search - - - - - - 功能刀具安全库存 - sf.real.time.distribution.of.functional.tools - - - - - - - - - - - - - - - - - - - - - - - 功能刀具安全库存 - sf.real.time.distribution.of.functional.tools - -
- - - - - -
-

- -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - 功能刀具安全库存 - sf.real.time.distribution.of.functional.tools - - - - - - - - - - - - - - - - - - - - - - - - 功能刀具安全库存 - ir.actions.act_window - sf.real.time.distribution.of.functional.tools - tree,form,search - - - - - - - 功能刀具出入库记录 - stock.move.line - - - - - - - - - - - - - - - - - - - - - - 功能刀具出入库记录 - stock.move.line - - - - - - - - - - - - - - - - - - - - - - - 功能刀具出入库记录 - ir.actions.act_window - stock.move.line - tree,search - - - [('functional_tool_name_id', '!=', False)] - - - 机床换刀申请 @@ -998,7 +491,7 @@ 功能刀具组装 sf.functional.tool.assembly -
+
@@ -23,12 +24,21 @@ - - - - - - + + +