From a9b4c5d91babcac43594341795b6712698d8f091 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Tue, 21 Jan 2025 16:04:16 +0800 Subject: [PATCH 01/77] =?UTF-8?q?=E4=BF=9D=E6=8C=81=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E5=B7=A5=E5=8E=82=E5=88=80=E5=85=B7=E6=A0=87=E5=87=86=E5=BA=93?= =?UTF-8?q?=E4=B8=8Ecloud=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_mrs_connect/models/sync_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index 835183b3..ddd7c3c4 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -2302,7 +2302,7 @@ class Cutting_tool_standard_library(models.Model): if result['status'] == 1: for item in result['cutting_tool_standard_library_all_list']: cutting_tool_standard_library = self.search( - [("code", '=', item['code'].replace("JKM", result['factory_short_name'])), + [("code", '=', item['code']), ("active", 'in', [True, False])]) cutting_tool_type = self.env['sf.cutting.tool.type'].search( [("code", '=', item['cutting_tool_type_code'])]) @@ -2313,7 +2313,7 @@ class Cutting_tool_standard_library(models.Model): brand = self.env['sf.machine.brand'].search([("code", '=', item['brand_code'])]) if not cutting_tool_standard_library: self.create({ - "code": item['code'].replace("JKM", result['factory_short_name']), + "code": item['code'], "name": item['name'], "cutting_tool_material_id": cutting_tool_material.id, "cutting_tool_type_id": cutting_tool_type.id, From b604209df4e5ba1b1346f1bb26c3b948c62727fe Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Tue, 21 Jan 2025 16:23:34 +0800 Subject: [PATCH 02/77] =?UTF-8?q?=E5=88=80=E5=85=B7=E6=A0=87=E5=87=86?= =?UTF-8?q?=E5=BA=93=E5=88=97=E8=A1=A8=E8=A7=86=E5=9B=BE=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=97=B6=E9=97=B4=E4=B8=8E=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_base/views/tool_views.xml | 2 ++ sf_mrs_connect/models/sync_common.py | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sf_base/views/tool_views.xml b/sf_base/views/tool_views.xml index b2d53392..82365a9e 100644 --- a/sf_base/views/tool_views.xml +++ b/sf_base/views/tool_views.xml @@ -112,6 +112,8 @@ + + diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index ddd7c3c4..d4aa3f36 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -2335,9 +2335,9 @@ class Cutting_tool_standard_library(models.Model): 'maintenance.equipment.image'].search( [('name', '=', item['fit_blade_shape'])]).id, "chuck_id": False if not item['chuck_code'] else self.search( - [('code', '=', item['chuck_code'].replace("JKM", result['factory_short_name']))]).id, + [('code', '=', item['chuck_code'])]).id, "handle_id": False if not item['handle_code'] else self.search( - [('code', '=', item['handle_code'].replace("JKM", result['factory_short_name']))]).id, + [('code', '=', item['handle_code'])]).id, "suitable_machining_method_ids": [(6, 0, [])] if not item.get( 'suitable_machining_method') else self.env['maintenance.equipment.image']._get_ids( item['suitable_machining_method']), @@ -2377,9 +2377,9 @@ class Cutting_tool_standard_library(models.Model): 'maintenance.equipment.image'].search( [('name', '=', item['fit_blade_shape'])]).id, "chuck_id": False if not item['chuck_code'] else self.search( - [('code', '=', item['chuck_code'].replace("JKM", result['factory_short_name']))]).id, + [('code', '=', item['chuck_code'])]).id, "handle_id": False if not item['handle_code'] else self.search( - [('code', '=', item['handle_code'].replace("JKM", result['factory_short_name']))]).id, + [('code', '=', item['handle_code'])]).id, "suitable_machining_method_ids": [(6, 0, [])] if not item.get( 'suitable_machining_methods') else self.env['maintenance.equipment.image']._get_ids( item['suitable_machining_methods']), From 8e27ee24347d241abdb1e11a0ea9410e28b115cb Mon Sep 17 00:00:00 2001 From: guanhuan Date: Thu, 20 Feb 2025 10:25:56 +0800 Subject: [PATCH 03/77] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=8A=B6=E6=80=81=E5=B7=B2=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/sale_order.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sf_manufacturing/models/sale_order.py b/sf_manufacturing/models/sale_order.py index e31faf1c..0865c4a8 100644 --- a/sf_manufacturing/models/sale_order.py +++ b/sf_manufacturing/models/sale_order.py @@ -1,6 +1,6 @@ import logging import json -from odoo import models, fields, api +from odoo import models, fields, api, _ from odoo.exceptions import UserError _logger = logging.getLogger(__name__) @@ -24,6 +24,8 @@ class SaleOrder(models.Model): self.state = 'supply method' def action_confirm(self): + if self._get_forbidden_state_confirm() & set(self.mapped('state')): + raise UserError(_('订单状态已发生变化,请刷新当前页面')) # 判断是否所有产品都选择了供货方式 filter_line = self.order_line.filtered(lambda line: not line.supply_method) if filter_line: From 162a1061de0615b7294fae09b76422651a74d2f4 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Thu, 20 Feb 2025 14:08:06 +0800 Subject: [PATCH 04/77] =?UTF-8?q?=E5=88=B6=E9=80=A0=E8=AE=A2=E5=8D=95=20?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workorder.py | 129 +++++++++++++++++- sf_manufacturing/views/mrp_workorder_view.xml | 5 + 2 files changed, 130 insertions(+), 4 deletions(-) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index f8197783..b074746f 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1,3 +1,4 @@ +import random import re import json import logging @@ -69,6 +70,50 @@ class ResMrpWorkOrder(models.Model): delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效', tracking=True) + back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True) + + @api.depends('state') + def _compute_back_button_display(self): + for record in self: + sorted_workorders = record.production_id.workorder_ids.filtered(lambda w: w.state != 'cancel').sorted( + key=lambda w: w.sequence) + if not sorted_workorders: + continue + position = next((idx for idx, workorder in enumerate(sorted_workorders) if workorder.id == record.id), -1) + cur_workorder = sorted_workorders[position] + if position == len(sorted_workorders) - 1: + picking_ids = cur_workorder.production_id.sale_order_id.picking_ids + finished_product_area = picking_ids.filtered( + lambda picking: picking.location_dest_id.name == '成品存货区' and picking.state == 'done' + ) + if finished_product_area: + moves = self.env['stock.move'].search([ + ('name', '=', cur_workorder.production_id.name), + ('state', '!=', 'cancel') + ]) + finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None) + if not finish_move and not cur_workorder.is_subcontract: + record.back_button_display = True + else: + record.back_button_display = any( + finish_move.move_dest_ids.ids not in move.ids and record.state == 'done' + for picking in finished_product_area + for move in picking.move_ids + ) + else: + if record.state == 'done': + record.back_button_display = True + else: + record.back_button_display = False + else: + next_workorder = sorted_workorders[position + 1] + next_state = next_workorder.state + if ((next_state == 'ready' and not next_workorder.is_subcontract) or (next_workorder.state == 'pending' and next_workorder.is_subcontract)) and cur_workorder.state == 'done': + record.back_button_display = True + else: + record.back_button_display = False + if cur_workorder.is_subcontract: + record.back_button_display = False @api.depends('processing_panel') def _compute_processing_panel_selection(self): @@ -85,6 +130,78 @@ class ResMrpWorkOrder(models.Model): manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True) + def button_back(self): + if self.production_id.state == 'rework': + raise UserError('制造订单为返工时不能进行工单回退') + sorted_workorders = self.production_id.workorder_ids.filtered(lambda w: w.state != 'cancel').sorted( + key=lambda w: w.sequence) + position = next((idx for idx, workorder in enumerate(sorted_workorders) if workorder.id == self.id), -1) + cur_workorder = sorted_workorders[position] + if position == len(sorted_workorders) - 1: + # 末工序 + picking_ids = cur_workorder.production_id.sale_order_id.picking_ids + finished_product_area = picking_ids.filtered( + lambda picking: picking.location_dest_id.name == '成品存货区' and picking.state == 'done' + ) + moves = self.env['stock.move'].search([ + ('name', '=', cur_workorder.production_id.name), + ('state', '!=', 'cancel') + ]) + finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None) or [] + if any( + finish_move.move_dest_ids.ids in move.ids + for picking in finished_product_area + for move in picking.move_ids + ): + raise UserError('已入库,无法回退') + else: + move_finished = cur_workorder.production_id.move_finished_ids + random_element = random.choice(move_finished) + moves = self.env['stock.move'].search([ + ('name', '=', random_element.name), + ('state', '!=', 'cancel') + ]) + move_lines = self.env['stock.move.line'].search([ + ('reference', '=', random_element.name), + ('state', '!=', 'cancel') + ]) + moves.state = 'assigned' + move_lines.state = 'assigned' + self.time_ids.date_end = None + cur_workorder.state = 'progress' + cur_workorder.production_id.state = 'progress' + quality_check = self.env['quality.check'].search( + [('workorder_id', '=', self.id)]) + for check_order in quality_check: + if check_order.point_id.is_inspect: + check_order.state = 'waiting' + else: + check_order.state = 'none' + # move_dest_ids + finished_quants = moves.mapped('move_line_ids.lot_id.quant_ids') + finished_quants.quantity = 0 + finish_move = next((move for move in moves if move.location_dest_id.name == '制造后'), None) + finish_move.move_dest_ids.reserved_availability = 0 + # finish_move.move_dest_ids.move_line_ids.reserved_uom_qty = 0 + else: + next_workorder = sorted_workorders[position + 1] + next_state = next_workorder.state + if next_state not in ['pending', 'waiting', 'ready']: + raise UserError('下工序已经开始,无法回退') + if next_workorder.is_subcontract: + next_workorder.picking_ids.write({'state': 'waiting'}) + next_workorder.state = 'pending' + self.time_ids.date_end = None + cur_workorder.state = 'progress' + cur_workorder.production_id.state = 'progress' + quality_check = self.env['quality.check'].search( + [('workorder_id', '=', self.id)]) + for check_order in quality_check: + if check_order.point_id.is_inspect: + check_order.state = 'waiting' + else: + check_order.state = 'none' + def _compute_working_users(self): super()._compute_working_users() for item in self: @@ -1117,6 +1234,7 @@ class ResMrpWorkOrder(models.Model): mo.get_move_line(workorder.production_id, workorder)) else: workorder.state = 'waiting' + # 重写工单开始按钮方法 def button_start(self): # 判断工单状态是否为等待组件 @@ -1283,7 +1401,8 @@ class ResMrpWorkOrder(models.Model): 'detailed_reason': record.detailed_reason, 'processing_panel': record.processing_panel, 'routing_type': record.routing_type, - 'handle_result': '待处理' if record.test_results in ['返工', '报废'] or record.is_rework is True else '', + 'handle_result': '待处理' if record.test_results in ['返工', + '报废'] or record.is_rework is True else '', 'test_results': record.test_results, 'test_report': record.detection_report})], 'is_scrap': True if record.test_results == '报废' else False @@ -1300,7 +1419,8 @@ class ResMrpWorkOrder(models.Model): raise UserError('请先完成该工单的工艺外协再进行操作') # 表面工艺外协,最后一张工单 workorders = self.production_id.workorder_ids - subcontract_workorders = workorders.filtered(lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence') + subcontract_workorders = workorders.filtered( + lambda wo: wo.is_subcontract == True and wo.state != 'cancel').sorted('sequence') if self == subcontract_workorders[-1]: # 给下一个库存移动就绪 self.move_subcontract_workorder_ids[0].move_dest_ids._action_done() @@ -1523,6 +1643,7 @@ class ResMrpWorkOrder(models.Model): # 根据工单对应的【作业_个性化记录】配置页签 if any(item.code == 'PTD' for item in mw.routing_workcenter_id.individuation_page_ids): mw.individuation_page_PTD = True + # ============================================================================================= is_inspect = fields.Boolean('需送检', compute='_compute_is_inspect', store=True, default=False) @@ -1659,8 +1780,8 @@ class CNCprocessing(models.Model): # 将FTP的多面的程序单文件下载到临时目录 def download_file_tmp(self, production_no, processing_panel): - remotepath = os.path.join('/home/ftp/ftp_root/NC', production_no, 'return', processing_panel) - serverdir = os.path.join('/tmp', production_no, 'return', processing_panel) + remotepath = os.path.join('/home/ftp/ftp_root/NC', 'production_no', 'return', processing_panel) + serverdir = os.path.join('/tmp', 'production_no', 'return', processing_panel) 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']) diff --git a/sf_manufacturing/views/mrp_workorder_view.xml b/sf_manufacturing/views/mrp_workorder_view.xml index 971a39ad..6d6b57bb 100644 --- a/sf_manufacturing/views/mrp_workorder_view.xml +++ b/sf_manufacturing/views/mrp_workorder_view.xml @@ -48,6 +48,7 @@ + @@ -58,6 +59,10 @@ {'invisible': [('state', '!=', 'ready')]} + + + + + + True + + + True + + + \ No newline at end of file diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py index 66a32f20..b719ffd1 100644 --- a/sf_sale/models/sale_order.py +++ b/sf_sale/models/sale_order.py @@ -401,12 +401,6 @@ class RePurchaseOrder(models.Model): def button_confirm(self): result = super(RePurchaseOrder, self).button_confirm() for item in self: - # 确认订单时,自动分配序列号 - if item.picking_ids: - for picking_id in item.picking_ids: - if picking_id.move_ids: - for move_id in picking_id.move_ids: - move_id.put_move_line() for line in item.order_line: if line.product_id.categ_type == '表面工艺': if item.origin: diff --git a/sf_warehouse/models/model.py b/sf_warehouse/models/model.py index 581ef114..848b1f66 100644 --- a/sf_warehouse/models/model.py +++ b/sf_warehouse/models/model.py @@ -956,7 +956,7 @@ class SfStockPicking(models.Model): qc_ids = self.env['quality.check'].sudo().search( [('picking_id', 'in', sp_ids), ('quality_state', 'in', ['waiting', 'none'])]) if qc_ids: - raise ValidationError(f'单据{[qc.picking_id.name for qc in qc_ids]}未完成质量检查,完成后再试。') + raise ValidationError(f'单据{list(set(qc.picking_id.name for qc in qc_ids))}未完成质量检查,完成后再试。') for record in self: if record.state != 'assigned': continue diff --git a/sf_warehouse/views/change_stock_move_views.xml b/sf_warehouse/views/change_stock_move_views.xml index 8cde3ca3..8feacf75 100644 --- a/sf_warehouse/views/change_stock_move_views.xml +++ b/sf_warehouse/views/change_stock_move_views.xml @@ -79,6 +79,9 @@ + + True +