From 749df39809c6ea8b36b0360f506aa04035ef0b96 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 24 Feb 2025 16:08:22 +0800 Subject: [PATCH 1/4] =?UTF-8?q?cnc=E5=B7=A5=E5=8D=95=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=9B=9E=E9=80=80=E6=8C=89=E9=92=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workorder.py | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 0d0c9632..b72a743b 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -17,7 +17,6 @@ from odoo.exceptions import UserError, ValidationError from odoo.addons.sf_mrs_connect.models.ftp_operate import FtpController - class ResMrpWorkOrder(models.Model): _inherit = 'mrp.workorder' _order = 'sequence asc' @@ -91,7 +90,7 @@ class ResMrpWorkOrder(models.Model): ('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 and not cur_workorder.name =='解除装夹': + if not finish_move and not cur_workorder.is_subcontract and not cur_workorder.name == '解除装夹': record.back_button_display = True else: record.back_button_display = any( @@ -107,12 +106,14 @@ class ResMrpWorkOrder(models.Model): else: next_workorder = sorted_workorders[position + 1] next_state = next_workorder.state - if ((next_state == 'ready' and not next_workorder.is_subcontract and not next_workorder.name =='解除装夹') or (next_workorder.state == 'pending' and next_workorder.is_subcontract and not next_workorder.name =='解除装夹')) and cur_workorder.state == 'done': + if (next_state == 'ready' 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 or cur_workorder.name =='解除装夹': + if cur_workorder.is_subcontract or cur_workorder.name == '解除装夹': record.back_button_display = False + date_planned_start = fields.Datetime(tracking=True) @api.depends('processing_panel') @@ -149,10 +150,10 @@ class ResMrpWorkOrder(models.Model): ]) 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 - ): + finish_move.move_dest_ids.ids in move.ids + for picking in finished_product_area + for move in picking.move_ids + ): raise UserError('已入库,无法回退') else: moves = self.env['stock.move'].search([ @@ -576,6 +577,7 @@ class ResMrpWorkOrder(models.Model): ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) detailed_reason = fields.Text('详细原因') is_rework = fields.Boolean(string='是否返工', default=False) + # rework_flag = fields.Boolean(string='返工标志', compute='_compute_rework_flag') # # @api.depends('state', 'production_line_state') @@ -1265,6 +1267,7 @@ class ResMrpWorkOrder(models.Model): mo.get_move_line(workorder.production_id, workorder)) else: workorder.state = 'waiting' + # 重写工单开始按钮方法 def button_start(self): # 判断工单状态是否为等待组件 @@ -1431,7 +1434,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 @@ -1448,7 +1452,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() @@ -1671,6 +1676,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) From 71e7ac99932fbdfec4a137a84d50a133fbdff063 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 24 Feb 2025 16:16:00 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=9C=80=E5=90=8E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=A7=A3=E9=99=A4=E8=A3=85=E5=A4=B9=E6=9C=AA=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E5=9B=9E=E9=80=80=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_workorder.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index b72a743b..d86578e5 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -103,6 +103,8 @@ class ResMrpWorkOrder(models.Model): record.back_button_display = True else: record.back_button_display = False + if cur_workorder.is_subcontract or cur_workorder.name == '解除装夹': + record.back_button_display = False else: next_workorder = sorted_workorders[position + 1] next_state = next_workorder.state From eb11e71d0d5e36b6b171b4ab4b715063a8e40280 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Mon, 24 Feb 2025 16:27:09 +0800 Subject: [PATCH 3/4] =?UTF-8?q?1=E3=80=81=E5=85=A5=E5=BA=93=E5=8D=95?= =?UTF-8?q?=E6=8D=AE=E4=BC=98=E5=8C=96=EF=BC=9A=E5=8D=95=E6=8D=AE=E5=B0=B1?= =?UTF-8?q?=E7=BB=AA=E8=87=AA=E5=8A=A8=E5=88=86=E9=85=8D=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8F=B7=EF=BC=9B=E5=88=86=E9=85=8D=E5=BA=8F=E5=88=97=E5=8F=B7?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=B5=81=E6=B0=B4=E5=8F=B7=E5=8F=96=E5=80=BC?= =?UTF-8?q?=3DMAX=EF=BC=88=E4=BA=A7=E5=93=81=E5=BA=8F=E5=88=97=E5=8F=B7?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=9C=80=E5=A4=A7=E5=BA=8F=E5=8F=B7=EF=BC=8C?= =?UTF-8?q?=E9=A2=84=E5=88=86=E9=85=8D=E6=9C=80=E5=A4=A7=E5=BA=8F=E5=8F=B7?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E7=95=8C=E9=9D=A2=E4=BC=98=E5=8C=96=E3=80=82?= =?UTF-8?q?2=E3=80=81=E5=AE=A2=E4=BE=9B=E6=96=99=E5=85=A5=E5=BA=93?= =?UTF-8?q?=E5=8D=95=E6=89=B9=E9=87=8F=E8=B0=83=E6=8B=A8=E6=9C=89=E5=BE=85?= =?UTF-8?q?=E8=B4=A8=E6=A3=80=E7=9A=84=E5=8D=95=E5=AD=90=E6=97=B6-?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E8=AF=AD=E6=98=BE=E7=A4=BA=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E4=BA=86=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/stock.py | 97 +++++++++++-------- sf_manufacturing/views/stock_picking_view.xml | 25 +++++ sf_sale/models/sale_order.py | 6 -- sf_warehouse/models/model.py | 2 +- .../views/change_stock_move_views.xml | 3 + 5 files changed, 85 insertions(+), 48 deletions(-) diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 901b521b..5e3aa156 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -449,25 +449,14 @@ class ProductionLot(models.Model): """Return the next serial number to be attributed to the product.""" if product.tracking == "serial": last_serial = self.env['stock.lot'].search( - [('company_id', '=', company.id), ('product_id', '=', product.id)], + [('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', product.name)], limit=1, order='name desc') - if last_serial: - if product.categ_id.name == '刀具': - return self.env['stock.lot'].get_tool_generate_lot_names1(company, product) - else: - # 对last_serial的name进行检测,如果不是以产品名称+数字的形式的就重新搜索 - if product.name.split('[')[0] not in last_serial.name: - last_serial = self.env['stock.lot'].search( - [('company_id', '=', company.id), ('product_id', '=', product.id), - ('name', 'ilike', product.name.split('[')[0])], - limit=1, order='name desc') - if not last_serial: - return "%s-%03d" % (product.name, 1) - return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name, 2)[1] - now = datetime.now().strftime("%Y%m%d") - if product.cutting_tool_model_id: - split_codes = product.cutting_tool_model_id.code.split('-') - return "%s-T-%s-%s-%03d" % (split_codes[0], now, product.specification_id.name, 1) + move_line_id = self.env['stock.move.line'].sudo().search( + [('product_id', '=', product.id), ('lot_name', 'ilike', product.name)], limit=1, order='lot_name desc') + if last_serial or move_line_id: + return self.env['stock.lot'].generate_lot_names1(product.name, last_serial.name if ( + not move_line_id or + (last_serial and last_serial.name > move_line_id.lot_name)) else move_line_id.lot_name, 2)[1] return "%s-%03d" % (product.name, 1) qr_code_image = fields.Binary(string='二维码', compute='_generate_qr_code') @@ -721,6 +710,34 @@ class StockPicking(models.Model): 'draft', 'sent']: picking.state = 'waiting' + # def write(self, vals): + # + # old_state = None + # if 'state' in vals: + # old_state = self.state + # res = super(StockPicking, self).write(vals) + # if (self.picking_type_id.use_existing_lots is False and self.picking_type_id.use_create_lots is True and + # (('move_ids_without_package' in vals and self.state == 'assigned') + # or ('state' in vals and vals['state'] == 'assigned' and old_state not in ['assigned', 'done']))): + # if self.move_ids_without_package: + # for move_id in self.move_ids_without_package: + # move_id.action_show_details() + # return res + + @api.constrains('state', 'move_ids_without_package') + def _check_move_ids_without_package(self): + """ + 凡库存调拨单的【作业类型】=“收料入库、客供料入库”,且其产品行的【产品_库存_追溯】="按唯一序列号/按批次”的,当调拨单的【状态】=就绪时 + 自动生成预分配序列号 + """ + for sp in self: + if (sp.picking_type_id.use_existing_lots is False and sp.picking_type_id.use_create_lots is True + and sp.state == 'assigned'): + if sp.move_ids_without_package: + for move_id in sp.move_ids_without_package: + if move_id.product_id.tracking in ['serial', 'lot'] and not move_id.move_line_nosuggest_ids: + move_id.action_show_details() + class ReStockMove(models.Model): _inherit = 'stock.move' @@ -856,11 +873,12 @@ class ReStockMove(models.Model): self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin) else: self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) - if (self.picking_type_id.use_existing_lots is False - and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids): - self.action_assign_serial_show_details() + if (self.picking_type_id.use_existing_lots is False + and self.picking_type_id.use_create_lots is True and not self.move_line_nosuggest_ids): + self.action_assign_serial_show_details() elif self.product_id.tracking == "lot": - self._put_tool_lot(self.company_id, self.product_id, self.origin) + if self.product_id.categ_id.name == '刀具': + self._put_tool_lot(self.company_id, self.product_id, self.origin) return { 'name': _('Detailed Operations'), @@ -885,23 +903,6 @@ class ReStockMove(models.Model): ), } - def put_move_line(self): - """ - 确认订单时,自动分配序列号 - """ - if self.product_id.tracking == "serial": - if self.product_id.categ_id.name == '刀具': - self.next_serial = self._get_tool_next_serial(self.company_id, self.product_id, self.origin) - else: - self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id) - self._generate_serial_numbers() - for item in self.move_line_nosuggest_ids: - if item.lot_name: - lot_name = item.lot_name - if item.product_id.categ_id.name == '坯料': - lot_name = lot_name.split('[', 1)[0] - item.lot_qr_code = self.compute_lot_qr_code(lot_name) - def _put_tool_lot(self, company, product, origin): if product.tracking == "lot" and self.product_id.categ_id.name == '刀具': if not self.move_line_nosuggest_ids: @@ -916,6 +917,8 @@ class ReStockMove(models.Model): lot_code = '%s-%03d' % (lot_code, int(move_line_ids.lot_name[-3:]) + 1) lot_names = self.env['stock.lot'].generate_lot_names(lot_code, 1) move_lines_commands = self._generate_serial_move_line_commands_tool_lot(lot_names) + for move_lines_command in move_lines_commands: + move_lines_command[2]['qty_done'] = self.product_uom_qty self.write({'move_line_nosuggest_ids': move_lines_commands}) for item in self.move_line_nosuggest_ids: if item.lot_name: @@ -943,10 +946,15 @@ class ReStockMove(models.Model): last_serial = self.env['stock.lot'].search( [('company_id', '=', company.id), ('product_id', '=', product.id), ('name', 'ilike', origin)], limit=1, order='id DESC') + move_line_id = self.env['stock.move.line'].sudo().search( + [('product_id', '=', product.id), ('lot_name', 'ilike', origin)], limit=1, order='lot_name desc') split_codes = product.cutting_tool_model_id.code.split('-') - if last_serial: + if last_serial or move_line_id: return "%s-T-%s-%s-%03d" % ( - split_codes[0], origin, product.specification_id.name, int(last_serial.name[-3:]) + 1) + split_codes[0], origin, product.specification_id.name, + int(last_serial.name[-3:] if (not move_line_id or + (last_serial and last_serial.name > move_line_id.lot_name)) + else move_line_id.lot_name[-3:]) + 1) else: return "%s-T-%s-%s-%03d" % (split_codes[0], origin, product.specification_id.name, 1) @@ -1042,6 +1050,13 @@ class ReStockMove(models.Model): subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True, index='btree_not_null') + def button_update_the_sequence_number(self): + """ + 更新序列号 功能按钮 + """ + self.move_line_nosuggest_ids.unlink() + return self.action_show_details() + class ReStockQuant(models.Model): _inherit = 'stock.quant' diff --git a/sf_manufacturing/views/stock_picking_view.xml b/sf_manufacturing/views/stock_picking_view.xml index 6c268b82..458494b9 100644 --- a/sf_manufacturing/views/stock_picking_view.xml +++ b/sf_manufacturing/views/stock_picking_view.xml @@ -74,5 +74,30 @@ + + + sf.stock.move.operations.form + stock.move + + + + True + + + True + + + + + + 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 +