From 1e172cb4e35970885e0b76d5cd1cce3fc85514e7 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Tue, 27 Aug 2024 17:24:04 +0800 Subject: [PATCH 001/217] =?UTF-8?q?=E5=A4=84=E7=90=86=E4=BB=A3=E5=8F=91?= =?UTF-8?q?=E8=B4=A7=E4=B8=8B=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_stock/__init__.py | 4 +++ sf_stock/__manifest__.py | 36 +++++++++++++++++++++++++ sf_stock/controllers/__init__.py | 3 +++ sf_stock/controllers/controllers.py | 21 +++++++++++++++ sf_stock/demo/demo.xml | 30 +++++++++++++++++++++ sf_stock/models/__init__.py | 3 +++ sf_stock/models/stock_picking.py | 39 +++++++++++++++++++++++++++ sf_stock/security/ir.model.access.csv | 2 ++ sf_stock/views/stock_picking.xml | 5 ++++ 9 files changed, 143 insertions(+) create mode 100644 sf_stock/__init__.py create mode 100644 sf_stock/__manifest__.py create mode 100644 sf_stock/controllers/__init__.py create mode 100644 sf_stock/controllers/controllers.py create mode 100644 sf_stock/demo/demo.xml create mode 100644 sf_stock/models/__init__.py create mode 100644 sf_stock/models/stock_picking.py create mode 100644 sf_stock/security/ir.model.access.csv create mode 100644 sf_stock/views/stock_picking.xml diff --git a/sf_stock/__init__.py b/sf_stock/__init__.py new file mode 100644 index 00000000..511a0ca3 --- /dev/null +++ b/sf_stock/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models \ No newline at end of file diff --git a/sf_stock/__manifest__.py b/sf_stock/__manifest__.py new file mode 100644 index 00000000..f22004e5 --- /dev/null +++ b/sf_stock/__manifest__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +{ + 'name': "sf_stock", + + 'summary': """ + Short (1 phrase/line) summary of the module's purpose, used as + subtitle on modules listing or apps.openerp.com""", + + 'description': """ + Long description of module's purpose + """, + + 'author': "My Company", + 'website': "https://www.yourcompany.com", + + # Categories can be used to filter modules in modules listing + # Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml + # for the full list + 'category': 'Uncategorized', + 'version': '0.1', + + # any module necessary for this one to work correctly + 'depends': ['sf_sale', 'stock'], + + # always loaded + 'data': [ + # 'security/ir.model.access.csv', + 'views/stock_picking.xml', + ], + # only loaded in demonstration mode + 'demo': [ + 'demo/demo.xml', + ], + 'installable': True, + 'application': True, +} diff --git a/sf_stock/controllers/__init__.py b/sf_stock/controllers/__init__.py new file mode 100644 index 00000000..457bae27 --- /dev/null +++ b/sf_stock/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/sf_stock/controllers/controllers.py b/sf_stock/controllers/controllers.py new file mode 100644 index 00000000..af35ee1d --- /dev/null +++ b/sf_stock/controllers/controllers.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# from odoo import http + + +# class SfStock(http.Controller): +# @http.route('/sf_stock/sf_stock', auth='public') +# def index(self, **kw): +# return "Hello, world" + +# @http.route('/sf_stock/sf_stock/objects', auth='public') +# def list(self, **kw): +# return http.request.render('sf_stock.listing', { +# 'root': '/sf_stock/sf_stock', +# 'objects': http.request.env['sf_stock.sf_stock'].search([]), +# }) + +# @http.route('/sf_stock/sf_stock/objects/', auth='public') +# def object(self, obj, **kw): +# return http.request.render('sf_stock.object', { +# 'object': obj +# }) diff --git a/sf_stock/demo/demo.xml b/sf_stock/demo/demo.xml new file mode 100644 index 00000000..726c51be --- /dev/null +++ b/sf_stock/demo/demo.xml @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/sf_stock/models/__init__.py b/sf_stock/models/__init__.py new file mode 100644 index 00000000..c62a4dff --- /dev/null +++ b/sf_stock/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import stock_picking \ No newline at end of file diff --git a/sf_stock/models/stock_picking.py b/sf_stock/models/stock_picking.py new file mode 100644 index 00000000..a28fbb5b --- /dev/null +++ b/sf_stock/models/stock_picking.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +import json +import requests +from odoo import models, fields, api + +from odoo.exceptions import UserError +import logging + +_logger = logging.getLogger(__name__) + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + # 重写验证,下发发货到bfm + def button_validate(self): + info = super(StockPicking, self).button_validate() + if self.picking_type_id.code == 'outgoing': + self.send_to_bfm() + return info + + def send_to_bfm(self): + # 下发发货到bfm + config = self.env['res.config.settings'].get_values() + json1 = { + 'params': { + 'name': self.origin, + 'send_no': self.name, + 'qty_done': self.move_line_ids.qty_done, + }, + } + url1 = config['bfm_url_new'] + '/api/stock/deliver_goods' + r = requests.post(url1, json=json1, data=None) + if r.status_code == 200: + result = json.loads(r.json()['result']) + if result['code'] != 200: + raise UserError(result['message'] or '工厂发货下发bfm失败') + else: + raise UserError('工厂发货下发bfm失败') diff --git a/sf_stock/security/ir.model.access.csv b/sf_stock/security/ir.model.access.csv new file mode 100644 index 00000000..af1bcbaf --- /dev/null +++ b/sf_stock/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_sf_stock_sf_stock,sf_stock.sf_stock,model_sf_stock_sf_stock,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/sf_stock/views/stock_picking.xml b/sf_stock/views/stock_picking.xml new file mode 100644 index 00000000..7750ca76 --- /dev/null +++ b/sf_stock/views/stock_picking.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From 3624ef47553183cbae6a58866e76874e4c1be67e Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Fri, 30 Aug 2024 16:30:34 +0800 Subject: [PATCH 002/217] =?UTF-8?q?=E9=94=80=E5=94=AE=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E4=BB=B7=E6=A0=BC=E9=97=AE=E9=A2=98=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/views/mrp_workorder_view.xml | 2 +- sf_mrs_connect/models/res_config_setting.py | 78 ++++++++++++++++++- .../views/res_config_settings_views.xml | 15 ++++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 457ac122..a41cad09 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -37,7 +37,7 @@ - + diff --git a/sf_mrs_connect/models/res_config_setting.py b/sf_mrs_connect/models/res_config_setting.py index 159e8d48..e1bd6de1 100644 --- a/sf_mrs_connect/models/res_config_setting.py +++ b/sf_mrs_connect/models/res_config_setting.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- # Part of SmartGo. See LICENSE file for full copyright and licensing details. import logging + +import requests + from odoo import api, fields, models -from odoo.exceptions import ValidationError +from odoo.exceptions import ValidationError, UserError _logger = logging.getLogger(__name__) @@ -144,3 +147,76 @@ class ResConfigSettings(models.TransientModel): ir_config.set_param("ftp_user", self.ftp_user or "") ir_config.set_param("ftp_password", self.ftp_password or "") ir_config.set_param("enable_tool_presetter", self.enable_tool_presetter or False) + + def sync_sale_price(self): + self.get_page_all_records(self.sale_order_price_process) + + def sale_order_price_process(self, datas): + if not datas: + return + try: + convert_sale_data = map(lambda data: + {'name': data.name, 'order_lines': { + '%s/%s/%s/%s/±%s/' % ( + order_line.product_template_id.model_long, order_line.product_template_id.model_width, order_line.product_template_id.model_height, + order_line.product_template_id.model_volume, + order_line.product_template_id.model_machining_precision, + ): order_line.price_unit for order_line in + data.order_line + } + }, + datas) + config = self.env['res.config.settings'].get_values() + url = config['bfm_url_new'] + '/api/sync/order/price' + json_data = { + 'params': { + 'data': list(convert_sale_data), + }, + } + response = requests.post(url, json=json_data, data=None) + response = response.json() + if not response.get('error'): + result = response.get('result') + for need_change_sale_data in result: + res_order_lines_map = need_change_sale_data.get('order_lines') + if not res_order_lines_map: + continue + need_change_sale_order = self.env['sale.order'].sudo().search([('name', '=', need_change_sale_data.get('name'))]) + + for need_change_sale_order_line in need_change_sale_order.order_line: + order_line_uniq='%s/%s/%s/%s/±%s/' % ( + need_change_sale_order_line.product_template_id.model_long, need_change_sale_order_line.product_template_id.model_width, + need_change_sale_order_line.product_template_id.model_height, + need_change_sale_order_line.product_template_id.model_volume, + need_change_sale_order_line.product_template_id.model_machining_precision, + ) + if not res_order_lines_map.get(order_line_uniq): + continue + need_change_sale_order_line.write({'price_unit': res_order_lines_map.get(order_line_uniq)}) + else: + logging.error('同步销售订单价格失败 {}'.format(response.text)) + raise UserError('同步销售订单价格失败') + except Exception as e: + raise UserError(e) + + def get_page_all_records(self, func, page_size=100): + # 获取模型对象 + model = self.env['sale.order'].sudo() + + # 初始化分页参数 + page_number = 1 + while True: + # 计算偏移量 + offset = (page_number - 1) * page_size + + # 获取当前页的数据 + records = model.search([], limit=page_size, offset=offset) + + # 如果没有更多记录,退出循环 + if not records: + break + + # 将当前页的数据添加到结果列表 + func(records) + # 增加页码 + page_number += 1 \ No newline at end of file diff --git a/sf_mrs_connect/views/res_config_settings_views.xml b/sf_mrs_connect/views/res_config_settings_views.xml index 05e5be41..651e6f15 100644 --- a/sf_mrs_connect/views/res_config_settings_views.xml +++ b/sf_mrs_connect/views/res_config_settings_views.xml @@ -129,6 +129,21 @@ +
+

销售订单价格同步

+
+
+
+
+
+
+
+
+
+
From 0ca6d46a7bfc81b094a217484a39879d4edbbb70 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Sat, 31 Aug 2024 21:09:56 +0800 Subject: [PATCH 003/217] =?UTF-8?q?=E4=BF=AE=E6=94=B9sf=E4=B8=8B=E5=8F=91?= =?UTF-8?q?=E6=AC=A0=E5=8D=95=EF=BC=8Cbfm=E6=8E=A5=E6=94=B6=E4=B8=8B?= =?UTF-8?q?=E5=8F=91=E6=AC=A0=E5=8D=95=EF=BC=8C=E5=8F=8A=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=8F=91=E8=B4=A7=E5=8D=95=E7=9A=84=E5=85=B3=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_stock/models/__init__.py | 3 +- .../models/stock_backorder_confirmation.py | 27 ++++++++++ sf_stock/models/stock_picking.py | 53 +++++++++++++++++-- 3 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 sf_stock/models/stock_backorder_confirmation.py diff --git a/sf_stock/models/__init__.py b/sf_stock/models/__init__.py index c62a4dff..313cb109 100644 --- a/sf_stock/models/__init__.py +++ b/sf_stock/models/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- -from . import stock_picking \ No newline at end of file +from . import stock_picking +from . import stock_backorder_confirmation \ No newline at end of file diff --git a/sf_stock/models/stock_backorder_confirmation.py b/sf_stock/models/stock_backorder_confirmation.py new file mode 100644 index 00000000..4e894381 --- /dev/null +++ b/sf_stock/models/stock_backorder_confirmation.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +import json +import requests +from odoo import models, fields, api + +from odoo.exceptions import UserError +import logging +from odoo.tools import date_utils + +_logger = logging.getLogger(__name__) + + +class StockBackorderConfirmation(models.TransientModel): + _inherit = 'stock.backorder.confirmation' + + # 继承创建欠单 + def process(self): + info = super(StockBackorderConfirmation, self).process() + _logger.info("创建欠单") + # 下发创建欠单到bfm + + return info + + # 继承取消创建欠单 + def process_cancel_backorder(self): + info = super(StockBackorderConfirmation, self).process_cancel_backorder() + return info diff --git a/sf_stock/models/stock_picking.py b/sf_stock/models/stock_picking.py index a28fbb5b..fdb22c21 100644 --- a/sf_stock/models/stock_picking.py +++ b/sf_stock/models/stock_picking.py @@ -5,6 +5,7 @@ from odoo import models, fields, api from odoo.exceptions import UserError import logging +from odoo.tools import date_utils _logger = logging.getLogger(__name__) @@ -15,22 +16,64 @@ class StockPicking(models.Model): # 重写验证,下发发货到bfm def button_validate(self): info = super(StockPicking, self).button_validate() - if self.picking_type_id.code == 'outgoing': + if self.picking_type_code == 'outgoing': self.send_to_bfm() return info + def deal_move_ids(self, send_move_ids): + move_ids = [] # 本次发货单 + if send_move_ids: + for item in send_move_ids: + val = { + 'name': item.product_id.upload_model_file.display_name, + 'quantity_done': item.quantity_done, + 'date': date_utils.json_default(item.date) if item.date else None, + 'description_picking': item.description_picking, + 'date_deadline': date_utils.json_default(item.date_deadline) if item.date_deadline else None, + 'product_uom_qty': item.product_uom_qty, + 'sequence': item.sequence, + 'price_unit': item.price_unit, + 'priority': item.priority + } + move_ids.append(val) + return move_ids + + def deal_send_backorder_id(self, backorder_ids1): + backorder_ids = [] + if backorder_ids1: + for item in backorder_ids1: + val = { + 'receiverName': item.receiverName, + 'name': item.sale_id.default_code, + 'send_no': item.name, + 'scheduled_date': date_utils.json_default(item.scheduled_date) if item.scheduled_date else None, + 'date': date_utils.json_default(item.date) if item.date else None, + 'date_deadline': date_utils.json_default(item.date_deadline) if item.date_deadline else None, + 'move_ids': self.deal_move_ids(item.move_ids), + } + backorder_ids.append(val) + return backorder_ids + def send_to_bfm(self): # 下发发货到bfm config = self.env['res.config.settings'].get_values() - json1 = { + + data = { 'params': { - 'name': self.origin, + 'receiverName': self.receiverName, + 'name': self.sale_id.default_code, 'send_no': self.name, - 'qty_done': self.move_line_ids.qty_done, + 'scheduled_date': date_utils.json_default(self.scheduled_date) if self.scheduled_date else None, + 'date': date_utils.json_default(self.date) if self.date else None, + 'date_deadline': date_utils.json_default(self.date_deadline) if self.date_deadline else None, + 'move_ids': self.deal_move_ids(self.move_ids), + 'backorder_id': self.deal_send_backorder_id(self.backorder_id), + 'backorder_ids': self.deal_send_backorder_id(self.backorder_ids) + }, } url1 = config['bfm_url_new'] + '/api/stock/deliver_goods' - r = requests.post(url1, json=json1, data=None) + r = requests.post(url1, json=data, data=None) if r.status_code == 200: result = json.loads(r.json()['result']) if result['code'] != 200: From 1dc55c277010c5259cba3a5402c6c7ff10d95469 Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Mon, 2 Sep 2024 09:04:42 +0800 Subject: [PATCH 004/217] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=8D=E8=89=AF?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 35f4a257..5168173e 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -312,9 +312,22 @@ class Sf_Dashboard_Connect(http.Controller): # 工单计划量 plan_data_plan_counts = plan_obj.search_count( [('production_line_id.name', '=', line), ('state', 'not in', ['finished'])]) - # 工单不良累计 - plan_data_fault_counts = plan_obj.search_count( - [('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel'])]) + # # 工单不良累计 + # plan_data_fault_counts = plan_obj.search_count( + # [('production_line_id.name', '=', line), ('production_id.detection_result_ids.state', 'in', ['返工', '报废'])]) + + # 查找符合条件的生产计划记录 + plan_data = plan_obj.search([ + ('production_line_id.name', '=', line), + ]) + + # 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录 + faulty_plans = plan_data.filtered(lambda p: any( + result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids + )) + + # 计算符合条件的记录数量 + plan_data_fault_counts = len(faulty_plans) # 工单返工数量 From fc1cdf4d0eae06756c78ea73de9686e013c6e61d Mon Sep 17 00:00:00 2001 From: hujiaying Date: Mon, 2 Sep 2024 19:02:53 +0800 Subject: [PATCH 005/217] =?UTF-8?q?=E8=81=94=E8=B0=83=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=EF=BC=8C=E5=A4=84=E7=90=86=E4=BF=AE=E6=94=B9?= =?UTF-8?q?sf=E4=B8=8B=E5=8F=91=E4=BB=A3=E5=8F=91=E8=B4=A7=EF=BC=8Cbfm?= =?UTF-8?q?=E6=8E=A5=E6=94=B6=E4=BB=A3=E5=8F=91=E8=B4=A7=E6=B6=88=E6=81=AF?= =?UTF-8?q?=EF=BC=8C=E4=B8=8B=E5=8F=91=E5=88=B0=E5=86=85=E9=83=A8=E7=AE=A1?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E5=A4=84=E7=90=86=E9=87=87=E8=B4=AD=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E4=BB=A3=E5=8F=91=E8=B4=A7=E6=95=B0=E9=87=8F=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=8F=8A=E5=88=9B=E5=BB=BA=E6=AC=A0=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_stock/models/stock_picking.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/sf_stock/models/stock_picking.py b/sf_stock/models/stock_picking.py index fdb22c21..b995f812 100644 --- a/sf_stock/models/stock_picking.py +++ b/sf_stock/models/stock_picking.py @@ -20,8 +20,9 @@ class StockPicking(models.Model): self.send_to_bfm() return info - def deal_move_ids(self, send_move_ids): + def deal_move_ids(self, send_move_ids, send_move_line_ids): move_ids = [] # 本次发货单 + move_line_ids = [] # 本次发货单行 if send_move_ids: for item in send_move_ids: val = { @@ -33,15 +34,28 @@ class StockPicking(models.Model): 'product_uom_qty': item.product_uom_qty, 'sequence': item.sequence, 'price_unit': item.price_unit, - 'priority': item.priority + 'priority': item.priority, + 'state': item.state, } move_ids.append(val) - return move_ids + for item in send_move_line_ids: + val = { + 'qty_done': item.qty_done, + 'reserved_qty': item.reserved_qty, + 'reserved_uom_qty': item.reserved_uom_qty, + 'date': date_utils.json_default(item.date) if item.date else None, + 'description_picking': item.description_picking, + 'state': item.state, + } + move_line_ids.append(val) + return move_ids, move_line_ids def deal_send_backorder_id(self, backorder_ids1): backorder_ids = [] + if backorder_ids1: for item in backorder_ids1: + move_ids, move_line_ids = self.deal_move_ids(item.move_ids, item.move_line_ids) val = { 'receiverName': item.receiverName, 'name': item.sale_id.default_code, @@ -49,7 +63,9 @@ class StockPicking(models.Model): 'scheduled_date': date_utils.json_default(item.scheduled_date) if item.scheduled_date else None, 'date': date_utils.json_default(item.date) if item.date else None, 'date_deadline': date_utils.json_default(item.date_deadline) if item.date_deadline else None, - 'move_ids': self.deal_move_ids(item.move_ids), + 'move_ids': move_ids, + 'move_line_ids': move_line_ids, + 'state': item.state, } backorder_ids.append(val) return backorder_ids @@ -57,7 +73,7 @@ class StockPicking(models.Model): def send_to_bfm(self): # 下发发货到bfm config = self.env['res.config.settings'].get_values() - + move_ids, move_line_ids = self.deal_move_ids(self.move_ids, self.move_line_ids) data = { 'params': { 'receiverName': self.receiverName, @@ -66,13 +82,17 @@ class StockPicking(models.Model): 'scheduled_date': date_utils.json_default(self.scheduled_date) if self.scheduled_date else None, 'date': date_utils.json_default(self.date) if self.date else None, 'date_deadline': date_utils.json_default(self.date_deadline) if self.date_deadline else None, - 'move_ids': self.deal_move_ids(self.move_ids), + 'move_ids': move_ids, + 'move_line_ids': move_line_ids, + 'state': self.state, 'backorder_id': self.deal_send_backorder_id(self.backorder_id), 'backorder_ids': self.deal_send_backorder_id(self.backorder_ids) }, } url1 = config['bfm_url_new'] + '/api/stock/deliver_goods' + json_str = json.dumps(data) + print('json_str', json_str) r = requests.post(url1, json=data, data=None) if r.status_code == 200: result = json.loads(r.json()['result']) From 6e9c326a52f74df2d02fdc6d0f0c167a1dc33ec4 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Tue, 3 Sep 2024 13:56:16 +0800 Subject: [PATCH 006/217] =?UTF-8?q?=E6=99=BA=E8=83=BD=E5=B7=A5=E5=8E=82?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=90=8C=E6=AD=A5=E4=BB=BB=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_mrs_connect/models/res_config_setting.py | 2 +- sf_mrs_connect/models/sync_common.py | 2 +- sf_tool_management/models/functional_tool_enroll.py | 8 ++++---- sf_warehouse/models/sync_common.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sf_mrs_connect/models/res_config_setting.py b/sf_mrs_connect/models/res_config_setting.py index e1bd6de1..ead17af5 100644 --- a/sf_mrs_connect/models/res_config_setting.py +++ b/sf_mrs_connect/models/res_config_setting.py @@ -89,7 +89,7 @@ class ResConfigSettings(models.TransientModel): _logger.info("同步刀具物料每齿走刀量完成") except Exception as e: - _logger.info("捕获错误信息:%s" % e) + _logger.info("sf_all_sync error: %s" % e) raise ValidationError("数据错误导致同步失败,请联系管理员") @api.model diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index e812bd03..2f77c7e6 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -73,7 +73,7 @@ class MrStaticResourceDataSync(models.Model): self.env['sf.feed.per.tooth'].sync_feed_per_tooth_yesterday() _logger.info("同步刀具物料每齿走刀量完成") except Exception as e: - logging.info("捕获错误信息:%s" % e) + logging.info("同步静态资源库失败:%s" % e) raise ValidationError("数据错误导致同步失败,请联系管理员") diff --git a/sf_tool_management/models/functional_tool_enroll.py b/sf_tool_management/models/functional_tool_enroll.py index ba3553e1..ab391511 100644 --- a/sf_tool_management/models/functional_tool_enroll.py +++ b/sf_tool_management/models/functional_tool_enroll.py @@ -50,7 +50,7 @@ class ToolDatasync(models.Model): # self.env['sf.real.time.distribution.of.functional.tools'].sudo().sync_enroll_functional_tool_real_time_distribution_all() # logging.info("功能刀具安全库存每日同步成功") except Exception as e: - logging.info("捕获错误信息:%s" % e) + logging.info("刀具物料、刀具信息同步失败:%s" % e) raise ValidationError("数据错误导致同步失败,请联系管理员") @@ -312,7 +312,7 @@ class FunctionalToolWarning(models.Model): else: logging.info('没有注册功能刀具预警信息') except Exception as e: - logging.info("捕获错误信息:%s" % e) + logging.info("功能刀具预警同步失败:%s" % e) class StockMoveLine(models.Model): @@ -373,7 +373,7 @@ class StockMoveLine(models.Model): else: logging.info('没有注册功能刀具出入库记录信息') except Exception as e: - logging.info("捕获错误信息:%s" % e) + logging.info("出入库记录信息同步失败:%s" % e) class RealTimeDistributionFunctionalTools(models.Model): @@ -446,4 +446,4 @@ class RealTimeDistributionFunctionalTools(models.Model): else: logging.info('没有注册功能刀具出入库记录信息') except Exception as e: - logging.info("捕获错误信息:%s" % e) + logging.info("实时功能刀具同步失败:%s" % e) diff --git a/sf_warehouse/models/sync_common.py b/sf_warehouse/models/sync_common.py index aaa186f9..d8f075dd 100644 --- a/sf_warehouse/models/sync_common.py +++ b/sf_warehouse/models/sync_common.py @@ -139,5 +139,5 @@ class MrsShelfLocationDataSync(models.Model): location_id.product_sn_id = False except Exception as e: - logging.info("捕获错误信息:%s" % e) + logging.info("库区信息同步失败:%s" % e) raise ValidationError("数据错误导致同步失败,请联系管理员") From 589035d42bcbdcccdcf07e56e5132cdcfdb158aa Mon Sep 17 00:00:00 2001 From: hujiaying Date: Tue, 3 Sep 2024 19:14:48 +0800 Subject: [PATCH 007/217] =?UTF-8?q?=E5=A4=84=E7=90=86=E9=87=87=E8=B4=AD?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E4=BB=A3=E5=8F=91=E8=B4=A7=E6=95=B0=E9=87=8F?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=8A=E5=88=9B=E5=BB=BA=E6=AC=A0=E5=8D=95?= =?UTF-8?q?=EF=BC=8C=E5=A4=84=E7=90=86sf=E5=8F=91=E8=B4=A7=E5=8D=95?= =?UTF-8?q?=E4=B8=8B=E5=8F=91=E4=B8=8D=E9=9C=80=E8=A6=81=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=AC=A0=E5=8D=95=EF=BC=8C=E5=A4=84=E7=90=86bfm=E5=8F=91?= =?UTF-8?q?=E8=B4=A7=E5=8D=95=E4=B8=8B=E5=8F=91=E5=86=85=E9=83=A8=E7=AE=A1?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E5=86=85=E9=83=A8=E7=AE=A1=E7=90=86=E5=A4=84?= =?UTF-8?q?=E7=90=86=E4=B8=8D=E9=9C=80=E8=A6=81=E5=88=9B=E5=BB=BA=E6=AC=A0?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_stock/models/__init__.py | 3 +-- .../models/stock_backorder_confirmation.py | 27 ------------------- sf_stock/models/stock_picking.py | 10 +++++-- 3 files changed, 9 insertions(+), 31 deletions(-) delete mode 100644 sf_stock/models/stock_backorder_confirmation.py diff --git a/sf_stock/models/__init__.py b/sf_stock/models/__init__.py index 313cb109..c62a4dff 100644 --- a/sf_stock/models/__init__.py +++ b/sf_stock/models/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- -from . import stock_picking -from . import stock_backorder_confirmation \ No newline at end of file +from . import stock_picking \ No newline at end of file diff --git a/sf_stock/models/stock_backorder_confirmation.py b/sf_stock/models/stock_backorder_confirmation.py deleted file mode 100644 index 4e894381..00000000 --- a/sf_stock/models/stock_backorder_confirmation.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -import json -import requests -from odoo import models, fields, api - -from odoo.exceptions import UserError -import logging -from odoo.tools import date_utils - -_logger = logging.getLogger(__name__) - - -class StockBackorderConfirmation(models.TransientModel): - _inherit = 'stock.backorder.confirmation' - - # 继承创建欠单 - def process(self): - info = super(StockBackorderConfirmation, self).process() - _logger.info("创建欠单") - # 下发创建欠单到bfm - - return info - - # 继承取消创建欠单 - def process_cancel_backorder(self): - info = super(StockBackorderConfirmation, self).process_cancel_backorder() - return info diff --git a/sf_stock/models/stock_picking.py b/sf_stock/models/stock_picking.py index b995f812..37f24bf0 100644 --- a/sf_stock/models/stock_picking.py +++ b/sf_stock/models/stock_picking.py @@ -13,6 +13,8 @@ _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() @@ -63,6 +65,7 @@ class StockPicking(models.Model): 'scheduled_date': date_utils.json_default(item.scheduled_date) if item.scheduled_date else None, 'date': date_utils.json_default(item.date) if item.date else None, 'date_deadline': date_utils.json_default(item.date_deadline) if item.date_deadline else None, + 'date_done': date_utils.json_default(item.date_done) if item.date_done else None, 'move_ids': move_ids, 'move_line_ids': move_line_ids, 'state': item.state, @@ -71,23 +74,26 @@ class StockPicking(models.Model): return backorder_ids def send_to_bfm(self): + skip_backorder = self.env.context.get('skip_backorder') # 下发发货到bfm config = self.env['res.config.settings'].get_values() move_ids, move_line_ids = self.deal_move_ids(self.move_ids, self.move_line_ids) data = { 'params': { 'receiverName': self.receiverName, + 'priority': self.priority, 'name': self.sale_id.default_code, 'send_no': self.name, 'scheduled_date': date_utils.json_default(self.scheduled_date) if self.scheduled_date else None, 'date': date_utils.json_default(self.date) if self.date else None, 'date_deadline': date_utils.json_default(self.date_deadline) if self.date_deadline else None, + 'date_done': date_utils.json_default(self.date_done) if self.date_done else None, 'move_ids': move_ids, 'move_line_ids': move_line_ids, 'state': self.state, 'backorder_id': self.deal_send_backorder_id(self.backorder_id), - 'backorder_ids': self.deal_send_backorder_id(self.backorder_ids) - + 'backorder_ids': self.deal_send_backorder_id(self.backorder_ids), + 'cancel_backorder_ids': skip_backorder, }, } url1 = config['bfm_url_new'] + '/api/stock/deliver_goods' From 04c0d687636af151803b1abd4fc8a95d3daad9b8 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 4 Sep 2024 14:58:04 +0800 Subject: [PATCH 008/217] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E8=BA=AB=E4=BB=BD=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/controllers/controllers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index 632e9e4c..719ac481 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -477,7 +477,7 @@ class Manufacturing_Connect(http.Controller): logging.info('LocationChange error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def AGVToProduct(self, **kw): """ @@ -549,7 +549,7 @@ class Manufacturing_Connect(http.Controller): logging.info('AGVToProduct error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def AGVDownProduct(self, **kw): """ From 511bed47ff6233211a94bb8d15b627e61351db04 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 4 Sep 2024 16:58:55 +0800 Subject: [PATCH 009/217] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BF=94=E5=B7=A5-?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E8=A3=85=E5=A4=B9=E6=B5=8B=E9=87=8F=E6=95=B0?= =?UTF-8?q?=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/product_template.py | 4 +- sf_manufacturing/views/mrp_workorder_view.xml | 4 +- sf_manufacturing/wizard/rework_wizard.py | 54 +++++++++++++++---- .../wizard/rework_wizard_views.xml | 26 +++++---- sf_sale/models/quick_easy_order.py | 4 +- sf_sale/models/quick_easy_order_old.py | 4 +- 6 files changed, 68 insertions(+), 28 deletions(-) diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 071f1167..37fa02bd 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -9,8 +9,8 @@ from odoo.exceptions import ValidationError, UserError from odoo.modules import get_resource_path -from OCC.Extend.DataExchange import read_step_file -from OCC.Extend.DataExchange import write_stl_file +# from OCC.Extend.DataExchange import read_step_file +# from OCC.Extend.DataExchange import write_stl_file class ResProductMo(models.Model): diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index a802266f..512534d4 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -486,8 +486,8 @@
- - + + = 1 and self.programming_state == '已编程': @@ -149,9 +183,7 @@ class ReworkWizard(models.TransientModel): 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name, ret), 'cnc_worksheet': cnc_rework.cnc_worksheet}) - new_pre_workorder = self.production_id.workorder_ids.filtered(lambda - p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in ( - 'rework', 'done')) + if new_pre_workorder: pre_rework = max(self.production_id.workorder_ids.filtered( lambda pr: pr.processing_panel == panel.name and pr.state in ( diff --git a/sf_manufacturing/wizard/rework_wizard_views.xml b/sf_manufacturing/wizard/rework_wizard_views.xml index 8ba3f4dd..d8cf0fb9 100644 --- a/sf_manufacturing/wizard/rework_wizard_views.xml +++ b/sf_manufacturing/wizard/rework_wizard_views.xml @@ -14,17 +14,25 @@ + +
+ 保留装夹测量数据 + + +
- 注意: 该制造订单产品已申请重新编程次数为,且当前编程状态为 - + + 注意: 该制造订单产品已申请重新编程次数为,且当前编程状态为 + +
申请重新编程 diff --git a/sf_sale/models/quick_easy_order.py b/sf_sale/models/quick_easy_order.py index 081807a4..cb1886a1 100644 --- a/sf_sale/models/quick_easy_order.py +++ b/sf_sale/models/quick_easy_order.py @@ -8,8 +8,8 @@ from datetime import datetime import requests from odoo import http from odoo.http import request -from OCC.Extend.DataExchange import read_step_file -from OCC.Extend.DataExchange import write_stl_file +# from OCC.Extend.DataExchange import read_step_file +# from OCC.Extend.DataExchange import write_stl_file from odoo import models, fields, api from odoo.modules import get_resource_path from odoo.exceptions import ValidationError, UserError diff --git a/sf_sale/models/quick_easy_order_old.py b/sf_sale/models/quick_easy_order_old.py index 3ae65db3..92f6cda2 100644 --- a/sf_sale/models/quick_easy_order_old.py +++ b/sf_sale/models/quick_easy_order_old.py @@ -6,8 +6,8 @@ import os from datetime import datetime from stl import mesh # from OCC.Core.GProp import GProp_GProps -from OCC.Extend.DataExchange import read_step_file -from OCC.Extend.DataExchange import write_stl_file +# from OCC.Extend.DataExchange import read_step_file +# from OCC.Extend.DataExchange import write_stl_file from odoo.addons.sf_base.commons.common import Common from odoo import models, fields, api from odoo.modules import get_resource_path From 69578ab054c49fac5775f9fdfecf0c24bd0e3b53 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Wed, 4 Sep 2024 17:28:26 +0800 Subject: [PATCH 010/217] =?UTF-8?q?bfm=E6=96=B0=E5=A2=9E=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=EF=BC=8C=E4=BB=A3=E5=8F=91=E8=B4=A7=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=EF=BC=8C=E5=A4=84=E7=90=86sf=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E6=80=A7=E5=8F=91=E8=B4=A7=EF=BC=8Cbfm=E7=94=9F=E6=88=90?= =?UTF-8?q?=E4=BB=A3=E5=8F=91=E8=B4=A7=E5=90=8D=E5=AD=97=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_stock/models/stock_picking.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sf_stock/models/stock_picking.py b/sf_stock/models/stock_picking.py index 37f24bf0..82ce7ea5 100644 --- a/sf_stock/models/stock_picking.py +++ b/sf_stock/models/stock_picking.py @@ -69,6 +69,7 @@ class StockPicking(models.Model): 'move_ids': move_ids, 'move_line_ids': move_line_ids, 'state': item.state, + 'move_type': item.move_type, } backorder_ids.append(val) return backorder_ids @@ -94,6 +95,7 @@ class StockPicking(models.Model): 'backorder_id': self.deal_send_backorder_id(self.backorder_id), 'backorder_ids': self.deal_send_backorder_id(self.backorder_ids), 'cancel_backorder_ids': skip_backorder, + 'move_type': self.move_type, }, } url1 = config['bfm_url_new'] + '/api/stock/deliver_goods' From ca1060ada5570873f13ed38b5b7ebce259fcbfbb Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 4 Sep 2024 17:33:54 +0800 Subject: [PATCH 011/217] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/product_template.py | 4 ++-- sf_sale/models/quick_easy_order.py | 4 ++-- sf_sale/models/quick_easy_order_old.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 37fa02bd..071f1167 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -9,8 +9,8 @@ from odoo.exceptions import ValidationError, UserError from odoo.modules import get_resource_path -# from OCC.Extend.DataExchange import read_step_file -# from OCC.Extend.DataExchange import write_stl_file +from OCC.Extend.DataExchange import read_step_file +from OCC.Extend.DataExchange import write_stl_file class ResProductMo(models.Model): diff --git a/sf_sale/models/quick_easy_order.py b/sf_sale/models/quick_easy_order.py index cb1886a1..081807a4 100644 --- a/sf_sale/models/quick_easy_order.py +++ b/sf_sale/models/quick_easy_order.py @@ -8,8 +8,8 @@ from datetime import datetime import requests from odoo import http from odoo.http import request -# from OCC.Extend.DataExchange import read_step_file -# from OCC.Extend.DataExchange import write_stl_file +from OCC.Extend.DataExchange import read_step_file +from OCC.Extend.DataExchange import write_stl_file from odoo import models, fields, api from odoo.modules import get_resource_path from odoo.exceptions import ValidationError, UserError diff --git a/sf_sale/models/quick_easy_order_old.py b/sf_sale/models/quick_easy_order_old.py index 92f6cda2..3ae65db3 100644 --- a/sf_sale/models/quick_easy_order_old.py +++ b/sf_sale/models/quick_easy_order_old.py @@ -6,8 +6,8 @@ import os from datetime import datetime from stl import mesh # from OCC.Core.GProp import GProp_GProps -# from OCC.Extend.DataExchange import read_step_file -# from OCC.Extend.DataExchange import write_stl_file +from OCC.Extend.DataExchange import read_step_file +from OCC.Extend.DataExchange import write_stl_file from odoo.addons.sf_base.commons.common import Common from odoo import models, fields, api from odoo.modules import get_resource_path From a7e1c89e660d8cc38292c4145fbe7c746321b4e5 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 4 Sep 2024 17:36:41 +0800 Subject: [PATCH 012/217] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/views/mrp_workorder_view.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 512534d4..a802266f 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -486,8 +486,8 @@
- - + +
Date: Wed, 4 Sep 2024 17:43:48 +0800 Subject: [PATCH 013/217] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BF=94=E5=B7=A5?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/wizard/rework_wizard.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sf_manufacturing/wizard/rework_wizard.py b/sf_manufacturing/wizard/rework_wizard.py index 51d40ac3..4698a994 100644 --- a/sf_manufacturing/wizard/rework_wizard.py +++ b/sf_manufacturing/wizard/rework_wizard.py @@ -63,11 +63,12 @@ class ReworkWizard(models.TransientModel): for panel in self.processing_panel_id: panel_workorder = self.production_id.workorder_ids.filtered( lambda ap: ap.processing_panel == panel.name and ap.state != 'rework') - rework_clamp_workorder = max(panel_workorder.filtered( - lambda rp: rp.processing_panel == panel.name and rp.routing_type == '装夹预调' and rp.state in [ - 'done', 'rework'])) if panel_workorder: panel_workorder.write({'state': 'rework'}) + rework_clamp_workorder = max(panel_workorder.filtered( + lambda + rp: rp.processing_panel == panel.name and rp.routing_type == '装夹预调' and rp.state in [ + 'done', 'rework'])) # panel_workorder.filtered( # lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered( # lambda wd: wd.status == '待下发').write({'status': '已取消'}) From 72eada06394ee82c23fe71c39683d17d6d62d97f Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Wed, 4 Sep 2024 17:55:24 +0800 Subject: [PATCH 014/217] =?UTF-8?q?1=E3=80=81=E5=8A=9F=E8=83=BD=E5=88=80?= =?UTF-8?q?=E5=85=B7=E7=BB=84=E8=A3=85tree=E3=80=81form=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4=EF=BC=9B2=E3=80=81?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AD=97=E6=AE=B5=E5=86=85=E5=AE=B9=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E8=B0=83=E6=95=B4=EF=BC=9B3=E3=80=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=88=80=E5=85=B7=E7=BB=84=E8=A3=85=E5=8D=95=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E9=80=BB=E8=BE=91=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/models/base.py | 13 ++- sf_tool_management/views/tool_base_views.xml | 103 +++++++++---------- sf_tool_management/wizard/wizard.py | 2 +- 3 files changed, 58 insertions(+), 60 deletions(-) diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 2876bc8f..a464d5cb 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -32,7 +32,7 @@ class MachineTableToolChangingApply(models.Model): domain=[('product_id.name', '=', '功能刀具')], related='functional_tool_name_id.barcode_id') rfid = fields.Char('Rfid', related='functional_tool_name_id.rfid') - functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', domain=[('assemble_status', '=', '1')], + functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', domain=[('assemble_status', '=', '2')], string='功能刀具名称') functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, related='functional_tool_name_id.after_assembly_functional_tool_type_id') @@ -375,16 +375,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', '待组装'), ('1', '组装中'), ('2', '已组装'), ('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) @@ -622,6 +623,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( diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index 390fcb15..480fc357 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -446,19 +446,19 @@ - + - + - - - - - - - +
+ +

@@ -496,20 +497,19 @@ - - - - - + + + - + + + + - + @@ -650,28 +650,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -685,9 +663,27 @@ + + + + + + + + + + + + + @@ -705,20 +701,19 @@ + + + - - - - - - + - + + @@ -743,7 +738,7 @@ tree,form,search - {'search_default_no_assemble_status':1} + {'search_default_no_assemble_status':2} diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py index aa5c645e..d38411bc 100644 --- a/sf_tool_management/wizard/wizard.py +++ b/sf_tool_management/wizard/wizard.py @@ -725,7 +725,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel): 'after_assembly_effective_length': self.after_assembly_effective_length, 'L_D_number': self.L_D_number, 'hiding_length': self.hiding_length, - 'assemble_status': '1', + 'assemble_status': '2', 'tool_loading_person': self.env.user.name, 'image': self.image, 'tool_loading_time': fields.Datetime.now() From 005a7bb68ea634e38b10a314f471a07483609c10 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Thu, 5 Sep 2024 08:51:36 +0800 Subject: [PATCH 015/217] =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E7=BB=B4=E4=BF=9D=E6=A0=87=E5=87=86,=E5=BB=BA=E7=AB=8B?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=BB=B4=E4=BF=9D=E6=A0=87=E5=87=86=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sf_equipment_maintenance_standards.py | 1 + .../equipment_maintenance_standards_views.xml | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sf_maintenance/models/sf_equipment_maintenance_standards.py b/sf_maintenance/models/sf_equipment_maintenance_standards.py index 92d075e1..0cd3c81c 100644 --- a/sf_maintenance/models/sf_equipment_maintenance_standards.py +++ b/sf_maintenance/models/sf_equipment_maintenance_standards.py @@ -24,6 +24,7 @@ class SfEquipmentSaintenanceStandards(models.Model): remark = fields.Char('备注') maintenance_type = fields.Selection([('保养', '保养'), ("检修", "检修")], string='类型', default='保养') name = fields.Char(string='名称') + active = fields.Boolean(default=True) @api.model_create_multi def create(self, vals_list): diff --git a/sf_maintenance/views/equipment_maintenance_standards_views.xml b/sf_maintenance/views/equipment_maintenance_standards_views.xml index 1af9bfc5..1389633b 100644 --- a/sf_maintenance/views/equipment_maintenance_standards_views.xml +++ b/sf_maintenance/views/equipment_maintenance_standards_views.xml @@ -6,15 +6,16 @@ equipment.maintenance.standards.form equipment.maintenance.standards - + - - - - - - + + + + + + + @@ -50,7 +51,8 @@ equipment.maintenance.standards.tree equipment.maintenance.standards - + + From eff39a39e3c66d818cca84be838610ba325d2fe9 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Thu, 5 Sep 2024 09:24:45 +0800 Subject: [PATCH 016/217] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20cloud=E7=AB=AF?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E8=A1=A8=E9=9D=A2=E5=B7=A5=E8=89=BA=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=BF=85=E5=A1=AB=E5=8F=82=E6=95=B0=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E5=A1=AB=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_base/views/common_view.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sf_base/views/common_view.xml b/sf_base/views/common_view.xml index 334eaa32..691eb208 100644 --- a/sf_base/views/common_view.xml +++ b/sf_base/views/common_view.xml @@ -26,19 +26,19 @@ - - + + - - - + + + - + From 54d9902ae45eac3bf20334156f8615f8f612b950 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Thu, 5 Sep 2024 09:26:05 +0800 Subject: [PATCH 017/217] =?UTF-8?q?=E6=92=A4=E5=9B=9E=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_base/views/common_view.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/sf_base/views/common_view.xml b/sf_base/views/common_view.xml index 691eb208..28f228f1 100644 --- a/sf_base/views/common_view.xml +++ b/sf_base/views/common_view.xml @@ -12,7 +12,6 @@ - sf.production.process.parameter From 00809d88f606fb4fb8eecf478f67e18200bcded4 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Thu, 5 Sep 2024 09:53:32 +0800 Subject: [PATCH 018/217] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_bf_connect/models/process_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sf_bf_connect/models/process_status.py b/sf_bf_connect/models/process_status.py index f4c4d859..111254c7 100644 --- a/sf_bf_connect/models/process_status.py +++ b/sf_bf_connect/models/process_status.py @@ -53,7 +53,7 @@ class StatusChange(models.Model): if not ret.get('error'): logging.info('接口已经执行=============') else: - logging.error('工厂加工同步订单状态失败 {}'.format(ret.text)) + logging.error('工厂加工同步订单状态失败 {}'.format(ret)) raise UserError('工厂加工同步订单状态失败') except UserError as e: logging.error('工厂加工同步订单状态失败 {}'.format(e)) From eec79d7d3e6ccc70c5deaeee149f246c56f0e2f9 Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Thu, 5 Sep 2024 10:30:04 +0800 Subject: [PATCH 019/217] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E4=B8=BAurl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 5168173e..6e661f46 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -15,7 +15,7 @@ db_config = { "user": "postgres", "password": "postgres", "port": "5432", - "host": "172.16.10.98" + "host": "172.16.10.113" } @@ -162,7 +162,8 @@ class Sf_Dashboard_Connect(http.Controller): 'first_online_duration': first_online_duration, # 停机时间:关机时间 - 运行时间 # 停机时长:关机时间 - 初次上线时间 - 'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}', + # 'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}', + 'img': f'https://xt.sf.jikimo.com/equipment/get_image/{machine_data.id}', 'equipment_type': machine_data.category_id.name, }) @@ -843,3 +844,21 @@ class Sf_Dashboard_Connect(http.Controller): # 返回数据 res['data'] = oee_data return json.dumps(res) + + @http.route(['/equipment/get_image/'], type='http', auth="public", website=True) + def get_image(self, record_id, **kwargs): + # 获取模型中的记录 + record = request.env['maintenance.equipment'].sudo().browse(record_id) + + # 获取图片字段的数据 + image_data_base64 = record.machine_tool_picture + + if image_data_base64: + # 将Base64解码为二进制数据 + image_data_binary = base64.b64decode(image_data_base64) + + # 返回图片数据,并设置正确的Content-Type + return request.make_response(image_data_binary, headers=[('Content-Type', 'image/png')]) + else: + # 如果没有图片数据,返回404 + return request.not_found() From 5f218e69d4793d9915c5b5c8db111a120631a5a7 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Thu, 5 Sep 2024 17:39:09 +0800 Subject: [PATCH 020/217] =?UTF-8?q?=E8=AE=A1=E5=88=92=E6=8E=92=E7=A8=8B?= =?UTF-8?q?=E8=80=83=E8=99=91CNC=E7=94=9F=E4=BA=A7=E7=BA=BF=E6=97=A5?= =?UTF-8?q?=E4=BA=A7=E8=83=BD=E7=9A=84=E4=BC=98=E5=8C=96=E9=9C=80=E6=B1=82?= =?UTF-8?q?,=E6=96=B0=E5=A2=9E=E5=8F=AF=E7=94=A8=E6=9C=BA=E5=8F=B0?= =?UTF-8?q?=E6=95=B0=E9=87=8F=EF=BC=8C=E5=8D=95=E5=8F=B0=E5=B0=8F=E6=97=B6?= =?UTF-8?q?=E4=BA=A7=E8=83=BD=EF=BC=8C=E6=97=A5=E6=9C=89=E6=95=88=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=97=B6=E9=95=BF=E8=AE=A1=E7=AE=97=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workcenter.py | 45 +++++++- .../views/mrp_workcenter_views.xml | 101 +++++++++++------- 2 files changed, 103 insertions(+), 43 deletions(-) diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 03597980..ecb29ad7 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -1,6 +1,6 @@ import datetime from collections import defaultdict -from odoo import fields, models +from odoo import fields, models, api from odoo.addons.resource.models.resource import Intervals @@ -41,14 +41,16 @@ class ResWorkcenter(models.Model): oee_target = fields.Float( string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True) - oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', store=True) + oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', + store=True) time_start = fields.Float('Setup Time', tracking=True) time_stop = fields.Float('Cleanup Time', tracking=True) costs_hour = fields.Float(string='Cost per hour', help='Hourly processing cost.', default=0.0, tracking=True) equipment_status = fields.Selection( - [("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")], + [("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), + ("封存(报废)", "封存(报废)")], string="设备状态", related='equipment_id.state') # @api.depends('equipment_id') @@ -127,6 +129,43 @@ class ResWorkcenter(models.Model): # AGV是否可配送 is_agv_scheduling = fields.Boolean(string="AGV所属区域", tracking=True) + # 生产线优化 + available_machine_number = fields.Integer(string="可用机台数量") + single_machine_capacity = fields.Float(string="单台小时产能") + production_line_hour_capacity = fields.Float(string="生产线小时产能", readonly=True, + _compute='_compute_production_line_hour_capacity') + effective_working_hours_day = fields.Float(string="日有效工作时长", default=0, readonly=True, + _compute='_compute_effective_working_hours_day') + default_capacity = fields.Float( + '生产线日产能', default=2.0, _compute='_compute_production_line_day_capacity', readonly=True) + + # 计算生产线日产能 + @api.depends('production_line_hour_capacity', 'effective_working_hours_day') + def _compute_production_line_day_capacity(self): + for record in self: + record.default_capacity = round( + record.production_line_hour_capacity * record.effective_working_hours_day, 2) + + # 计算日有效工作时长 + @api.depends('attendance_ids', 'attendance_ids.hour_to', 'attendance_ids.hour_from') + def _compute_effective_working_hours_day(self): + for record in self: + attendance_ids = record.resource_calendar_id.attendance_ids.filter( + lambda r: r.dayofweek == datetime.now().weekday()) + if attendance_ids: + for attendance_id in attendance_ids: + if attendance_id.hour_from and attendance_id.hour_to: + record.effective_working_hours_day += attendance_id.hour_to - attendance_id.hour_from + else: + record.effective_working_hours_day = 0 + + # 计算生产线小时产能 + @api.depends('single_machine_capacity', 'available_machine_number') + def _compute_production_line_hour_capacity(self): + for record in self: + record.production_line_hour_capacity = round( + record.single_machine_capacity * record.available_machine_number, 2) + class ResWorkcenterProductivity(models.Model): _inherit = 'mrp.workcenter.productivity' diff --git a/sf_manufacturing/views/mrp_workcenter_views.xml b/sf_manufacturing/views/mrp_workcenter_views.xml index d0b4bee6..aad35d04 100644 --- a/sf_manufacturing/views/mrp_workcenter_views.xml +++ b/sf_manufacturing/views/mrp_workcenter_views.xml @@ -6,9 +6,9 @@ mrp.production - - - + + +
- +

@@ -510,7 +513,7 @@ - + @@ -524,8 +527,10 @@ - - + + - + - + - - + +
+ options="{'no_create': True, 'no_quick_create': True}" + attrs="{'readonly': [('assemble_status', 'in', ['1','2'])]}"/> @@ -569,14 +577,18 @@
- +
- - + + + @@ -585,16 +597,19 @@
- + + attrs="{'invisible': [('integral_freight_lot_id', '=', False),'|','|','|', ('blade_freight_lot_id', '!=', False),('bar_freight_lot_id', '!=', False),('pad_freight_lot_id', '!=', False),('assemble_status', 'not in', ['0','01'])]}">
- - + + + @@ -602,13 +617,16 @@
+ attrs="{'invisible': [('blade_freight_lot_id', '=', False),'|', ('integral_freight_lot_id', '!=', False),('assemble_status', 'not in', ['0','01'])]}">
- - + + + @@ -617,14 +635,18 @@
- +
- - + + + @@ -632,14 +654,18 @@
- +
- - + + + @@ -708,8 +734,10 @@ - - + + + @@ -738,7 +766,7 @@ tree,form,search - {'search_default_no_assemble_status':2} + {'search_default_no_assemble_status':[1,01]} diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py index d38411bc..d1797855 100644 --- a/sf_tool_management/wizard/wizard.py +++ b/sf_tool_management/wizard/wizard.py @@ -598,361 +598,175 @@ class FunctionalToolAssemblyOrder(models.TransientModel): else: record.L_D_number = 0 - def functional_tool_assembly(self): - """ - 功能刀具组装 - :return: - """ - logging.info('功能刀具开始组装!') - # 获取组装单对象 - functional_tool_assembly = self.env['sf.functional.tool.assembly'].search([ - ('assembly_order_code', '=', self.assembly_order_code), - ('machine_tool_name_id', '=', self.machine_tool_name_id.id), - ('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id), - ('assemble_status', '=', '0'), - ]) - # 对物料做必填判断 - self.materials_must_be_judged() - - product_id = self.env['product.product'] - # 创建组装入库单 - # 创建功能刀具批次/序列号记录 - stock_lot = product_id.create_assemble_warehouse_receipt(self.id, functional_tool_assembly, self) - # 封装功能刀具数据,用于更新组装单信息 - desc_1 = self.get_desc_1(stock_lot) - # 封装功能刀具数据,用于创建功能刀具记录 - desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly) - # 创建功能刀具组装入库单 - self.env['stock.picking'].create_tool_stocking_picking(stock_lot, functional_tool_assembly, 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.after_name_id.id - }, record_1) - - # =====================修改功能刀具组装单、机床换刀申请、CAM工单程序用刀计划的状态============== - # 修改功能刀具组装单信息 - functional_tool_assembly.write(desc_1) - if functional_tool_assembly.sf_machine_table_tool_changing_apply_id: - # 修改机床换刀申请的状态 - self.env['sf.machine.table.tool.changing.apply'].sudo().search([ - ('id', '=', functional_tool_assembly.sf_machine_table_tool_changing_apply_id.id) - ]).write({'status': '3'}) - elif functional_tool_assembly.sf_cam_work_order_program_knife_plan_id: - # 修改CAM工单程序用刀计划状态 - cam_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().search([ - ('id', '=', functional_tool_assembly.sf_cam_work_order_program_knife_plan_id.id) - ]) - cam_plan.write({'plan_execute_status': '2'}) - - logging.info('功能刀具组装完成!') - - # 关闭弹出窗口 - return {'type': 'ir.actions.act_window_close'} - - def materials_must_be_judged(self): - """ - 功能刀具组装必填判断 - """ - # 物料必填校验 - if not self.handle_code_id: - raise ValidationError('缺少【刀柄】物料信息!') - if not self.integral_product_id and not self.blade_product_id: - raise ValidationError('【整体式刀具】和【刀片】必须填写一个!') - if self.blade_product_id: - if not self.bar_product_id and not self.pad_product_id: - raise ValidationError('【刀盘】和【刀杆】必须填写一个!') - # 组装参数必填校验 - if self.after_assembly_functional_tool_length == 0: - raise ValidationError('组装参数信息【伸出长】不能为0!') - if self.after_assembly_max_lifetime_value == 0: - raise ValidationError('组装参数信息【最大寿命值】不能为0!') - if self.after_assembly_alarm_value == 0: - raise ValidationError('组装参数信息【报警值】不能为0!') - # if self.after_assembly_effective_length == 0: - # raise ValidationError('组装参数信息【有效长】不能为0!!!') - # if self.hiding_length == 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_1(self, stock_lot): - return { - 'start_preset_bool': False, - 'barcode_id': stock_lot.id, - 'code': self.code, - 'rfid': self.rfid, - 'tool_groups_id': self.after_tool_groups_id.id, - 'handle_code_id': self.handle_code_id.id, - 'integral_freight_barcode_id': self.integral_freight_barcode_id.id, - 'integral_lot_id': self.integral_freight_lot_id.lot_id.id, - 'blade_freight_barcode_id': self.blade_freight_barcode_id.id, - 'blade_lot_id': self.blade_freight_lot_id.lot_id.id, - 'bar_freight_barcode_id': self.bar_freight_barcode_id.id, - 'bar_lot_id': self.bar_freight_lot_id.lot_id.id, - 'pad_freight_barcode_id': self.pad_freight_barcode_id.id, - 'pad_lot_id': self.pad_freight_lot_id.lot_id.id, - 'chuck_freight_barcode_id': self.chuck_freight_barcode_id.id, - 'chuck_lot_id': self.chuck_freight_lot_id.lot_id.id, - - 'after_assembly_functional_tool_name': self.after_assembly_functional_tool_name, - 'after_assembly_functional_tool_type_id': self.after_assembly_functional_tool_type_id.id, - 'after_assembly_functional_tool_diameter': self.after_assembly_functional_tool_diameter, - 'after_assembly_knife_tip_r_angle': self.after_assembly_knife_tip_r_angle, - 'after_assembly_new_former': self.after_assembly_new_former, - 'cut_time': self.cut_time, - 'cut_length': self.cut_length, - 'cut_number': self.cut_number, - 'after_assembly_whether_standard_knife': self.after_assembly_whether_standard_knife, - 'after_assembly_coarse_middle_thin': self.after_assembly_coarse_middle_thin, - 'after_assembly_max_lifetime_value': self.after_assembly_max_lifetime_value, - 'after_assembly_alarm_value': self.after_assembly_alarm_value, - 'after_assembly_used_value': self.after_assembly_used_value, - 'after_assembly_tool_loading_length': self.after_assembly_tool_loading_length, - 'after_assembly_handle_length': self.after_assembly_handle_length, - 'after_assembly_functional_tool_length': self.after_assembly_functional_tool_length, - 'after_assembly_effective_length': self.after_assembly_effective_length, - 'L_D_number': self.L_D_number, - 'hiding_length': self.hiding_length, - 'assemble_status': '2', - 'tool_loading_person': self.env.user.name, - 'image': self.image, - 'tool_loading_time': fields.Datetime.now() - } - - def get_desc_2(self, stock_lot, functional_tool_assembly_id): - return { - 'barcode_id': stock_lot.id, - 'code': self.code, - 'name': self.after_name_id.name, - 'tool_name_id': self.after_name_id.id, - 'rfid': self.rfid, - 'tool_groups_id': self.after_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 functional_tool_assembly(self): + # """ + # 功能刀具组装 + # :return: + # """ + # logging.info('功能刀具开始组装!') + # # 获取组装单对象 + # functional_tool_assembly = self.env['sf.functional.tool.assembly'].search([ + # ('assembly_order_code', '=', self.assembly_order_code), + # ('machine_tool_name_id', '=', self.machine_tool_name_id.id), + # ('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id), + # ('assemble_status', '=', '0'), + # ]) + # # 对物料做必填判断 + # self.materials_must_be_judged() + # + # product_id = self.env['product.product'] + # # 创建组装入库单 + # # 创建功能刀具批次/序列号记录 + # stock_lot = product_id.create_assemble_warehouse_receipt(self.id, functional_tool_assembly, self) + # # 封装功能刀具数据,用于更新组装单信息 + # desc_1 = self.get_desc_1(stock_lot) + # # 封装功能刀具数据,用于创建功能刀具记录 + # desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly) + # # 创建功能刀具组装入库单 + # self.env['stock.picking'].create_tool_stocking_picking(stock_lot, functional_tool_assembly, 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.after_name_id.id + # }, record_1) + # + # # =====================修改功能刀具组装单、机床换刀申请、CAM工单程序用刀计划的状态============== + # # 修改功能刀具组装单信息 + # functional_tool_assembly.write(desc_1) + # if functional_tool_assembly.sf_machine_table_tool_changing_apply_id: + # # 修改机床换刀申请的状态 + # self.env['sf.machine.table.tool.changing.apply'].sudo().search([ + # ('id', '=', functional_tool_assembly.sf_machine_table_tool_changing_apply_id.id) + # ]).write({'status': '3'}) + # elif functional_tool_assembly.sf_cam_work_order_program_knife_plan_id: + # # 修改CAM工单程序用刀计划状态 + # cam_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().search([ + # ('id', '=', functional_tool_assembly.sf_cam_work_order_program_knife_plan_id.id) + # ]) + # cam_plan.write({'plan_execute_status': '2'}) + # + # logging.info('功能刀具组装完成!') + # + # # 关闭弹出窗口 + # return {'type': 'ir.actions.act_window_close'} + # + # def materials_must_be_judged(self): + # """ + # 功能刀具组装必填判断 + # """ + # # 物料必填校验 + # if not self.handle_code_id: + # raise ValidationError('缺少【刀柄】物料信息!') + # if not self.integral_product_id and not self.blade_product_id: + # raise ValidationError('【整体式刀具】和【刀片】必须填写一个!') + # if self.blade_product_id: + # if not self.bar_product_id and not self.pad_product_id: + # raise ValidationError('【刀盘】和【刀杆】必须填写一个!') + # # 组装参数必填校验 + # if self.after_assembly_functional_tool_length == 0: + # raise ValidationError('组装参数信息【伸出长】不能为0!') + # if self.after_assembly_max_lifetime_value == 0: + # raise ValidationError('组装参数信息【最大寿命值】不能为0!') + # if self.after_assembly_alarm_value == 0: + # raise ValidationError('组装参数信息【报警值】不能为0!') + # # if self.after_assembly_effective_length == 0: + # # raise ValidationError('组装参数信息【有效长】不能为0!!!') + # # if self.hiding_length == 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_1(self, stock_lot): + # return { + # 'start_preset_bool': False, + # 'barcode_id': stock_lot.id, + # 'code': self.code, + # 'rfid': self.rfid, + # 'tool_groups_id': self.after_tool_groups_id.id, + # 'handle_code_id': self.handle_code_id.id, + # 'integral_freight_barcode_id': self.integral_freight_barcode_id.id, + # 'integral_lot_id': self.integral_freight_lot_id.lot_id.id, + # 'blade_freight_barcode_id': self.blade_freight_barcode_id.id, + # 'blade_lot_id': self.blade_freight_lot_id.lot_id.id, + # 'bar_freight_barcode_id': self.bar_freight_barcode_id.id, + # 'bar_lot_id': self.bar_freight_lot_id.lot_id.id, + # 'pad_freight_barcode_id': self.pad_freight_barcode_id.id, + # 'pad_lot_id': self.pad_freight_lot_id.lot_id.id, + # 'chuck_freight_barcode_id': self.chuck_freight_barcode_id.id, + # 'chuck_lot_id': self.chuck_freight_lot_id.lot_id.id, + # + # 'after_assembly_functional_tool_name': self.after_assembly_functional_tool_name, + # 'after_assembly_functional_tool_type_id': self.after_assembly_functional_tool_type_id.id, + # 'after_assembly_functional_tool_diameter': self.after_assembly_functional_tool_diameter, + # 'after_assembly_knife_tip_r_angle': self.after_assembly_knife_tip_r_angle, + # 'after_assembly_new_former': self.after_assembly_new_former, + # 'cut_time': self.cut_time, + # 'cut_length': self.cut_length, + # 'cut_number': self.cut_number, + # 'after_assembly_whether_standard_knife': self.after_assembly_whether_standard_knife, + # 'after_assembly_coarse_middle_thin': self.after_assembly_coarse_middle_thin, + # 'after_assembly_max_lifetime_value': self.after_assembly_max_lifetime_value, + # 'after_assembly_alarm_value': self.after_assembly_alarm_value, + # 'after_assembly_used_value': self.after_assembly_used_value, + # 'after_assembly_tool_loading_length': self.after_assembly_tool_loading_length, + # 'after_assembly_handle_length': self.after_assembly_handle_length, + # 'after_assembly_functional_tool_length': self.after_assembly_functional_tool_length, + # 'after_assembly_effective_length': self.after_assembly_effective_length, + # 'L_D_number': self.L_D_number, + # 'hiding_length': self.hiding_length, + # 'assemble_status': '1', + # 'tool_loading_person': self.env.user.name, + # 'image': self.image, + # 'tool_loading_time': fields.Datetime.now() + # } + # + # def get_desc_2(self, stock_lot, functional_tool_assembly_id): + # return { + # 'barcode_id': stock_lot.id, + # 'code': self.code, + # 'name': self.after_name_id.name, + # 'tool_name_id': self.after_name_id.id, + # 'rfid': self.rfid, + # 'tool_groups_id': self.after_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, + # } -class StockPicking(models.Model): - _inherit = 'stock.picking' - - def create_tool_stocking_picking(self, stock_lot, functional_tool_assembly, 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': functional_tool_assembly.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.after_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 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: - # 修改刀柄序列号状态为【在用】 - 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: - datas['data'].append( - {'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_freight_lot_id.lot_id}) - if obj.blade_product_id: - datas['data'].append( - {'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_freight_lot_id.lot_id}) - if obj.bar_product_id: - datas['data'].append( - {'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_freight_lot_id.lot_id}) - if obj.pad_product_id: - datas['data'].append( - {'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_freight_lot_id.lot_id}) - if obj.chuck_product_id: - datas['data'].append( - {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_freight_lot_id.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: - # 创建库存移动记录 - 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, tool_assembly_order_id, functional_tool_assembly, 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) diff --git a/sf_tool_management/wizard/wizard_view.xml b/sf_tool_management/wizard/wizard_view.xml index b5c2f82e..53a45d85 100644 --- a/sf_tool_management/wizard/wizard_view.xml +++ b/sf_tool_management/wizard/wizard_view.xml @@ -427,10 +427,10 @@
-
From 388929f207441611da4e51b764f637e5fb6f68be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Thu, 5 Sep 2024 17:59:55 +0800 Subject: [PATCH 023/217] =?UTF-8?q?=E6=89=AB=E7=A0=81=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E6=8C=89=E9=94=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../static/src/js/custom_barcode_handlers.js | 60 +++++++++++++++++++ sf_base/static/src/js/remove_focus.js | 25 +++++++- sf_manufacturing/__manifest__.py | 1 + .../js/workpiece_delivery_wizard_confirm.js | 2 +- sf_manufacturing/views/mrp_workorder_view.xml | 4 +- .../wizard/workpiece_delivery_views.xml | 6 +- .../wizard/workpiece_delivery_wizard.py | 7 +++ 7 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 sf_base/static/src/js/custom_barcode_handlers.js diff --git a/sf_base/static/src/js/custom_barcode_handlers.js b/sf_base/static/src/js/custom_barcode_handlers.js new file mode 100644 index 00000000..2f988d0b --- /dev/null +++ b/sf_base/static/src/js/custom_barcode_handlers.js @@ -0,0 +1,60 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { barcodeGenericHandlers } from '@barcodes/barcode_handlers'; +import { patch } from "@web/core/utils/patch"; + +// 定义新的 clickOnButton 函数 +function customClickOnButton(selector) { + console.log("This is the custom clickOnButton function!"); + + const buttons = document.body.querySelectorAll(selector); + + let length = buttons.length; + if (length > 0) { + buttons[length - 1].click(); + } else { + console.warn(`Button with selector ${selector} not found`); + } +} + +patch(barcodeGenericHandlers, "start", { + start(env, { ui, barcode, notification }) { + // 使用新定义的 clickOnButton 函数 + const COMMANDS = { + "O-CMD.EDIT": () => customClickOnButton(".o_form_button_edit"), + "O-CMD.DISCARD": () => customClickOnButton(".o_form_button_cancel"), + "O-CMD.SAVE": () => customClickOnButton(".o_form_button_save"), + "O-CMD.PREV": () => customClickOnButton(".o_pager_previous"), + "O-CMD.NEXT": () => customClickOnButton(".o_pager_next"), + "O-CMD.PAGER-FIRST": () => updatePager("first"), + "O-CMD.PAGER-LAST": () => updatePager("last"), + "O-CMD.CONFIRM": () => customClickOnButton(".jikimo_button_confirm"), + + barcode.bus.addEventListener("barcode_scanned", (ev) => { + const barcode = ev.detail.barcode; + if (barcode.startsWith("O-BTN.")) { + let targets = []; + try { + targets = getVisibleElements(ui.activeElement, `[barcode_trigger=${barcode.slice(6)}]`); + } catch (_e) { + console.warn(`Barcode '${barcode}' is not valid`); + } + for (let elem of targets) { + elem.click(); + } + } + if (barcode.startsWith("O-CMD.")) { + const fn = COMMANDS[barcode]; + if (fn) { + fn(); + } else { + notification.add(env._t("Barcode: ") + `'${barcode}'`, { + title: env._t("Unknown barcode command"), + type: "danger" + }); + } + } + }); + } +}) diff --git a/sf_base/static/src/js/remove_focus.js b/sf_base/static/src/js/remove_focus.js index 3e4cbb22..9a524360 100644 --- a/sf_base/static/src/js/remove_focus.js +++ b/sf_base/static/src/js/remove_focus.js @@ -5,9 +5,12 @@ import { registry } from '@web/core/registry'; import { formView } from '@web/views/form/form_view'; import { FormController } from '@web/views/form/form_controller'; +import { listView } from '@web/views/list/list_view'; +import { ListController } from '@web/views/list/list_controller' + import { onRendered, onMounted } from "@odoo/owl"; -export class RemoveFocusController extends FormController { +export class RemoveFocusFormController extends FormController { setup() { super.setup(); @@ -17,7 +20,23 @@ export class RemoveFocusController extends FormController { } } -registry.category('views').add('remove_focus_view', { +registry.category('views').add('remove_focus_form_view', { ...formView, - Controller: RemoveFocusController, + Controller: RemoveFocusFormController, +}); + + +export class RemoveFocusListController extends ListController { + setup() { + super.setup(); + + onMounted(() => { + this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur()); + }) + } +} + +registry.category('views').add('remove_focus_list_view', { + ...listView, + Controller: RemoveFocusListController, }); \ No newline at end of file diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 35501367..8cf16106 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -45,6 +45,7 @@ 'sf_manufacturing/static/src/scss/kanban_change.scss', 'sf_manufacturing/static/src/xml/button_show_on_tree.xml', 'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js', + 'sf_manufacturing/static/src/js/custom_barcode_handlers.js', ] }, diff --git a/sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js b/sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js index 236836ab..d7900984 100644 --- a/sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js +++ b/sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js @@ -10,7 +10,7 @@ odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) { title: "确认", $content: $('
').append("请确认是否仅配送" + params.workorder_count + "个工件?"), buttons: [ - { text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) }, + { text: "确认", classes: 'btn-primary jikimo_button_confirm', close: true, click: () => dispatchConfirmed(parent, params) }, { text: "取消", close: true }, ], }); diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index a802266f..aefa2ac5 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -659,9 +659,9 @@ 工件配送 sf.workpiece.delivery - +
-
sf.workpiece.delivery.wizard.form.view sf.workpiece.delivery.wizard -
+ @@ -18,8 +18,8 @@
-
diff --git a/sf_manufacturing/wizard/workpiece_delivery_wizard.py b/sf_manufacturing/wizard/workpiece_delivery_wizard.py index 5d5dc889..67fb6035 100644 --- a/sf_manufacturing/wizard/workpiece_delivery_wizard.py +++ b/sf_manufacturing/wizard/workpiece_delivery_wizard.py @@ -180,6 +180,13 @@ class WorkpieceDeliveryWizard(models.TransientModel): self.feeder_station_destination_id = self.route_id.end_site_id.id def on_barcode_scanned(self, barcode): + # 判断barcode是否是数字 + if not barcode.isdigit(): + # 判断是否是AGV接驳站名称 + agv_site = self.env['sf.agv.site'].search([('name', '=', barcode)]) + if agv_site: + self.feeder_station_start_id = agv_site.id + return delivery_type = self.env.context.get('default_delivery_type') if delivery_type == '上产线': workorder = self.env['mrp.workorder'].search( From d123ca51737c8d64b6563980582ffe764ac1c19b Mon Sep 17 00:00:00 2001 From: hujiaying Date: Fri, 6 Sep 2024 09:13:38 +0800 Subject: [PATCH 024/217] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=97=A5=E6=9C=89?= =?UTF-8?q?=E6=95=88=E5=B7=A5=E4=BD=9C=E6=97=B6=E9=95=BF=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workcenter.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 6af8fc85..7183b755 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -133,11 +133,11 @@ class ResWorkcenter(models.Model): available_machine_number = fields.Integer(string="可用机台数量") single_machine_capacity = fields.Float(string="单台小时产能") production_line_hour_capacity = fields.Float(string="生产线小时产能", readonly=True, - _compute='_compute_production_line_hour_capacity', store=True) + compute='_compute_production_line_hour_capacity') effective_working_hours_day = fields.Float(string="日有效工作时长", default=0, readonly=True, - _compute='_compute_effective_working_hours_day', store=True) + compute='_compute_effective_working_hours_day') default_capacity = fields.Float( - '生产线日产能', default=2.0, _compute='_compute_production_line_day_capacity', store=True, readonly=True) + '生产线日产能', compute='_compute_production_line_day_capacity', readonly=True) # 计算生产线日产能 @api.depends('production_line_hour_capacity', 'effective_working_hours_day') @@ -147,11 +147,12 @@ class ResWorkcenter(models.Model): record.production_line_hour_capacity * record.effective_working_hours_day, 2) # 计算日有效工作时长 - @api.depends('attendance_ids', 'attendance_ids.hour_to', 'attendance_ids.hour_from') + @api.depends('resource_calendar_id', 'resource_calendar_id.attendance_ids', + 'resource_calendar_id.attendance_ids.hour_to', 'resource_calendar_id.attendance_ids.hour_from') def _compute_effective_working_hours_day(self): for record in self: - attendance_ids = record.resource_calendar_id.attendance_ids.filter( - lambda r: r.dayofweek == datetime.now().weekday()) + attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if + p.dayofweek == self.get_current_day_of_week()] if attendance_ids: for attendance_id in attendance_ids: if attendance_id.hour_from and attendance_id.hour_to: @@ -159,6 +160,11 @@ class ResWorkcenter(models.Model): else: record.effective_working_hours_day = 0 + def get_current_day_of_week(self): + + day_num = datetime.datetime.now().weekday() + return str(day_num) + # 计算生产线小时产能 @api.depends('single_machine_capacity', 'available_machine_number') def _compute_production_line_hour_capacity(self): From 096542c8ffaab661324758f0dde383c15692c24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Fri, 6 Sep 2024 09:37:05 +0800 Subject: [PATCH 025/217] =?UTF-8?q?=E6=89=AB=E7=A0=81=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E6=8C=89=E9=94=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_base/static/src/js/custom_barcode_handlers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/sf_base/static/src/js/custom_barcode_handlers.js b/sf_base/static/src/js/custom_barcode_handlers.js index 2f988d0b..220d2cbb 100644 --- a/sf_base/static/src/js/custom_barcode_handlers.js +++ b/sf_base/static/src/js/custom_barcode_handlers.js @@ -30,6 +30,7 @@ patch(barcodeGenericHandlers, "start", { "O-CMD.PAGER-FIRST": () => updatePager("first"), "O-CMD.PAGER-LAST": () => updatePager("last"), "O-CMD.CONFIRM": () => customClickOnButton(".jikimo_button_confirm"), + }; barcode.bus.addEventListener("barcode_scanned", (ev) => { const barcode = ev.detail.barcode; From 2d5ef0aae4888a3e999659958ab461ab9f7ebe12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Fri, 6 Sep 2024 15:34:30 +0800 Subject: [PATCH 026/217] =?UTF-8?q?=E8=A7=A3=E9=99=A4=E8=A3=85=E5=A4=B9?= =?UTF-8?q?=E8=B5=B7=E7=82=B9=E4=BB=8E=E4=B8=8B=E4=BA=A7=E7=BA=BFagv?= =?UTF-8?q?=E8=B0=83=E5=BA=A6=E4=BB=BB=E5=8A=A1=E5=85=B3=E8=81=94=E5=B8=A6?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/agv_scheduling.py | 14 ++++++++++++++ sf_manufacturing/models/mrp_workorder.py | 10 ++++++++-- .../wizard/workpiece_delivery_wizard.py | 7 +++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/sf_manufacturing/models/agv_scheduling.py b/sf_manufacturing/models/agv_scheduling.py index d232c643..488fa0a0 100644 --- a/sf_manufacturing/models/agv_scheduling.py +++ b/sf_manufacturing/models/agv_scheduling.py @@ -265,3 +265,17 @@ class ResMrpWorkOrder(models.Model): 'sf_agv_scheduling_mrp_workorder_ref', string='AGV调度', domain=[('state', '!=', '已取消')]) + + def get_down_product_agv_scheduling(self): + """ + 获取关联的制造订单下产线的agv任务 + """ + workorder_ids = self.production_id.workorder_ids + cnc_workorder = workorder_ids.filtered( + lambda w: w.routing_type == 'CNC加工' and w.state == 'done' and w.processing_panel == self.processing_panel + ) + if cnc_workorder: + agv_schedulingss = cnc_workorder.agv_scheduling_ids + return agv_schedulingss.filtered(lambda a: a.state == '已配送' and a.agv_route_type == '下产线') + else: + return None diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index c3842d30..976097e3 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1340,7 +1340,7 @@ class ResMrpWorkOrder(models.Model): 'name': 'button_delivery', 'type': 'object', 'string': '解除装夹', - 'class': 'btn-primary', + 'class': 'btn-primary jikimo_button_confirm', # 'className': 'btn-primary', 'modifiers': '{"force_show": 1}' }) @@ -1355,6 +1355,7 @@ class ResMrpWorkOrder(models.Model): workorder_ids = [] delivery_type = '运送空料架' max_num = 4 # 最大配送数量 + feeder_station_start_id = False if len(self) > max_num: raise UserError('仅限于拆卸1-4个制造订单,请重新选择') for item in self: @@ -1363,6 +1364,10 @@ class ResMrpWorkOrder(models.Model): production_ids.append(item.production_id.id) workorder_ids.append(item.id) + if not feeder_station_start_id: + down_product_agv_scheduling = self.get_down_product_agv_scheduling() + if down_product_agv_scheduling: + feeder_station_start_id = down_product_agv_scheduling.end_site_id.id return { 'name': _('确认'), 'type': 'ir.actions.act_window', @@ -1375,7 +1380,8 @@ class ResMrpWorkOrder(models.Model): 'default_delivery_type': delivery_type, 'default_workorder_ids': [(6, 0, workorder_ids)], 'default_workcenter_id': self.env.context.get('default_workcenter_id'), - 'default_confirm_button': '确认解除' + 'default_confirm_button': '确认解除', + 'default_feeder_station_start_id': feeder_station_start_id, }} diff --git a/sf_manufacturing/wizard/workpiece_delivery_wizard.py b/sf_manufacturing/wizard/workpiece_delivery_wizard.py index 67fb6035..f00ac499 100644 --- a/sf_manufacturing/wizard/workpiece_delivery_wizard.py +++ b/sf_manufacturing/wizard/workpiece_delivery_wizard.py @@ -210,6 +210,13 @@ class WorkpieceDeliveryWizard(models.TransientModel): # 将对象添加到对应的同模型且是多对多类型里 self.production_ids |= workorder.production_id self.workorder_ids |= workorder + + if not self.feeder_station_start_id: + down_product_agv_scheduling = self.get_down_product_agv_scheduling() + if down_product_agv_scheduling: + self.feeder_station_start_id = down_product_agv_scheduling.end_site_id.id else: raise UserError('该rfid码对应的工单不存在') return + + From 4560bbc0ed38f9fdf4dc3ff185f8ad254b3aa035 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Fri, 6 Sep 2024 17:48:27 +0800 Subject: [PATCH 027/217] =?UTF-8?q?sf=E8=AE=A1=E5=88=92=E6=8E=92=E7=A8=8B?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E7=94=9F=E4=BA=A7=E7=BA=BF=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=94=9F=E4=BA=A7=E7=BA=BF=EF=BC=8C=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=E6=8E=92=E7=A8=8B=E6=97=B6=E9=97=B4=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=9C=A8=E5=B7=A5=E4=BD=9C=E6=97=B6=E9=97=B4=E5=86=85=EF=BC=8C?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E7=94=9F=E4=BA=A7=E7=BA=BF=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E6=9C=89=E5=8F=AF=E6=8E=92=E7=A8=8B=E7=9A=84=E8=B5=84=E6=BA=90?= =?UTF-8?q?=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workcenter.py | 47 +++++++++++++++++++++-- sf_plan/models/custom_plan.py | 25 +++++++++++- sf_plan/wizard/action_plan_some.py | 2 +- 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 7183b755..003d1b2b 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -1,4 +1,5 @@ import datetime +from datetime import time from collections import defaultdict from odoo import fields, models, api from odoo.addons.resource.models.resource import Intervals @@ -152,7 +153,7 @@ class ResWorkcenter(models.Model): def _compute_effective_working_hours_day(self): for record in self: attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if - p.dayofweek == self.get_current_day_of_week()] + p.dayofweek == self.get_current_day_of_week(datetime.datetime.now())] if attendance_ids: for attendance_id in attendance_ids: if attendance_id.hour_from and attendance_id.hour_to: @@ -160,9 +161,9 @@ class ResWorkcenter(models.Model): else: record.effective_working_hours_day = 0 - def get_current_day_of_week(self): - - day_num = datetime.datetime.now().weekday() + # 获取传入时间是星期几 + def get_current_day_of_week(self, datetime): + day_num = datetime.weekday() return str(day_num) # 计算生产线小时产能 @@ -172,6 +173,44 @@ class ResWorkcenter(models.Model): record.production_line_hour_capacity = round( record.single_machine_capacity * record.available_machine_number, 2) + # 判断计划开始时间是否在配置的工作中心的工作日历内 + def deal_with_workcenter_calendar(self, start_date): + for record in self: + attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if + p.dayofweek == record.get_current_day_of_week(start_date) and self.is_between_times( + p.hour_from, p.hour_to, start_date)] + return False if not attendance_ids else True + + # 判断传入时间是否在配置的工作中心的工作日历内 + def is_between_times(self, hour_from, hour_to, start_date): + integer_part, decimal_part = self.get_integer_and_decimal_parts(hour_from) + start_time = time(integer_part, decimal_part) + integer_part, decimal_part = self.get_integer_and_decimal_parts(hour_to) + end_time = time(integer_part, decimal_part) + return start_time <= start_date.time() <= end_time + + # 获取整数部分和小数部分 + def get_integer_and_decimal_parts(self, value): + integer_part = int(value) + decimal_part = value - integer_part + return int(integer_part), int(decimal_part) + + ## 处理生产线的是否有可排程的资源 + def deal_production_lines_available(self, date_planned_start): + for record in self: + # 自动生产线工单 + date_planned_end = date_planned_start + datetime.datetime.timedelta(hours=1) + workorder_ids = record.env['mrp.workorder'].sudo().search( + [('workcenter_id', '=', record.id), ('date_planned_start', '>=', datetime), + ('date_planned_start', '<=', date_planned_end)]) + if not workorder_ids: + return True + sum_qty = sum([p.qty_produced for p in workorder_ids]) + if sum_qty >= record.default_capacity: + return False + + return True + class ResWorkcenterProductivity(models.Model): _inherit = 'mrp.workcenter.productivity' diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py index 0e56bf22..a1c57f2a 100644 --- a/sf_plan/models/custom_plan.py +++ b/sf_plan/models/custom_plan.py @@ -191,7 +191,7 @@ class sf_production_plan(models.Model): return num - def do_production_schedule(self): + def do_production_schedule(self, date_planned_start): """ 排程方法 """ @@ -199,6 +199,10 @@ class sf_production_plan(models.Model): if not record.production_line_id: raise ValidationError("未选择生产线") else: + date_planned_start = date_planned_start + timedelta(hours=8) # 转换为北京时间 + is_schedule = self.deal_processing_schedule(date_planned_start) + if not is_schedule: + raise ValidationError("排程失败") workorder_id_list = record.production_id.workorder_ids.ids if record.production_id: if record.production_id.workorder_ids: @@ -249,6 +253,25 @@ class sf_production_plan(models.Model): 'target': 'current', # 跳转的目标窗口,可以是'current'或'new' } + # 处理是否可排程 + def deal_processing_schedule(self, date_planned_start): + for record in self: + workcenter_ids = record.production_line_id.mrp_workcenter_ids + if not workcenter_ids: + raise UserError('生产线没有配置工作中心') + production_lines = workcenter_ids.filtered(lambda b: "自动生产线" in b.name) + if not production_lines: # 判断是否配置了自动生产线 + raise UserError('生产线没有配置自动生产线') + if all(not production_line.deal_with_workcenter_calendar(date_planned_start) for production_line in + production_lines): # 判断计划开始时间是否在配置的工作中心的工作日历内 + raise UserError('当前计划开始时间不能预约排程') + if not production_lines.deal_production_lines_available(date_planned_start): # 判断生产线是否可排程 + raise UserError('当前计划开始时间不能预约排程,生产线没有可排程的资源') + return True + + + + def calculate_plan_time_before(self, item, workorder_id_list): """ 根据CNC工单的时间去计算之前的其他工单的开始结束时间 diff --git a/sf_plan/wizard/action_plan_some.py b/sf_plan/wizard/action_plan_some.py index 706714ee..9410efd5 100644 --- a/sf_plan/wizard/action_plan_some.py +++ b/sf_plan/wizard/action_plan_some.py @@ -36,7 +36,7 @@ class Action_Plan_All_Wizard(models.TransientModel): plan_obj = self.env['sf.production.plan'].browse(plan.id) plan_obj.production_line_id = self.production_line_id.id plan.date_planned_start = self.date_planned_start - plan_obj.do_production_schedule() + plan_obj.do_production_schedule(self.date_planned_start) # plan_obj.state = 'done' print('处理计划:', plan.id, '完成') From 1eae92f2b245066c05063a5bf370b61684a1a066 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Fri, 6 Sep 2024 17:55:59 +0800 Subject: [PATCH 028/217] =?UTF-8?q?1=E3=80=81=E5=8A=9F=E8=83=BD=E5=88=80?= =?UTF-8?q?=E5=85=B7=E7=BB=84=E8=A3=85=E5=8D=95=E6=B7=BB=E5=8A=A0=E7=89=A9?= =?UTF-8?q?=E6=96=99=E6=A0=A1=E9=AA=8C=E6=A0=87=E7=AD=BE=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E3=80=81=E6=96=B0=E5=A2=9E=E6=89=AB=E6=8F=8F=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E3=80=81=E4=BC=98=E5=8C=96=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E6=96=B9=E6=B3=95=EF=BC=9B2=E3=80=81?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0=E6=97=A7=E3=80=81=E7=B2=97=E4=B8=AD?= =?UTF-8?q?=E7=B2=BE=E5=AD=97=E6=AE=B5=E5=80=BC=E6=A0=B9=E6=8D=AE=E6=89=80?= =?UTF-8?q?=E9=80=89=E7=89=A9=E6=96=99=E8=87=AA=E5=8A=A8=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E4=BC=B8=E5=87=BA=E9=95=BF=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=AE=A1=E7=AE=97=E6=96=B9=E6=B3=95=EF=BC=9B3?= =?UTF-8?q?=E3=80=81=E5=8E=BB=E9=99=A4=E9=83=A8=E5=88=86=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E6=A0=A1=E9=AA=8C=E6=96=B9=E6=B3=95=EF=BC=9B4?= =?UTF-8?q?=E3=80=81=E7=BB=84=E8=A3=85=E5=8D=95form=E3=80=81tree=E8=A7=86?= =?UTF-8?q?=E5=9B=BE=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4=EF=BC=8Ctree?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E7=8A=B6=E6=80=81=E5=AD=97=E6=AE=B5=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=83=8C=E6=99=AF=E9=A2=9C=E8=89=B2=EF=BC=8C=E5=8F=98?= =?UTF-8?q?=E6=9B=B4=E7=AD=9B=E9=80=89=E6=9D=A1=E4=BB=B6=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/models/base.py | 98 ++++++++++++-- sf_tool_management/views/tool_base_views.xml | 132 ++++++++++--------- 2 files changed, 156 insertions(+), 74 deletions(-) diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 2255f68f..c5d8d831 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -349,9 +349,42 @@ 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: + 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 + record.handle_verify = True + else: + raise ValidationError('刀具选择错误,请重新确认!!!') + else: + location = self.env['sf.shelf.location'].sudo().search([('barcode', '=', barcode)]) + if location: + if location == record.integral_freight_barcode_id: + record.integral_verify = True + elif location == record.blade_freight_barcode_id: + record.blade_verify = True + elif location == record.bar_freight_barcode_id: + record.bar_verify = True + elif location == record.pad_freight_barcode_id: + record.pad_verify = True + elif location == record.chuck_freight_barcode_id: + record.chuck_verify = True + else: + raise ValidationError('刀具选择错误,请重新确认!') + else: + raise ValidationError(f'扫描为【{barcode}】的刀具不存在,请重新扫描!') @api.depends('functional_tool_name') def _compute_name(self): @@ -390,6 +423,8 @@ class FunctionalToolAssembly(models.Model): '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 # 刀具组 @@ -397,7 +432,6 @@ class FunctionalToolAssembly(models.Model): 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 # 最大寿命 functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', @@ -451,6 +485,7 @@ 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.onchange('integral_freight_barcode_id') def _onchange_integral_freight_barcode_id(self): @@ -478,6 +513,7 @@ 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.onchange('blade_freight_barcode_id') def _onchange_blade_freight_barcode_id(self): @@ -505,6 +541,7 @@ 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') + brand_verify = fields.Boolean('刀杆校验', default=False) @api.onchange('bar_freight_barcode_id') def _onchange_bar_freight_barcode_id(self): @@ -532,6 +569,7 @@ 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.onchange('pad_freight_barcode_id') def _onchange_pad_freight_barcode_id(self): @@ -558,6 +596,7 @@ class FunctionalToolAssembly(models.Model): 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): @@ -584,6 +623,7 @@ 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.onchange('chuck_freight_barcode_id') def _onchange_chuck_freight_barcode_id(self): @@ -630,14 +670,16 @@ class FunctionalToolAssembly(models.Model): 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') + 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='组装后粗/中/精', default='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)') @@ -645,7 +687,7 @@ class FunctionalToolAssembly(models.Model): 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_inventory_num') + 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) @@ -659,12 +701,44 @@ 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.assemble_status != '01': + return True + if item.after_assembly_tool_loading_length == 0: + raise ValidationError('【总长度】值为0,请重新测量!') + elif 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) + elif item.after_assembly_tool_loading_length < item.after_assembly_handle_length: + raise ValidationError('【总长度】不能小于【刀柄长度】') + + @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: @@ -724,7 +798,7 @@ class FunctionalToolAssembly(models.Model): def functional_tool_assembly(self): """ - 功能刀具组装 + 功能刀具确认组装 :return: """ logging.info('功能刀具开始组装!') @@ -777,6 +851,8 @@ class FunctionalToolAssembly(models.Model): """ 功能刀具组装必填判断 """ + # 物料准确性校验 + # 物料必填校验 if not self.handle_code_id: raise ValidationError('缺少【刀柄】物料信息!') @@ -790,8 +866,8 @@ class FunctionalToolAssembly(models.Model): raise ValidationError('组装参数信息【伸出长】不能为0!') if self.after_assembly_max_lifetime_value == 0: raise ValidationError('组装参数信息【最大寿命值】不能为0!') - if self.after_assembly_alarm_value == 0: - raise ValidationError('组装参数信息【报警值】不能为0!') + # if self.after_assembly_alarm_value == 0: + # raise ValidationError('组装参数信息【报警值】不能为0!') # if self.after_assembly_effective_length == 0: # raise ValidationError('组装参数信息【有效长】不能为0!!!') # if self.hiding_length == 0: diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index 8a3c3a73..87ddeec6 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -448,7 +448,11 @@ - + @@ -513,54 +517,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
@@ -641,17 +598,15 @@
- - - - - - - - - + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -734,10 +742,8 @@ - + - From 9a43af98c31801241df224d6220017ce02d835ce Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 9 Sep 2024 10:46:05 +0800 Subject: [PATCH 029/217] =?UTF-8?q?cloud=20=E4=B8=8E=20sf=20=E9=9D=99?= =?UTF-8?q?=E6=80=81=E8=B5=84=E6=BA=90=E5=90=8C=E6=AD=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_mrs_connect/models/sync_common.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index 2f77c7e6..e106bbf2 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -2,6 +2,8 @@ import logging import json import base64 +import traceback + import requests from odoo import models from odoo.exceptions import ValidationError @@ -73,7 +75,8 @@ class MrStaticResourceDataSync(models.Model): self.env['sf.feed.per.tooth'].sync_feed_per_tooth_yesterday() _logger.info("同步刀具物料每齿走刀量完成") except Exception as e: - logging.info("同步静态资源库失败:%s" % e) + traceback_error = traceback.format_exc() + logging.error("同步静态资源库失败:%s" % traceback_error) raise ValidationError("数据错误导致同步失败,请联系管理员") @@ -2759,8 +2762,9 @@ class CuttingToolBasicParameters(models.Model): if result['status'] == 1: if 'basic_parameters_integral_tool' in result['cutting_tool_basic_parameters_yesterday_list']: if result['cutting_tool_basic_parameters_yesterday_list']['basic_parameters_integral_tool']: - basic_parameters_integral_tool_list = json.loads( - result['cutting_tool_basic_parameters_yesterday_list']['basic_parameters_integral_tool']) + cutting_tool_basic_parameters_yesterday_list= result['cutting_tool_basic_parameters_yesterday_list'] + basic_parameters_integral_tool_list = cutting_tool_basic_parameters_yesterday_list['basic_parameters_integral_tool'] + if basic_parameters_integral_tool_list: for integral_tool_item in basic_parameters_integral_tool_list: integral_tool = self.search( From 37493a4688c1a30cebefbc036aa61bf274b7fc67 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Mon, 9 Sep 2024 14:08:39 +0800 Subject: [PATCH 030/217] =?UTF-8?q?sf=E8=AE=A1=E5=88=92=E6=8E=92=E7=A8=8B?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E6=8E=92=E7=A8=8B=E6=97=B6=E9=97=B4=E6=97=A5?= =?UTF-8?q?=E6=8E=92=E7=A8=8B=E7=9A=84=E5=88=B6=E9=80=A0=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E6=80=BB=E6=95=B0=E9=87=8F=E4=B8=8D=E8=83=BD=E8=B6=85=E8=BF=87?= =?UTF-8?q?=E4=BA=A7=E7=BA=BF=E6=97=A5=E4=BA=A7=E8=83=BD=E6=95=B0=E9=87=8F?= =?UTF-8?q?=EF=BC=8C=E8=AE=A1=E7=AE=97=E6=8E=92=E7=A8=8B=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=88=B6=E9=80=A0=E8=AE=A2=E5=8D=95=E6=AF=8F=E5=B0=8F=E6=97=B6?= =?UTF-8?q?=E6=8E=92=E7=A8=8B=E4=B8=8D=E8=83=BD=E8=B6=85=E8=BF=87=E4=BA=A7?= =?UTF-8?q?=E7=BA=BF=E5=B0=8F=E6=97=B6=E4=BA=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workcenter.py | 44 +++++++++++++------ .../views/mrp_workcenter_views.xml | 36 ++++++++++++--- sf_plan/models/custom_plan.py | 15 ++++--- 3 files changed, 67 insertions(+), 28 deletions(-) diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 003d1b2b..0a7e1d0e 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -1,8 +1,9 @@ import datetime -from datetime import time +from datetime import timedelta, time from collections import defaultdict from odoo import fields, models, api from odoo.addons.resource.models.resource import Intervals +from odoo.exceptions import UserError, ValidationError class ResWorkcenter(models.Model): @@ -138,7 +139,7 @@ class ResWorkcenter(models.Model): effective_working_hours_day = fields.Float(string="日有效工作时长", default=0, readonly=True, compute='_compute_effective_working_hours_day') default_capacity = fields.Float( - '生产线日产能', compute='_compute_production_line_day_capacity', readonly=True) + string='生产线日产能', compute='_compute_production_line_day_capacity', readonly=True) # 计算生产线日产能 @api.depends('production_line_hour_capacity', 'effective_working_hours_day') @@ -175,6 +176,7 @@ class ResWorkcenter(models.Model): # 判断计划开始时间是否在配置的工作中心的工作日历内 def deal_with_workcenter_calendar(self, start_date): + start_date = start_date + timedelta(hours=8) # 转换为北京时间 for record in self: attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if p.dayofweek == record.get_current_day_of_week(start_date) and self.is_between_times( @@ -195,20 +197,34 @@ class ResWorkcenter(models.Model): decimal_part = value - integer_part return int(integer_part), int(decimal_part) - ## 处理生产线的是否有可排程的资源 - def deal_production_lines_available(self, date_planned_start): - for record in self: - # 自动生产线工单 - date_planned_end = date_planned_start + datetime.datetime.timedelta(hours=1) - workorder_ids = record.env['mrp.workorder'].sudo().search( - [('workcenter_id', '=', record.id), ('date_planned_start', '>=', datetime), - ('date_planned_start', '<=', date_planned_end)]) - if not workorder_ids: - return True - sum_qty = sum([p.qty_produced for p in workorder_ids]) - if sum_qty >= record.default_capacity: + # 处理排程是否超过日产能 + def deal_available_default_capacity(self, date_planned): + date_planned_start = date_planned.strftime('%Y-%m-%d') + date_planned_end = date_planned + timedelta(days=1) + date_planned_end = date_planned_end.strftime('%Y-%m-%d') + plan_ids = self.env['sf.production.plan'].sudo().search([('date_planned_start', '>=', date_planned_start), + ('date_planned_start', '<', + date_planned_end), ('state', '!=', 'draft')]) + if plan_ids: + sum_qty = sum([p.product_qty for p in plan_ids]) + if sum_qty >= self.default_capacity: return False + return True + # 处理排程是否超过小时产能 + def deal_available_single_machine_capacity(self, date_planned): + + date_planned_start = date_planned.strftime('%Y-%m-%d %H:00:00') + date_planned_end = date_planned + timedelta(hours=1) + date_planned_end = date_planned_end.strftime('%Y-%m-%d %H:00:00') + plan_ids = self.env['sf.production.plan'].sudo().search([('date_planned_start', '>=', date_planned_start), + ('date_planned_start', '<', + date_planned_end), ('state', '!=', 'draft')]) + + if plan_ids: + sum_qty = sum([p.product_qty for p in plan_ids]) + if sum_qty >= self.single_machine_capacity: + return False return True diff --git a/sf_manufacturing/views/mrp_workcenter_views.xml b/sf_manufacturing/views/mrp_workcenter_views.xml index aad35d04..37ff8af5 100644 --- a/sf_manufacturing/views/mrp_workcenter_views.xml +++ b/sf_manufacturing/views/mrp_workcenter_views.xml @@ -36,15 +36,37 @@
- 产线日产能 + 1 + + + + - - - - - - + diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py index a1c57f2a..c4043d33 100644 --- a/sf_plan/models/custom_plan.py +++ b/sf_plan/models/custom_plan.py @@ -199,7 +199,7 @@ class sf_production_plan(models.Model): if not record.production_line_id: raise ValidationError("未选择生产线") else: - date_planned_start = date_planned_start + timedelta(hours=8) # 转换为北京时间 + is_schedule = self.deal_processing_schedule(date_planned_start) if not is_schedule: raise ValidationError("排程失败") @@ -262,16 +262,17 @@ class sf_production_plan(models.Model): production_lines = workcenter_ids.filtered(lambda b: "自动生产线" in b.name) if not production_lines: # 判断是否配置了自动生产线 raise UserError('生产线没有配置自动生产线') + if date_planned_start < datetime.now(): # 判断计划开始时间是否小于当前时间 + raise UserError('计划开始时间不能小于当前时间') if all(not production_line.deal_with_workcenter_calendar(date_planned_start) for production_line in production_lines): # 判断计划开始时间是否在配置的工作中心的工作日历内 - raise UserError('当前计划开始时间不能预约排程') - if not production_lines.deal_production_lines_available(date_planned_start): # 判断生产线是否可排程 - raise UserError('当前计划开始时间不能预约排程,生产线没有可排程的资源') + raise UserError('当前计划开始时间不能预约排程,请在工作时间内排程') + if not production_lines.deal_available_default_capacity(date_planned_start): # 判断生产线是否可排程 + raise UserError('当前计划开始时间不能预约排程,生产线今日没有可排程的资源') + if not production_lines.deal_available_single_machine_capacity(date_planned_start): # 判断生产线是否可排程 + raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源') return True - - - def calculate_plan_time_before(self, item, workorder_id_list): """ 根据CNC工单的时间去计算之前的其他工单的开始结束时间 From dba8d233485af72192c850734e1c879be22b8892 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 9 Sep 2024 17:11:33 +0800 Subject: [PATCH 031/217] =?UTF-8?q?=E9=94=80=E5=94=AE=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E4=BB=B7=E6=A0=BC=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_mrs_connect/models/res_config_setting.py | 24 +++++++-------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/sf_mrs_connect/models/res_config_setting.py b/sf_mrs_connect/models/res_config_setting.py index ead17af5..04693fa7 100644 --- a/sf_mrs_connect/models/res_config_setting.py +++ b/sf_mrs_connect/models/res_config_setting.py @@ -157,12 +157,7 @@ class ResConfigSettings(models.TransientModel): try: convert_sale_data = map(lambda data: {'name': data.name, 'order_lines': { - '%s/%s/%s/%s/±%s/' % ( - order_line.product_template_id.model_long, order_line.product_template_id.model_width, order_line.product_template_id.model_height, - order_line.product_template_id.model_volume, - order_line.product_template_id.model_machining_precision, - ): order_line.price_unit for order_line in - data.order_line + str(i): str(value.price_unit) for i, value in enumerate(data.order_line) } }, datas) @@ -182,17 +177,14 @@ class ResConfigSettings(models.TransientModel): if not res_order_lines_map: continue need_change_sale_order = self.env['sale.order'].sudo().search([('name', '=', need_change_sale_data.get('name'))]) - - for need_change_sale_order_line in need_change_sale_order.order_line: - order_line_uniq='%s/%s/%s/%s/±%s/' % ( - need_change_sale_order_line.product_template_id.model_long, need_change_sale_order_line.product_template_id.model_width, - need_change_sale_order_line.product_template_id.model_height, - need_change_sale_order_line.product_template_id.model_volume, - need_change_sale_order_line.product_template_id.model_machining_precision, - ) - if not res_order_lines_map.get(order_line_uniq): + for index,need_change_sale_order_line in enumerate(need_change_sale_order.order_line): + if not res_order_lines_map.get(str(index)): continue - need_change_sale_order_line.write({'price_unit': res_order_lines_map.get(order_line_uniq)}) + order_line = self.env['sale.order.line'].browse(need_change_sale_order_line.id) + new_price = res_order_lines_map.get(str(index)) + if order_line: + # 修改单价 + order_line.write({'remark': new_price}) else: logging.error('同步销售订单价格失败 {}'.format(response.text)) raise UserError('同步销售订单价格失败') From 7ac7077f8c2f4c5dce425bd2ccb012495602d36c Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 9 Sep 2024 17:36:23 +0800 Subject: [PATCH 032/217] =?UTF-8?q?bom=E7=89=A9=E6=96=99=E6=B8=85=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_base/views/tool_views.xml | 8 +- sf_tool_management/__manifest__.py | 7 +- sf_tool_management/models/__init__.py | 4 +- .../models/functional_cutting_tool_model.py | 6 ++ sf_tool_management/models/jikimo_bom.py | 96 +++++++++++++++++++ sf_tool_management/models/tool_inventory.py | 34 +++++++ .../security/ir.model.access.csv | 3 + sf_tool_management/views/jikimo_bom.xml | 51 ++++++++++ sf_tool_management/views/tool_inventory.xml | 15 +++ sf_tool_management/views/tool_views.xml | 18 ++++ sf_tool_management/wizard/__init__.py | 1 + .../wizard/jikimo_bom_wizard.py | 28 ++++++ .../wizard/jikimo_bom_wizard.xml | 33 +++++++ 13 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 sf_tool_management/models/functional_cutting_tool_model.py create mode 100644 sf_tool_management/models/jikimo_bom.py create mode 100644 sf_tool_management/models/tool_inventory.py create mode 100644 sf_tool_management/views/jikimo_bom.xml create mode 100644 sf_tool_management/views/tool_inventory.xml create mode 100644 sf_tool_management/views/tool_views.xml create mode 100644 sf_tool_management/wizard/jikimo_bom_wizard.py create mode 100644 sf_tool_management/wizard/jikimo_bom_wizard.xml diff --git a/sf_base/views/tool_views.xml b/sf_base/views/tool_views.xml index 87dbd94c..b90d1a5c 100644 --- a/sf_base/views/tool_views.xml +++ b/sf_base/views/tool_views.xml @@ -80,10 +80,10 @@ sf.cutter.function.tree sf.functional.cutting.tool.model - - - - + + + + diff --git a/sf_tool_management/__manifest__.py b/sf_tool_management/__manifest__.py index a1a88fb0..97c26e40 100644 --- a/sf_tool_management/__manifest__.py +++ b/sf_tool_management/__manifest__.py @@ -10,7 +10,7 @@ """, 'category': 'sf', 'website': 'https://www.sf.jikimo.com', - 'depends': ['sf_manufacturing'], + 'depends': ['sf_manufacturing', 'sf_base'], 'data': [ 'security/group_security.xml', 'security/ir.model.access.csv', @@ -24,6 +24,11 @@ 'views/menu_view.xml', 'views/stock.xml', 'data/tool_data.xml', + 'wizard/jikimo_bom_wizard.xml', + 'views/tool_inventory.xml', + 'views/jikimo_bom.xml', + 'views/tool_views.xml', + ], 'demo': [ ], diff --git a/sf_tool_management/models/__init__.py b/sf_tool_management/models/__init__.py index 90776ca7..e4f2a622 100644 --- a/sf_tool_management/models/__init__.py +++ b/sf_tool_management/models/__init__.py @@ -8,4 +8,6 @@ from . import fixture_material_search from . import fixture_enroll from . import temporary_data_processing_methods from . import stock - +from . import jikimo_bom +from . import tool_inventory +from . import functional_cutting_tool_model \ No newline at end of file diff --git a/sf_tool_management/models/functional_cutting_tool_model.py b/sf_tool_management/models/functional_cutting_tool_model.py new file mode 100644 index 00000000..9b5a5002 --- /dev/null +++ b/sf_tool_management/models/functional_cutting_tool_model.py @@ -0,0 +1,6 @@ +from odoo import models, fields + + +class SyncFunctionalCuttingToolModel(models.Model): + _inherit = 'sf.functional.cutting.tool.model' + cutting_tool_type_ids = fields.Many2many('sf.cutting.tool.type', string='整体式刀具物料') \ No newline at end of file diff --git a/sf_tool_management/models/jikimo_bom.py b/sf_tool_management/models/jikimo_bom.py new file mode 100644 index 00000000..5272d4f0 --- /dev/null +++ b/sf_tool_management/models/jikimo_bom.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +from xml import etree + +from odoo import models, fields, api, Command +from odoo.http import request + + +class jikimo_bom(models.Model): + _name = 'jikimo.bom' + _description = '功能刀具物料清单' + tool_inventory_id = fields.Many2one('sf.tool.inventory', '功能刀具清单') + tool_name = fields.Char(related="tool_inventory_id.name", string='功能刀具名称') + functional_cutting_tool_model_id = fields.Many2one(related='tool_inventory_id.functional_cutting_tool_model_id', + string='功能刀具类型') + tool_groups_id = fields.Many2one(related='tool_inventory_id.tool_groups_id', string='刀具组') + tool_length = fields.Float(related='tool_inventory_id.tool_length', string='刀具总长(mm)') + diameter = fields.Float(related='tool_inventory_id.diameter', string='直径(mm)') + angle = fields.Float(related='tool_inventory_id.angle', string='R角(mm)') + extension = fields.Float(related='tool_inventory_id.extension', string='伸出长度(mm)') + product_ids = fields.Many2many('product.product', string='产品') + knife_handle_model = fields.Selection(related='tool_inventory_id.knife_handle_model', string='使用刀柄型号') + options = fields.Char('产品清单') + + def name_get(self): + result = [] + for bom in self: + result.append((bom.id, '功能刀具物料清单')) + return result + def bom_product_domains(self, assembly_options): + self.options = assembly_options + cutting_tool_materials = self.env['sf.cutting.tool.material'].search( + [('name', 'in', assembly_options.split('+'))]) + domains = [] + for index, option in enumerate(cutting_tool_materials): + domain = ['&',('cutting_tool_material_id', '=', option.id), + ("cutting_tool_type_id", "in", + self.tool_inventory_id.functional_cutting_tool_model_id.cutting_tool_type_ids.ids)] + if option.name == '刀柄': + domain = ['&']+domain+[ ("cutting_tool_taper_shank_model", "=", self.tool_inventory_id.knife_handle_model)] + + if option.name == '整体式刀具': + domain=['&']+domain+[ + '|', + # 刀具直径 + ('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter), + + # r角 + ('cutting_tool_blade_tip_working_size', '=', self.tool_inventory_id.angle)] + if option.name == '刀杆': + domain = ['&'] + domain + [ + ("cutting_tool_cutter_arbor_diameter", "=", self.tool_inventory_id.diameter)] + if option.name == '刀片': + domain = ['&'] + domain + [ + ("cutting_tool_blade_tip_circular_arc_radius", "=", self.tool_inventory_id.angle)] + if option.name == '刀盘': + domain = ['&'] + domain + [ + ("cutting_tool_cutter_head_diameter", "=", self.tool_inventory_id.diameter)] + domains=domains+domain + if index != 0: + domains = ['|'] + domains + # wqwqwe = self.env['product.product'].search(ddd) + # product = self.env['product.product'].search(domain) + # if product: + # products = products + product + return domains + + def generate_bill_materials(self, assembly_options): + domains=self.bom_product_domains(assembly_options) + products = self.env['product.product'].search(domains) + if products: + self.product_ids = [Command.set(products.ids)] + # if option.name == '刀盘': + # hilt = self.env['product.product'].search( + # [('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter), + # ('cutting_tool_material_id', '=', option.id)]) + # self.product_ids = [Command.set(hilt.ids)]k + + +class jikimo_bom_line(models.Model): + _name = 'jikimo.bom.line' + _description = 'jikimo.bom.line' + + name = fields.Char() + + +class ProductProduct(models.Model): + _inherit = 'product.product' + + def search(self, args, offset=0, limit=None, order=None, count=False): + # 你可以在这里修改 `args` 以调整搜索条件 + # 例如,添加额外的搜索条件 + if self.env.context.get('jikimo_bom_product'): + bom_id = self.env['jikimo.bom'].browse(request.session.get('jikimo_bom_product').get('bom_id')) + domains = bom_id.bom_product_domains(bom_id.options) + args=args+domains + return super(ProductProduct, self).search(args, offset=offset, limit=limit, order=order, count=count) diff --git a/sf_tool_management/models/tool_inventory.py b/sf_tool_management/models/tool_inventory.py new file mode 100644 index 00000000..db15834a --- /dev/null +++ b/sf_tool_management/models/tool_inventory.py @@ -0,0 +1,34 @@ +from odoo import models, fields +from odoo.http import request + + +class ToolInventory(models.Model): + _inherit = 'sf.tool.inventory' + _description = '功能刀具清单' + knife_handle_model = fields.Selection([('BT30', 'BT30'), ('BT40', 'BT40'), ('BT50', 'BT50'), ('GSK30', 'GSK30'), ('GSK40', 'GSK40'), ('GSK50', 'GSK50')], string='使用刀柄型号') + jikimo_bom_ids = fields.One2many('jikimo.bom','tool_inventory_id', 'bom单') + def bom_mainfest(self): + + jikimo_bom_ids = self.mapped('jikimo_bom_ids') + if not jikimo_bom_ids: + self._bom_mainfest() + return self.bom_mainfest() + request.session['jikimo_bom_product'] = {'bom_id': int(self.jikimo_bom_ids)} + # context = dict(self.env.context) + # context.update({'jikimo_bom_product': self.jikimo_bom_ids.options}) + # if self.functional_cutting_tool_model_id.cutting_tool_type_ids: + # context.update({'jikimo_bom_product_cutting_tool_type': self.functional_cutting_tool_model_id.cutting_tool_type_ids.ids}) + return { + 'type': 'ir.actions.act_window', + 'name': '刀具组装清单', + 'res_model': 'jikimo.bom', + 'view_mode': 'form', + 'view_id': self.env.ref('sf_tool_management.view_jikimo_bom_form').id, + 'res_id': int(self.jikimo_bom_ids), + 'target': 'current', # Use 'new' to open in a new window/tab + # {'jikimo_bom_product': self.jikimo_bom_ids.options} + } + + # 创建bom单 + def _bom_mainfest(self): + self.env['jikimo.bom'].create({'tool_inventory_id':self.id}) \ No newline at end of file diff --git a/sf_tool_management/security/ir.model.access.csv b/sf_tool_management/security/ir.model.access.csv index 26b45aeb..8c188464 100644 --- a/sf_tool_management/security/ir.model.access.csv +++ b/sf_tool_management/security/ir.model.access.csv @@ -38,3 +38,6 @@ access_sf_fixture_material_search_group_plan_dispatch,sf.fixture.material.search access_sf_functional_tool_dismantle,sf.functional.tool.dismantle,model_sf_functional_tool_dismantle,base.group_user,1,1,1,0 access_sf_functional_tool_dismantle_group_sf_tool_user,sf.functional.tool.dismantle_group_sf_tool_user,model_sf_functional_tool_dismantle,sf_base.group_sf_tool_user,1,1,1,0 access_sf_functional_tool_dismantle_group_plan_dispatch,sf.functional.tool.dismantle_group_plan_dispatch,model_sf_functional_tool_dismantle,sf_base.group_plan_dispatch,1,0,0,0 + +access_jikimo_bom,jikimo.bom,model_jikimo_bom,base.group_user,1,1,1,1 +access_jikimo_bom_wizard,jikimo.bom.wizard,model_jikimo_bom_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/sf_tool_management/views/jikimo_bom.xml b/sf_tool_management/views/jikimo_bom.xml new file mode 100644 index 00000000..bb1454df --- /dev/null +++ b/sf_tool_management/views/jikimo_bom.xml @@ -0,0 +1,51 @@ + + + + bom物料清单 + jikimo.bom + tree,form + + + jikimo.bom.form + jikimo.bom + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
\ No newline at end of file diff --git a/sf_tool_management/views/tool_inventory.xml b/sf_tool_management/views/tool_inventory.xml new file mode 100644 index 00000000..f7fbc6b5 --- /dev/null +++ b/sf_tool_management/views/tool_inventory.xml @@ -0,0 +1,15 @@ + + + + sf.tool.inventory.inherit.tree + sf.tool.inventory + + + + +

+ + + + +
+

获取检测报告服务配置

+
+
+
+
+
+
+
diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index a5e20ff7..2e77bcdd 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -414,12 +414,10 @@ class ResMrpWorkOrder(models.Model): # 获取三次元检测点数据 def get_three_check_datas(self): - factory_nick_name = 'XT' ftp_resconfig = self.env['res.config.settings'].get_values() ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'], ftp_resconfig['ftp_password']) - # ftp.connect() local_dir_path = '/ftp/before' os.makedirs(local_dir_path, exist_ok=True) @@ -428,6 +426,17 @@ class ResMrpWorkOrder(models.Model): logging.info('local_file_path:%s' % local_file_path) remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename logging.info('remote_path:%s' % remote_path) + # if not ftp.file_exists(remote_path): + paload_data = { + "filename": local_filename + } + if not ftp_resconfig['get_check_file_path']: + raise UserError('请先配置获取检测报告地址') + url = ftp_resconfig['get_check_file_path'] + '/get/check/report' + response = requests.post(url, json=paload_data) + logging.info('response:%s' % response.json()) + if response.json().get('detail'): + raise UserError(response.json().get('detail')) if not ftp.file_exists(remote_path): raise UserError(f"文件不存在: {remote_path}") @@ -540,6 +549,7 @@ class ResMrpWorkOrder(models.Model): raise UserError('PT10点未测或数据错误') self.data_state = True + self.getcenter() return True diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 7cada1be..bb451f51 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -513,6 +513,11 @@ + +
@@ -552,6 +557,10 @@ +
+
@@ -572,6 +581,10 @@ +
+
@@ -592,6 +605,10 @@ +
+
@@ -610,6 +627,10 @@ +
+
From 908e4a3df619081bc0949b485fde01746ef7443d Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Wed, 11 Sep 2024 18:07:21 +0800 Subject: [PATCH 060/217] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index e4496553..92410fbd 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -378,7 +378,7 @@ class Sf_Dashboard_Connect(http.Controller): pass_rate = 1 if pass_nums: pass_rate = round( - (len(pass_nums) / len(plan_data_finish_orders) if len(plan_data_finish_orders) > 0 else 0), 3) + (len(pass_nums) / detection_data if len(plan_data_finish_orders) > 0 else 0), 3) # 返工率 rework_rate = round( @@ -413,6 +413,7 @@ class Sf_Dashboard_Connect(http.Controller): 'plan_data_finish_counts': plan_data_finish_counts, 'plan_data_plan_counts': plan_data_total_counts, 'plan_data_fault_counts': plan_data_fault_counts, + 'nopass_orders_counts': detection_data - len(pass_nums), 'finishe_rate': finishe_rate, 'plan_data_progress_deviation': plan_data_progress_deviation, 'plan_data_rework_counts': plan_data_rework_counts, From 95e49c7b0fad4f90c120ec4d0b6531a0330d9d90 Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Thu, 12 Sep 2024 09:17:52 +0800 Subject: [PATCH 061/217] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 92410fbd..e8c5c226 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -70,7 +70,7 @@ class Sf_Dashboard_Connect(http.Controller): # 获取当前时间的时间戳 current_timestamp = datetime.now().timestamp() - print(current_timestamp) + # print(current_timestamp) # tem_list = [ # "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3", @@ -199,7 +199,7 @@ class Sf_Dashboard_Connect(http.Controller): begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') - print('begin_time: %s' % begin_time) + # print('begin_time: %s' % begin_time) for item in machine_list: sql = ''' @@ -280,7 +280,7 @@ class Sf_Dashboard_Connect(http.Controller): res = {'Succeed': True} line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')]) line_list = list(map(lambda x: x.name, line_list_obj)) - print('line_list: %s' % line_list) + # print('line_list: %s' % line_list) res['LineList'] = line_list except Exception as e: @@ -307,7 +307,7 @@ class Sf_Dashboard_Connect(http.Controller): plan_obj = request.env['sf.production.plan'].sudo() production_obj = request.env['mrp.production'].sudo() line_list = ast.literal_eval(kw['line_list']) - print('line_list: %s' % line_list) + # print('line_list: %s' % line_list) for line in line_list: # 工单计划量 @@ -446,7 +446,7 @@ class Sf_Dashboard_Connect(http.Controller): end_time_str = kw['end_time'].strip('"') begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') - print('line_list: %s' % line_list) + # print('line_list: %s' % line_list) def get_date_list(start_date, end_date): date_list = [] @@ -483,7 +483,9 @@ class Sf_Dashboard_Connect(http.Controller): 'date': date.strftime('%Y-%m-%d'), 'order_count': len(orders), 'rework_orders': len(rework_orders), - 'not_passed_orders': len(not_passed_orders) + 'not_passed_orders': len(not_passed_orders), + 'finish_order_nums': len(orders), + 'plan_order_nums': 20, }) # 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div # 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理 @@ -570,7 +572,7 @@ class Sf_Dashboard_Connect(http.Controller): end_time_str = kw['end_time'].strip('"') begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') - print('line_list: %s' % line_list) + # print('line_list: %s' % line_list) not_done_data = [] done_data = [] final_data = {} @@ -581,13 +583,13 @@ class Sf_Dashboard_Connect(http.Controller): [('production_line_id.name', '=', line), ('state', 'not in', ['finished']), ('production_id.state', 'not in', ['cancel']), ('active', '=', True) ]) - print(not_done_orders) + # print(not_done_orders) # 完成订单 finish_orders = plan_obj.search([ ('production_line_id.name', '=', line), ('state', 'in', ['finished']), ('production_id.state', 'not in', ['cancel']), ('active', '=', True) ]) - print(finish_orders) + # print(finish_orders) # 获取所有未完成订单的ID列表 order_ids = [order.id for order in not_done_orders] @@ -710,11 +712,11 @@ class Sf_Dashboard_Connect(http.Controller): # 执行SQL命令 cur.execute(sql, (item,)) result = cur.fetchall() - print('result========', result) + # print('result========', result) cur.execute(sql2, (item,)) result2 = cur.fetchall() - print('result2========', result2) + # print('result2========', result2) # for row in result: res['data'][item] = {'idle_count': row[0]} @@ -770,7 +772,7 @@ class Sf_Dashboard_Connect(http.Controller): # 执行SQL命令 cur.execute(sql) result = cur.fetchall() - print('result', result) + # print('result', result) # 将查询结果转换为字典列表 data = [] @@ -977,7 +979,7 @@ class Sf_Dashboard_Connect(http.Controller): LIMIT 1; """, (item,)) first_today = fetch_result_as_dict(cur) - print("当天第一条记录(非离线):", first_today) + # print("当天第一条记录(非离线):", first_today) # 获取当天最新一条记录(排除device_state等于‘离线’的记录) with conn.cursor() as cur: @@ -990,7 +992,7 @@ class Sf_Dashboard_Connect(http.Controller): LIMIT 1; """, (item,)) last_today = fetch_result_as_dict(cur) - print("当天最新一条记录(非离线):", last_today) + # print("当天最新一条记录(非离线):", last_today) # 计算当天运行时长 if first_today and last_today: @@ -1012,7 +1014,7 @@ class Sf_Dashboard_Connect(http.Controller): LIMIT 1; """, (item,)) first_month = fetch_result_as_dict(cur) - print("当月第一条记录(非离线):", first_month) + # print("当月第一条记录(非离线):", first_month) # 获取当月最新一条记录(排除device_state等于‘离线’的记录) with conn.cursor() as cur: @@ -1026,7 +1028,7 @@ class Sf_Dashboard_Connect(http.Controller): LIMIT 1; """, (item,)) last_month = fetch_result_as_dict(cur) - print("当月最新一条记录(非离线):", last_month) + # print("当月最新一条记录(非离线):", last_month) # 计算当月运行时长 if first_month and last_month: @@ -1047,7 +1049,7 @@ class Sf_Dashboard_Connect(http.Controller): LIMIT 1; """, (item,)) first_all_time = fetch_result_as_dict(cur) - print("有记录以来的第一条记录(非离线):", first_all_time) + # print("有记录以来的第一条记录(非离线):", first_all_time) # 获取有记录以来的最新一条记录(排除device_state等于‘离线’的记录) with conn.cursor() as cur: @@ -1059,7 +1061,7 @@ class Sf_Dashboard_Connect(http.Controller): LIMIT 1; """, (item,)) last_all_time = fetch_result_as_dict(cur) - print("有记录以来的最新一条记录(非离线):", last_all_time) + # print("有记录以来的最新一条记录(非离线):", last_all_time) # 计算有记录以来的运行时长 if first_all_time and last_all_time: From c050e2f11adb9930e26310485c4a181414f1b458 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Thu, 12 Sep 2024 10:14:38 +0800 Subject: [PATCH 062/217] =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E7=9A=84=E9=87=87=E8=B4=AD=E5=91=98=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E5=BF=85=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_sale/views/res_partner_view.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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)]} From 988c4a460dc03fd3fb07903c3b472ae5d21d845e Mon Sep 17 00:00:00 2001 From: guanhuan Date: Thu, 12 Sep 2024 10:24:45 +0800 Subject: [PATCH 063/217] =?UTF-8?q?2D=E5=8A=A0=E5=B7=A5=E5=9B=BE=E7=BA=B8?= =?UTF-8?q?=E6=94=B9=E4=B8=BApdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_sale/views/quick_easy_order_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 @@ - + From e0bad6ed40731a8b0cd9092989f1985806cb10e7 Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Thu, 12 Sep 2024 11:08:24 +0800 Subject: [PATCH 064/217] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BD=93=E7=8F=AD?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E9=87=8F=EF=BC=8C=E5=AE=8C=E6=88=90=E9=87=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index e8c5c226..4f99f319 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -447,6 +447,27 @@ class Sf_Dashboard_Connect(http.Controller): begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') # print('line_list: %s' % line_list) + print('kw', kw) + time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天 + print('time_unit: %s' % time_unit) + + # 日期或小时循环生成器,根据time_unit决定是按天还是按小时 + def get_time_intervals(start_time, end_time, time_unit): + intervals = [] + current_time = start_time + if time_unit == 'hour': + delta = timedelta(hours=1) + else: + delta = timedelta(days=1) + + while current_time < end_time: + next_time = current_time + delta + # 确保最后一个时间段不会超出end_time + if next_time > end_time: + next_time = end_time + intervals.append((current_time, next_time)) + current_time = next_time + return intervals def get_date_list(start_date, end_date): date_list = [] @@ -457,10 +478,33 @@ class Sf_Dashboard_Connect(http.Controller): return date_list for line in line_list: - date_list = get_date_list(begin_time, end_time) + date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名 order_counts = [] - date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名 + if time_unit == 'hour': + time_intervals = get_time_intervals(begin_time, end_time, time_unit) + print('============================= %s' % time_intervals) + + time_count_dict = {} + + for time_interval in time_intervals: + start_time, end_time = time_interval + # print(start_time, end_time) + orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']), + (date_field_name, '>=', start_time.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', end_time.strftime('%Y-%m-%d 00:00:00')) + ]) + # 使用小时和分钟作为键,确保每个小时的数据有独立的键 + key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 + time_count_dict[key] = len(orders) + order_counts.append({ + 'finish_order_nums': time_count_dict, + 'plan_order_nums': 28 + }) + res['data'][line] = order_counts + return json.dumps(res) + + date_list = get_date_list(begin_time, end_time) for date in date_list: next_day = date + timedelta(days=1) @@ -483,9 +527,7 @@ class Sf_Dashboard_Connect(http.Controller): 'date': date.strftime('%Y-%m-%d'), 'order_count': len(orders), 'rework_orders': len(rework_orders), - 'not_passed_orders': len(not_passed_orders), - 'finish_order_nums': len(orders), - 'plan_order_nums': 20, + 'not_passed_orders': len(not_passed_orders) }) # 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div # 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理 From 2808005ce9da2a2bc517cd65b5c4b3bc41e12d8f Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Thu, 12 Sep 2024 15:17:09 +0800 Subject: [PATCH 065/217] =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_bf_connect/models/process_status.py | 12 ++++++++---- sf_manufacturing/models/mrp_production.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/sf_bf_connect/models/process_status.py b/sf_bf_connect/models/process_status.py index 111254c7..3bbdb5fc 100644 --- a/sf_bf_connect/models/process_status.py +++ b/sf_bf_connect/models/process_status.py @@ -1,3 +1,4 @@ +import traceback from datetime import datetime import logging import requests @@ -53,11 +54,14 @@ class StatusChange(models.Model): if not ret.get('error'): logging.info('接口已经执行=============') else: - logging.error('工厂加工同步订单状态失败 {}'.format(ret)) - raise UserError('工厂加工同步订单状态失败') + traceback_error = traceback.format_exc() + logging.error("bfm订单状态同步失败:%s request info %s" % traceback_error) + logging.error('/api/get/state/get_order 请求失败{}'.format(ret)) + raise UserError('工厂加工同步订单状态到bfm失败') except UserError as e: - logging.error('工厂加工同步订单状态失败 {}'.format(e)) - raise UserError('工厂加工同步订单状态失败') + traceback_error = traceback.format_exc() + logging.error("工厂加工同步订单状态失败:%s " % traceback_error) + raise UserError(e) return res def action_cancel(self): diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 405baf38..0ab2be6b 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -318,8 +318,10 @@ class MrpProduction(models.Model): # cnc程序获取 def fetchCNC(self, production_names): cnc = self.env['mrp.production'].search([('id', '=', self.id)]) - quick_order = self.env['quick.easy.order'].search( - [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])]) + quick_order = False + if cnc.product_id.default_code: + quick_order = self.env['quick.easy.order'].search( + [('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])]) programme_way = False if cnc.manual_quotation is True: programme_way = 'manual operation' From 01c57a86912a5d811862a51ad771157a133c7c7c Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Thu, 12 Sep 2024 15:17:47 +0800 Subject: [PATCH 066/217] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B7=BB=E5=8A=A0=E6=8F=8F=E8=BF=B0=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/models/jikimo_bom.py | 42 +++++++++++++------ sf_tool_management/models/tool_inventory.py | 6 +-- .../wizard/jikimo_bom_wizard.py | 1 - 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/sf_tool_management/models/jikimo_bom.py b/sf_tool_management/models/jikimo_bom.py index 73f63173..8453eb48 100644 --- a/sf_tool_management/models/jikimo_bom.py +++ b/sf_tool_management/models/jikimo_bom.py @@ -30,9 +30,23 @@ class jikimo_bom(models.Model): return result def check_types_in_list(self): - # 统计每个元素的类型 - type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids) - return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+') + """ + 检查产品列表中的元素是否包含了所有指定的类型,并且每种类型至少出现一次。 + :return: 如果条件满足返回True,否则返回False + """ + if not self.product_ids: + return False + try: + # 统计每个类型的出现次数 + type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids) + + # 检查是否每种类型的出现次数都大于0,并且类型的数量与选项字符串中的数量相等 + return all(count > 0 for count in type_counts.values()) and len(type_counts) == len(self.options.split('+')) + except AttributeError: + # 如果出现属性错误,说明产品列表中的元素可能缺少必要的属性 + return False + # type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids) + # return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+') def write(self, vals): # 在更新模型时记录旧的 Many2many ID 列表 @@ -51,6 +65,11 @@ class jikimo_bom(models.Model): return super(jikimo_bom, self).write(vals) def bom_product_domains(self, assembly_options): + """ + 根据装配选项生成产品域列表 + :param assembly_options: 装配选项字符串,各选项以'+'分隔 + :return: 动态生成的产品搜索条件 + """ self.options = assembly_options cutting_tool_materials = self.env['sf.cutting.tool.material'].search( [('name', 'in', assembly_options.split('+'))]) @@ -83,23 +102,22 @@ class jikimo_bom(models.Model): domains = domains + domain if index != 0: domains = ['|'] + domains - # wqwqwe = self.env['product.product'].search(ddd) - # product = self.env['product.product'].search(domain) - # if product: - # products = products + product domains = domains + [('stock_move_count', '>', 0)] return domains def generate_bill_materials(self, assembly_options): + """ + 生成物料清单 + + 根据装配选项生成物料清单,首先获取产品领域,然后搜索相关产品,并设置产品ID。 + + :param assembly_options: 组装方式 + :type assembly_options: 装配选项字符串,各选项以'+'分隔 + """ domains = self.bom_product_domains(assembly_options) products = self.env['product.product'].search(domains) if products: self.product_ids = [Command.set(products.ids)] - # if option.name == '刀盘': - # hilt = self.env['product.product'].search( - # [('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter), - # ('cutting_tool_material_id', '=', option.id)]) - # self.product_ids = [Command.set(hilt.ids)]k class jikimo_bom_line(models.Model): diff --git a/sf_tool_management/models/tool_inventory.py b/sf_tool_management/models/tool_inventory.py index 5b27c9aa..7eb83aff 100644 --- a/sf_tool_management/models/tool_inventory.py +++ b/sf_tool_management/models/tool_inventory.py @@ -14,10 +14,7 @@ class ToolInventory(models.Model): self._bom_mainfest() return self.bom_mainfest() request.session['jikimo_bom_product'] = {'bom_id': int(self.jikimo_bom_ids)} - # context = dict(self.env.context) - # context.update({'jikimo_bom_product': self.jikimo_bom_ids.options}) - # if self.functional_cutting_tool_model_id.cutting_tool_type_ids: - # context.update({'jikimo_bom_product_cutting_tool_type': self.functional_cutting_tool_model_id.cutting_tool_type_ids.ids}) + return { 'type': 'ir.actions.act_window', 'name': '刀具组装清单', @@ -26,7 +23,6 @@ class ToolInventory(models.Model): 'view_id': self.env.ref('sf_tool_management.view_jikimo_bom_form').id, 'res_id': int(self.jikimo_bom_ids), 'target': 'current', # Use 'new' to open in a new window/tab - # {'jikimo_bom_product': self.jikimo_bom_ids.options} } # 创建bom单 diff --git a/sf_tool_management/wizard/jikimo_bom_wizard.py b/sf_tool_management/wizard/jikimo_bom_wizard.py index f86a7a09..ed6fe790 100644 --- a/sf_tool_management/wizard/jikimo_bom_wizard.py +++ b/sf_tool_management/wizard/jikimo_bom_wizard.py @@ -15,7 +15,6 @@ class JikimoBomWizard(models.TransientModel): ('刀柄+刀杆+刀片', '刀柄+刀杆+刀片'), ('刀柄+刀盘+刀片', '刀柄+刀盘+刀片') ], string='组装方式', required=True) - # assembly_options_ids = fields.Many2many('sf.cutting.tool.material', string="组装方式") is_ok = fields.Boolean('确认上述信息正确无误。') def submit(self): From eae902ee92871c00c2e5bb1848daffb3de8b1d6b Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Thu, 12 Sep 2024 15:36:30 +0800 Subject: [PATCH 067/217] =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E6=97=B6=E9=95=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 4f99f319..faf68f6e 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -497,11 +497,11 @@ class Sf_Dashboard_Connect(http.Controller): # 使用小时和分钟作为键,确保每个小时的数据有独立的键 key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 time_count_dict[key] = len(orders) - order_counts.append({ + # order_counts.append() + res['data'][line] = { 'finish_order_nums': time_count_dict, 'plan_order_nums': 28 - }) - res['data'][line] = order_counts + } return json.dumps(res) date_list = get_date_list(begin_time, end_time) @@ -981,7 +981,7 @@ class Sf_Dashboard_Connect(http.Controller): # 如果没有图片数据,返回404 return request.not_found() - # 设备运行时长 + # 设备运行率 @http.route('/api/RunningTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") def RunningTime(self, **kw): """ @@ -1143,3 +1143,42 @@ class Sf_Dashboard_Connect(http.Controller): res['data']['all_time_work_rate'] = all_time_work_rate return json.dumps(res) + + # 设备运行时长 + @http.route('/api/RunningTimeDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") + def RunningTimeDetail(self, **kw): + """ + 获取 + """ + res = {'status': 1, 'message': '成功', 'data': {}} + # 连接数据库 + conn = psycopg2.connect(**db_config) + # 获取请求的机床数据 + machine_list = ast.literal_eval(kw['machine_list']) + + def fetch_result_as_dict(cursor): + """辅助函数:将查询结果转为字典""" + columns = [desc[0] for desc in cursor.description] + return dict(zip(columns, cursor.fetchone())) if cursor.rowcount != 0 else None + + for item in machine_list: + with conn.cursor() as cur: + cur.execute(""" + SELECT * FROM device_data + WHERE device_name = %s + AND device_state != '离线' + ORDER BY time DESC + LIMIT 1; + """, (item,)) + last_all_time = fetch_result_as_dict(cur) + # 返回数据 + res['data'][item] = { + 'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0, + 'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0, + 'run_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0 + last_all_time['process_time'] if last_all_time['process_time'] is not None else 0, + 'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0 + } + + conn.close() + + return json.dumps(res) From 1719e0394e3c470336b0a87980e96deb3a3feeb7 Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Thu, 12 Sep 2024 16:33:55 +0800 Subject: [PATCH 068/217] =?UTF-8?q?=E5=8E=BB=E6=8E=89run=5Ftime=E8=BF=94?= =?UTF-8?q?=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index faf68f6e..e301154b 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -1175,7 +1175,6 @@ class Sf_Dashboard_Connect(http.Controller): res['data'][item] = { 'wait_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0, 'cut_time': last_all_time['process_time'] if last_all_time['process_time'] is not None else 0, - 'run_time': last_all_time['run_time'] if last_all_time['run_time'] is not None else 0 + last_all_time['process_time'] if last_all_time['process_time'] is not None else 0, 'power_on_time': last_all_time['power_on_time'] if last_all_time['power_on_time'] is not None else 0 } From b5a3815f1fde3a0f464aee37179d6cb2de8a005a Mon Sep 17 00:00:00 2001 From: guanhuan Date: Thu, 12 Sep 2024 16:43:54 +0800 Subject: [PATCH 069/217] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E7=BB=B4=E4=BF=9D?= =?UTF-8?q?=E6=A0=87=E5=87=86=E7=AD=9B=E9=80=89=E6=B7=BB=E5=8A=A0=E5=B7=B2?= =?UTF-8?q?=E5=BD=92=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_maintenance/views/equipment_maintenance_standards_views.xml | 1 + 1 file changed, 1 insertion(+) 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 @@ + From 22864b966988d9f30e674581b4767f98d9712513 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Thu, 12 Sep 2024 17:06:16 +0800 Subject: [PATCH 070/217] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=8F=90=E9=86=92=E6=A8=A1=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_message/__init__.py | 1 + sf_message/__manifest__.py | 25 ++++++ sf_message/models/__init__.py | 1 + sf_message/models/sf_message_template.py | 48 ++++++++++++ sf_message/security/group_security.xml | 5 ++ sf_message/security/ir.model.access.csv | 15 ++++ sf_message/views/sf_message_template_view.xml | 76 +++++++++++++++++++ 7 files changed, 171 insertions(+) create mode 100644 sf_message/__init__.py create mode 100644 sf_message/__manifest__.py create mode 100644 sf_message/models/__init__.py create mode 100644 sf_message/models/sf_message_template.py create mode 100644 sf_message/security/group_security.xml create mode 100644 sf_message/security/ir.model.access.csv create mode 100644 sf_message/views/sf_message_template_view.xml 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..0d41e9f0 --- /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"内容", required=True, translate=True) + 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..30ca230d --- /dev/null +++ b/sf_message/views/sf_message_template_view.xml @@ -0,0 +1,76 @@ + + + + + + + + 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 From 2e5694cd1a311df553d407be8e55dc33e6045c02 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Thu, 12 Sep 2024 17:20:58 +0800 Subject: [PATCH 071/217] =?UTF-8?q?1=E3=80=81=E4=BC=98=E5=8C=96=E7=BB=84?= =?UTF-8?q?=E8=A3=85=E5=8D=95=E6=89=AB=E6=8F=8F=E9=AA=8C=E8=AF=81=E7=89=A9?= =?UTF-8?q?=E6=96=99=E6=96=B9=E6=B3=95=EF=BC=9B2=E3=80=81=E5=AF=B9?= =?UTF-8?q?=E7=BB=84=E8=A3=85=E5=8D=95=E7=89=A9=E6=96=99=E6=89=B9=E6=AC=A1?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E8=B0=83=E6=95=B4=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AD=97=E6=AE=B5=E5=92=8C=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=9B=E4=BC=98=E5=8C=96=E8=8E=B7=E5=8F=96BOM=E5=B9=B6?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=88=9D=E5=A7=8B=E5=8C=96=E7=89=A9=E6=96=99?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=96=B9=E6=B3=95=EF=BC=9B3=E3=80=81?= =?UTF-8?q?=E8=B4=A7=E4=BD=8D=E6=89=B9=E6=AC=A1=E6=A8=A1=E5=9E=8B=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=89=A9=E6=96=99=E5=AD=97=E6=AE=B5=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9Etree=E8=A7=86=E5=9B=BE=EF=BC=8C=E5=B9=B6=E4=B8=94?= =?UTF-8?q?=E9=99=A4=E5=88=80=E6=9F=84=E5=A4=96=E7=89=A9=E6=96=99=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E3=80=90=E6=9B=B4=E5=A4=9A=E3=80=91=E6=97=B6=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E8=87=B3=E4=B8=8D=E5=90=8C=E7=9A=84=E8=B4=A7=E4=BD=8D?= =?UTF-8?q?=E6=89=B9=E6=AC=A1=E6=A8=A1=E5=9E=8Btree=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=EF=BC=9B4=E3=80=81=E6=B7=BB=E5=8A=A0=E5=88=80=E6=9F=84?= =?UTF-8?q?=E5=92=8C=E5=85=B6=E4=BB=96=E7=89=A9=E6=96=99=E5=9C=A8=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E3=80=90=E6=9B=B4=E5=A4=9A=E3=80=91=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E5=90=8E=E9=80=89=E5=8F=96=E6=96=B0=E7=89=A9=E6=96=99=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E3=80=90=E7=A1=AE=E8=AE=A4=E3=80=91=E5=90=8E=E5=9B=9E?= =?UTF-8?q?=E5=A1=AB=E7=89=A9=E6=96=99=E4=BF=A1=E6=81=AF=E5=88=B0=E7=BB=84?= =?UTF-8?q?=E8=A3=85=E5=8D=95=E5=B9=B6=E7=BB=99=E5=87=BA=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=9B5=E3=80=81=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=BB=84=E8=A3=85=E7=89=A9=E6=96=99=E4=BF=A1=E6=81=AF=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=88=96=E9=87=8D=E6=96=B0=E9=80=89=E5=8F=96=E7=89=A9=E6=96=99?= =?UTF-8?q?=E5=90=8E=E9=9C=80=E8=BF=9B=E8=A1=8C=E9=AA=8C=E8=AF=81=E6=89=8D?= =?UTF-8?q?=E8=83=BD=E8=BF=9B=E8=A1=8C=E7=BB=84=E8=A3=85=EF=BC=9B6?= =?UTF-8?q?=E3=80=81=E7=BB=84=E8=A3=85=E5=8D=95form=E8=A7=86=E5=9B=BE?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/controllers/controllers.py | 3 +- sf_tool_management/models/base.py | 189 ++++++------------ sf_tool_management/models/stock.py | 106 +++++++++- sf_tool_management/views/stock.xml | 127 ++++++++++++ 4 files changed, 295 insertions(+), 130 deletions(-) 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 518c4c0f..76fb20ff 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -358,6 +358,7 @@ class FunctionalToolAssembly(models.Model): 智能工厂组装单处扫码校验刀具物料 """ 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: @@ -365,26 +366,31 @@ class FunctionalToolAssembly(models.Model): raise ValidationError(f'Rfid为【{barcode}】的刀柄已被占用,请重新扫描!!') if lot_id.product_id == record.handle_product_id: record.handle_code_id = lot_id.id - record.handle_verify = True + tool_assembly_id.handle_code_id = lot_id.id else: - raise ValidationError('刀具选择错误,请重新确认!!!') + 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}】的刀具不存在,请重新扫描!') + raise ValidationError(f'扫描为【{barcode}】的货位不存在,请重新扫描!') @api.depends('functional_tool_name') def _compute_name(self): @@ -472,12 +478,9 @@ class FunctionalToolAssembly(models.Model): # 刀具物料信息 # ==============整体式刀具型号============= - integral_freight_barcode_id = fields.Many2one('sf.shelf.location', 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_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='整体式刀具批次', - domain="[('shelf_location_id', '=', integral_freight_barcode_id)]") - integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', compute='_compute_integral_lot_id', - store=True) + 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='整体式刀具型号', @@ -488,19 +491,6 @@ class FunctionalToolAssembly(models.Model): related='integral_product_id.brand_id') integral_verify = fields.Boolean('整体刀校验', default=False) - @api.onchange('integral_freight_barcode_id') - def _onchange_integral_freight_barcode_id(self): - for item in self: - item.integral_freight_lot_id = False - - @api.depends('integral_freight_lot_id', 'integral_freight_barcode_id') - def _compute_integral_lot_id(self): - for item in self: - if item.integral_freight_lot_id: - item.integral_lot_id = item.integral_freight_lot_id.lot_id.id - elif not item.integral_freight_barcode_id: - item.integral_lot_id = False - @api.depends('integral_lot_id') def _compute_integral_product_id(self): for item in self: @@ -510,11 +500,9 @@ class FunctionalToolAssembly(models.Model): item.integral_product_id = False # =================刀片型号============= - blade_freight_barcode_id = fields.Many2one('sf.shelf.location', 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_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀片批次', - domain="[('shelf_location_id', '=', blade_freight_barcode_id)]") - blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', compute='blade_freight_lot_id.lot_id', store=True) + 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='刀片型号', @@ -524,19 +512,6 @@ class FunctionalToolAssembly(models.Model): sf_tool_brand_id_2 = fields.Many2one('sf.machine.brand', '刀片品牌', related='blade_product_id.brand_id') blade_verify = fields.Boolean('刀片校验', default=False) - @api.onchange('blade_freight_barcode_id') - def _onchange_blade_freight_barcode_id(self): - for item in self: - item.blade_freight_lot_id = False - - @api.depends('blade_freight_lot_id', 'blade_freight_barcode_id') - def _compute_blade_lot_id(self): - for item in self: - if item.blade_freight_lot_id: - item.blade_lot_id = item.blade_freight_lot_id.lot_id.id - elif not item.blade_freight_barcode_id: - item.blade_lot_id = False - @api.depends('blade_lot_id') def _compute_blade_product_id(self): for item in self: @@ -546,11 +521,9 @@ class FunctionalToolAssembly(models.Model): item.blade_product_id = False # ==============刀杆型号================ - bar_freight_barcode_id = fields.Many2one('sf.shelf.location', 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_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀杆批次', - domain="[('shelf_location_id', '=', bar_freight_barcode_id)]") - bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', compute='_compute_bar_lot_id', store=True) + 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='刀杆型号', @@ -558,20 +531,7 @@ 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') - brand_verify = fields.Boolean('刀杆校验', default=False) - - @api.onchange('bar_freight_barcode_id') - def _onchange_bar_freight_barcode_id(self): - for item in self: - item.bar_freight_lot_id = False - - @api.depends('bar_freight_lot_id', 'bar_freight_barcode_id') - def _compute_bar_lot_id(self): - for item in self: - if item.bar_freight_lot_id: - item.bar_lot_id = item.bar_freight_lot_id.lot_id.id - elif not item.bar_freight_barcode_id: - item.bar_lot_id = False + bar_verify = fields.Boolean('刀杆校验', default=False) @api.depends('bar_lot_id') def _compute_bar_product_id(self): @@ -582,11 +542,9 @@ class FunctionalToolAssembly(models.Model): item.bar_product_id = False # =============刀盘型号================ - pad_freight_barcode_id = fields.Many2one('sf.shelf.location', 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_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='刀盘批次', - domain="[('shelf_location_id', '=', pad_freight_barcode_id)]") - pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', compute='_compute_pad_lot_id', store=True) + 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='刀盘型号', @@ -596,19 +554,6 @@ class FunctionalToolAssembly(models.Model): sf_tool_brand_id_4 = fields.Many2one('sf.machine.brand', '刀盘品牌', related='pad_product_id.brand_id') pad_verify = fields.Boolean('刀盘校验', default=False) - @api.onchange('pad_freight_barcode_id') - def _onchange_pad_freight_barcode_id(self): - for item in self: - item.pad_freight_lot_id = False - - @api.depends('pad_freight_lot_id', 'pad_freight_barcode_id') - def _compute_pad_lot_id(self): - for item in self: - if item.pad_freight_lot_id: - item.pad_lot_id = item.pad_freight_lot_id.lot_id.id - elif not item.pad_freight_barcode_id: - item.pad_lot_id = False - @api.depends('pad_lot_id') def _compute_pad_product_id(self): for item in self: @@ -618,12 +563,10 @@ class FunctionalToolAssembly(models.Model): 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', '刀柄序列号', - domain=[('product_id.cutting_tool_material_id.name', '=', '刀柄'), - ('tool_material_status', '=', '可用')]) - 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='刀柄规格', @@ -632,23 +575,19 @@ class FunctionalToolAssembly(models.Model): 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_freight_barcode_id = fields.Many2one('sf.shelf.location', string='夹头货位', readonly=True, domain="[('product_id.cutting_tool_material_id.name', '=', '夹头'),('product_num', '>', 0)]") - chuck_freight_lot_id = fields.Many2one('sf.shelf.location.lot', string='夹头批次', - domain="[('shelf_location_id', '=', chuck_freight_barcode_id)]") - chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', compute='_compute_chuck_lot_id', store=True) + 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='夹头型号', @@ -658,19 +597,6 @@ class FunctionalToolAssembly(models.Model): sf_tool_brand_id_6 = fields.Many2one('sf.machine.brand', '夹头品牌', related='chuck_product_id.brand_id') chuck_verify = fields.Boolean('夹头校验', default=False) - @api.onchange('chuck_freight_barcode_id') - def _onchange_chuck_freight_barcode_id(self): - for item in self: - item.chuck_freight_lot_id = False - - @api.depends('chuck_freight_lot_id', 'chuck_freight_barcode_id') - def _compute_chuck_lot_id(self): - for item in self: - if item.chuck_freight_lot_id: - item.chuck_lot_id = item.chuck_freight_lot_id.lot_id.id - elif not item.chuck_freight_barcode_id: - item.chuck_lot_id = False - @api.depends('chuck_lot_id') def _compute_chuck_product_id(self): for item in self: @@ -841,7 +767,8 @@ class FunctionalToolAssembly(models.Model): # 配置刀柄信息 for handle_id in bom.get('handle_ids'): if handle_id: - self.handle_product_id = handle_id.id + if not self.handle_product_id: + self.handle_product_id = handle_id.id break # 刀柄之外的物料配置 @@ -850,26 +777,26 @@ class FunctionalToolAssembly(models.Model): 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_freight_lot_id = integra_location_lot_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_freight_lot_id = blade_location_lot_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_freight_lot_id = bar_location_lot_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_freight_lot_id = pad_location_lot_id.id + self.pad_lot_id = pad_location_lot_id.lot_id.id def _get_old_tool_material_lot(self, material_ids): """ 根据先进先出原则选择物料批次 """ @@ -896,7 +823,7 @@ class FunctionalToolAssembly(models.Model): options = inventory_id.jikimo_bom_ids.options # BOM产品组装类型 if not product_ids or not options: - raise ValidationError('功能刀具清单的BOM未进行配置,请先配置BOM再开始组装!') + 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 == '刀片') @@ -934,39 +861,46 @@ class FunctionalToolAssembly(models.Model): 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('handle_ids')) + 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": [[self.env.ref('sf_tool_management.view_tool_product_tree').id, "tree"], + "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)] + "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": [[False, "tree"]], + "views": [[tool_material_tree_id.id, "tree"]], "target": "new", "domain": [('id', 'in', tool_material_ids.ids)], - "context": {'create': False} + "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), ('quantity', '>', '0')]) + [('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) @@ -1033,22 +967,21 @@ class FunctionalToolAssembly(models.Model): # 物料必填校验 if not self.handle_code_id: raise ValidationError('缺少【刀柄】物料信息!') - if not self.integral_product_id and not self.blade_product_id: - raise ValidationError('【整体式刀具】和【刀片】必须填写一个!') - if self.blade_product_id: - if not self.bar_product_id and not self.pad_product_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_functional_tool_length == 0: - raise ValidationError('组装参数信息【伸出长】不能为0!') if self.after_assembly_max_lifetime_value == 0: raise ValidationError('组装参数信息【最大寿命值】不能为0!') - # if self.after_assembly_alarm_value == 0: - # raise ValidationError('组装参数信息【报警值】不能为0!') - # if self.after_assembly_effective_length == 0: - # raise ValidationError('组装参数信息【有效长】不能为0!!!') - # if self.hiding_length == 0: - # raise ValidationError('组装参数信息【避空长】不能为0!!!') if self.after_assembly_functional_tool_diameter <= 0: raise ValidationError('组装参数信息【刀具直径】不能小于等于0!') if self.after_assembly_tool_loading_length == 0: @@ -1172,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 组装单打印 @@ -1563,6 +1503,7 @@ class FunctionalToolDismantle(models.Model): 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': '刀具寿命到期' }) diff --git a/sf_tool_management/models/stock.py b/sf_tool_management/models/stock.py index 50218e65..32719520 100644 --- a/sf_tool_management/models/stock.py +++ b/sf_tool_management/models/stock.py @@ -115,6 +115,13 @@ class StockPicking(models.Model): 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): """ 创建刀具物料出库单 @@ -133,25 +140,32 @@ class StockPicking(models.Model): 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_freight_lot_id.lot_id}) + {'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_freight_lot_id.lot_id}) + {'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_freight_lot_id.lot_id}) + {'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_freight_lot_id.lot_id}) + {'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_freight_lot_id.lot_id}) + {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_lot_id}) # 创建刀具物料出库库存移动记录 stock_move_id.create_tool_material_stock_moves(datas) # 将刀具物料出库库单的状态更改为就绪 @@ -187,6 +201,9 @@ class StockMove(models.Model): 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, @@ -248,8 +265,87 @@ class ProductProduct(models.Model): 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 From 43a49242c5c37845a96c0c33b5f456e3d462d861 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Thu, 12 Sep 2024 17:21:37 +0800 Subject: [PATCH 072/217] =?UTF-8?q?1=E3=80=81=E7=BB=84=E8=A3=85=E5=8D=95?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/views/tool_base_views.xml | 180 +++++++++---------- 1 file changed, 87 insertions(+), 93 deletions(-) diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index 0c492721..ab4912ce 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -479,6 +479,10 @@
- - -
- -
- - - - - - - - - -
-
-
- -
- -
- - - - - - - - - - - -
-
@@ -594,65 +551,102 @@ - - - - - - - + + + + + + + + + + +
- -
- -
+ +
+ + + +
+ +
+ - - - + + + + + + +
+
+
+ + +
+ +
+ + + + + + + + + + + + +
+
+ +
+ +
+ + + + + + + + + + + + + +
+
- - - - - - - - - - - - - - - - - - - -
From 5f5e991d33f275ad247d7f8d41956d2bc609a3d7 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Thu, 12 Sep 2024 17:24:01 +0800 Subject: [PATCH 073/217] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_message/models/sf_message_template.py | 2 +- sf_message/views/sf_message_template_view.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sf_message/models/sf_message_template.py b/sf_message/models/sf_message_template.py index 0d41e9f0..4d89cc2a 100644 --- a/sf_message/models/sf_message_template.py +++ b/sf_message/models/sf_message_template.py @@ -32,7 +32,7 @@ class SfMessageTemplate(models.Model): ('订单发货', '订单发货') ], string='类型', required=True) description = fields.Char(string=u"描述") - content = fields.Html(string=u"内容", required=True, translate=True) + 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') diff --git a/sf_message/views/sf_message_template_view.xml b/sf_message/views/sf_message_template_view.xml index 30ca230d..ac412589 100644 --- a/sf_message/views/sf_message_template_view.xml +++ b/sf_message/views/sf_message_template_view.xml @@ -19,7 +19,8 @@ - + From 6ad2fa80f1e96c82232ee853ddf6e0556a6d03e6 Mon Sep 17 00:00:00 2001 From: hujiaying Date: Fri, 13 Sep 2024 10:15:26 +0800 Subject: [PATCH 074/217] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=80=E5=94=AE?= =?UTF-8?q?=EF=BC=8C=E9=87=87=E8=B4=AD=EF=BC=8C=E5=88=B6=E9=80=A0=EF=BC=8C?= =?UTF-8?q?=E4=BB=93=E5=BA=93=E4=BA=A7=E5=93=81=E8=AE=BE=E7=BD=AE=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E7=B1=BB=E5=9E=8B=E4=B8=BA=E9=BB=98=E8=AE=A4=E5=88=86?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/product_template_management_view.xml | 18 ++++++++++++------ .../views/mrp_workcenter_views.xml | 2 +- sf_sale/views/purchase_order_view.xml | 5 +++++ sf_sale/views/sale_order_view.xml | 5 +++++ sf_stock/__manifest__.py | 3 ++- sf_stock/models/stock_picking.py | 2 -- sf_stock/views/stock_picking.xml | 1 + sf_stock/views/stock_product_template.xml | 11 +++++++++++ 8 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 sf_stock/views/stock_product_template.xml 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_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 @@