From 5be5e8b3ec3fd69fe0200eabca40c5547f906e1b Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Mon, 1 Jul 2024 17:11:44 +0800 Subject: [PATCH 01/74] =?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=AE=8C=E6=88=90=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90=E5=86=85=E9=83=A8=E8=B0=83?= =?UTF-8?q?=E6=8B=A8=E5=8D=95=E4=BB=A5=E5=8F=8A=E6=89=80=E6=9C=89=E7=9A=84?= =?UTF-8?q?=E5=88=80=E5=85=B7=E7=89=A9=E6=96=99=E7=9A=84=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E5=92=8C=E7=A7=BB=E5=8A=A8=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=EF=BC=8C=E5=B9=B6=E8=87=AA=E5=8A=A8=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E8=AF=A5=E8=B0=83=E6=8B=A8=E5=8D=95=EF=BC=9B=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7=E7=9A=84=E5=88=80?= =?UTF-8?q?=E5=85=B7=E7=BB=84=E8=A3=85=E5=85=A5=E5=BA=93=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E7=94=9F=E6=88=90=EF=BC=8C=E7=AE=80=E5=8C=96=E5=85=A5=E5=BA=93?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/functional_tool_views.xml | 2 +- sf_tool_management/wizard/wizard.py | 153 +++++++++++------- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml index 6fde82a0..7211548c 100644 --- a/sf_tool_management/views/functional_tool_views.xml +++ b/sf_tool_management/views/functional_tool_views.xml @@ -174,7 +174,7 @@ - + diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py index af772016..005d8f84 100644 --- a/sf_tool_management/wizard/wizard.py +++ b/sf_tool_management/wizard/wizard.py @@ -621,26 +621,10 @@ class FunctionalToolAssemblyOrder(models.TransientModel): desc_1 = self.get_desc_1(stock_lot) # 封装功能刀具数据,用于创建功能刀具记录 desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly) - # 创建刀具组装入库单 + # 创建功能刀具组装入库单 self.env['stock.picking'].create_stocking_picking(stock_lot, functional_tool_assembly, self) - # 刀具物料出库 - if self.handle_code_id: - product_id.tool_material_stock_moves(self.handle_code_id, self.assembly_order_code) - if self.integral_product_id: - self.integral_product_id.material_stock_moves(self.integral_freight_barcode_id, - self.integral_freight_lot_id, self.assembly_order_code) - if self.blade_product_id: - self.blade_product_id.material_stock_moves(self.blade_freight_barcode_id, - self.blade_freight_lot_id, self.assembly_order_code) - if self.bar_product_id: - self.bar_product_id.material_stock_moves(self.bar_freight_barcode_id, - self.bar_freight_lot_id, self.assembly_order_code) - if self.pad_product_id: - self.pad_product_id.material_stock_moves(self.pad_freight_barcode_id, - self.pad_freight_lot_id, self.assembly_order_code) - if self.chuck_product_id: - self.chuck_product_id.material_stock_moves(self.chuck_freight_barcode_id, - self.chuck_freight_lot_id, self.assembly_order_code) + # 创建刀具物料出库单 + self.env['stock.picking'].create_stocking_picking1(self) # ============================创建功能刀具列表、安全库存记录=============================== # 创建功能刀具列表记录 @@ -788,7 +772,7 @@ class StockPicking(models.Model): def create_stocking_picking(self, stock_lot, functional_tool_assembly, obj): """ - 创建刀具组装入库单 + 创建功能刀具组装入库单 """ # 获取名称为刀具组装入库的作业类型 picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '刀具组装入库')]) @@ -807,6 +791,7 @@ class StockPicking(models.Model): '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, @@ -836,6 +821,65 @@ class StockPicking(models.Model): num = "%03d" % m return name + str(num) + def create_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 + }) + # =============刀具物料出库=================== + product_id = self.env['product.product'] + datas = {'data': [], 'picking_id': picking_id} + if obj.handle_code_id: + 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}) + # 创建刀具物料出库库存移动记录 + product_id.create_tool_material_stock_moves(datas) + # 将刀具物料出库库单的状态更改为就绪 + picking_id.action_confirm() + # 修改刀具物料出库移动历史记录 + product_id.write_tool_material_stock_move_lines(datas) + # 设置数量,并验证完成 + picking_id.action_set_quantities_to_reservation() + picking_id.button_validate() + + def _get_name_stock1(self, picking_type_id): + name = picking_type_id.sequence_id.prefix + 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) + class ProductProduct(models.Model): _inherit = 'product.product' @@ -853,13 +897,6 @@ class ProductProduct(models.Model): 'product_id': product_id[0].id, 'company_id': self.env.company.id }) - # 获取位置对象 - location_inventory_ids = self.env['stock.location'].search([('name', 'in', ('Production', '生产'))]) - stock_location_id = self.env['stock.location'].search([('name', '=', '组装后')]) - # 创建功能刀具该批次/序列号 库存移动和移动历史 - stock_lot.create_stock_quant(location_inventory_ids[-1], stock_location_id, functional_tool_assembly.id, - obj.assembly_order_code, obj, obj.after_tool_groups_id) - return stock_lot def get_stock_lot_name(self, obj): @@ -878,39 +915,37 @@ class ProductProduct(models.Model): num = "%03d" % m return '%s-%s' % (code, num) - def tool_material_stock_moves(self, tool_material, assembly_order_code): - """ - 对刀具物料进行库存移动到 刀具组装位置 - """ - # 获取位置对象 - location_inventory_id = tool_material.quant_ids.location_id[-1] - stock_location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) - # 创建功能刀具该批次/序列号 库存移动和移动历史 - tool_material.create_stock_quant(location_inventory_id, stock_location_id, None, assembly_order_code, False, - False) + 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 material_stock_moves(self, shelf_location_barcode_id, lot_id, assembly_order_code): - # 创建库存移动记录 - stock_move_id = self.env['stock.move'].sudo().create({ - 'name': assembly_order_code, - 'product_id': self.id, - 'location_id': self.env['stock.location'].search([('name', '=', '刀具房')]).id, - 'location_dest_id': self.env['stock.location'].search([('name', '=', '刀具组装位置')]).id, - 'product_uom_qty': 1.00, - 'state': 'done' - }) - - # 创建移动历史记录 - stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.id, - 'move_id': stock_move_id.id, - 'lot_id': lot_id.lot_id.id, - 'current_location_id': shelf_location_barcode_id.id, - 'install_tool_time': fields.Datetime.now(), - 'qty_done': 1.0, - 'state': 'done', - }) - return stock_move_id, stock_move_line_id + 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 StockLot(models.Model): From 0b267cc88a84e9014e05689b0848518375c9c30e Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Tue, 2 Jul 2024 09:32:50 +0800 Subject: [PATCH 02/74] 1 --- sf_tool_management/models/base.py | 9 +++- sf_tool_management/wizard/wizard.py | 74 +++++++++++++++-------------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 182141cf..20c1e45d 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -776,7 +776,7 @@ class FunctionalToolDismantle(models.Model): dismantle_person = fields.Char('拆解人', readonly=True) image = fields.Binary('图片', readonly=True) - scrap_id = fields.Char('报废单号', readonly=True) + scrap_ids = fields.One2many('stock.scrap', 'functional_tool_dismantle_id', string='报废单号', readonly=True) grinding_id = fields.Char('磨削单号', readonly=True) state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True) @@ -941,7 +941,6 @@ class FunctionalToolDismantle(models.Model): lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)]) if not lot: raise ValidationError('Rfid为【%s】的功能刀具序列号不存在!' % self.handle_rfid) - functional_tool_assembly = self.functional_tool_id.functional_tool_name_id if self.scrap_boolean: # 刀柄报废 入库到Scrap lot.create_stock_quant(location, location_dest_scrap_ids[-1], False, code, False, False) @@ -1043,3 +1042,9 @@ class ProductProduct(models.Model): }) return stock_move_id, stock_move_line_id + + +class CustomStockScrap(models.Model): + _inherit = 'stock.scrap' + + functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string="功能刀具拆解单") diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py index 005d8f84..111211c2 100644 --- a/sf_tool_management/wizard/wizard.py +++ b/sf_tool_management/wizard/wizard.py @@ -836,7 +836,7 @@ class StockPicking(models.Model): 'origin': obj.assembly_order_code }) # =============刀具物料出库=================== - product_id = self.env['product.product'] + stock_move_id = self.env['stock.move'] datas = {'data': [], 'picking_id': picking_id} if obj.handle_code_id: datas['data'].append( @@ -857,11 +857,11 @@ class StockPicking(models.Model): datas['data'].append( {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_freight_lot_id.lot_id}) # 创建刀具物料出库库存移动记录 - product_id.create_tool_material_stock_moves(datas) + stock_move_id.create_tool_material_stock_moves(datas) # 将刀具物料出库库单的状态更改为就绪 picking_id.action_confirm() # 修改刀具物料出库移动历史记录 - product_id.write_tool_material_stock_move_lines(datas) + stock_move_id.write_tool_material_stock_move_lines(datas) # 设置数量,并验证完成 picking_id.action_set_quantities_to_reservation() picking_id.button_validate() @@ -881,6 +881,42 @@ class StockPicking(models.Model): 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' @@ -915,38 +951,6 @@ class ProductProduct(models.Model): num = "%03d" % m return '%s-%s' % (code, num) - 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 StockLot(models.Model): _inherit = 'stock.lot' From cbc8a419849cf602b0f4088e731bd9216734d9d6 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Tue, 2 Jul 2024 17:32:27 +0800 Subject: [PATCH 03/74] =?UTF-8?q?1=E3=80=81=E6=B7=BB=E5=8A=A0=E6=8B=86?= =?UTF-8?q?=E8=A7=A3=E7=9A=84=E4=BB=93=E5=BA=93=E7=AE=A1=E7=90=86=E5=9C=B0?= =?UTF-8?q?=E7=82=B9=EF=BC=9B2=E3=80=81=E4=BC=98=E5=8C=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=88=80=E5=85=B7=E6=8B=86=E8=A7=A3=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E7=AE=A1=E7=90=86=EF=BC=9B3=E3=80=81=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_dlm_management/data/stock_data.xml | 10 + sf_tool_management/models/base.py | 190 +++++++++++++------ sf_tool_management/views/tool_base_views.xml | 19 +- sf_tool_management/wizard/wizard.py | 62 +----- 4 files changed, 166 insertions(+), 115 deletions(-) diff --git a/sf_dlm_management/data/stock_data.xml b/sf_dlm_management/data/stock_data.xml index 3d2c4527..be2a36e0 100644 --- a/sf_dlm_management/data/stock_data.xml +++ b/sf_dlm_management/data/stock_data.xml @@ -22,6 +22,16 @@ + + 拆解 + + internal + DJCJ + true + true + + + 刀具组装入库 diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 20c1e45d..12f7e79e 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -933,79 +933,55 @@ class FunctionalToolDismantle(models.Model): self.rfid, self.functional_tool_id.current_location)) # 目标重复校验 self.location_duplicate_check() - location = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) - location_dest = self.env['stock.location'].search([('name', '=', '刀具房')]) + datas = {'scrap': [], 'picking': []} # =================刀柄是否[报废]拆解======= - location_dest_scrap_ids = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))]) if self.handle_rfid: lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)]) if not lot: - raise ValidationError('Rfid为【%s】的功能刀具序列号不存在!' % self.handle_rfid) + raise ValidationError('Rfid为【%s】的刀柄序列号不存在!' % self.handle_rfid) if self.scrap_boolean: # 刀柄报废 入库到Scrap - lot.create_stock_quant(location, location_dest_scrap_ids[-1], False, code, False, False) + datas['scrap'].append({'lot_id': lot}) lot.tool_material_status = '报废' else: # 刀柄不报废 入库到刀具房 - lot.create_stock_quant(location, location_dest, False, code, False, False) + datas['picking'].append({'lot_id': lot, 'destination': self.env['sf.shelf.location']}) lot.tool_material_status = '可用' - # ==============功能刀具[报废]拆解================ if self.dismantle_cause in ['寿命到期报废', '崩刀报废']: # 除刀柄外物料报废 入库到Scrap if self.integral_product_id: - self.integral_product_id.dismantle_stock_moves(False, self.integral_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.integral_lot_id}) elif self.blade_product_id: - self.blade_product_id.dismantle_stock_moves(False, self.blade_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.blade_lot_id}) if self.bar_product_id: - self.bar_product_id.dismantle_stock_moves(False, self.bar_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.bar_lot_id}) elif self.pad_product_id: - self.pad_product_id.dismantle_stock_moves(False, self.pad_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.pad_lot_id}) if self.chuck_product_id: - self.chuck_product_id.dismantle_stock_moves(False, self.chuck_lot_id, location, - location_dest_scrap_ids[-1], code) - # ===========功能刀具[磨削]拆解============== - # elif self.dismantle_cause in ['刀具需磨削']: - # location_dest = self.env['stock.location'].search([('name', '=', '磨削房')]) - # # 除刀柄外物料拆解 入库到具体库位 - # if self.integral_product_id: - # self.integral_product_id.dismantle_stock_moves(False, location, location_dest) - # elif self.blade_product_id: - # self.blade_product_id.dismantle_stock_moves(False, location, location_dest) - # if self.bar_product_id: - # self.bar_product_id.dismantle_stock_moves(False, location, location_dest) - # elif self.pad_product_id: - # self.pad_product_id.dismantle_stock_moves(False, location, location_dest) - # if self.chuck_product_id: - # self.chuck_product_id.dismantle_stock_moves(False, location, location_dest) + datas['scrap'].append({'lot_id': self.chuck_lot_id}) # ==============功能刀具[更换,磨削]拆解============== elif self.dismantle_cause in ['更换为其他刀具', '刀具需磨削']: # 除刀柄外物料拆解 入库到具体货位 if self.integral_freight_id: - self.integral_product_id.dismantle_stock_moves(self.integral_freight_id, self.integral_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.integral_lot_id, 'destination': self.integral_freight_id}) elif self.blade_freight_id: - self.blade_product_id.dismantle_stock_moves(self.blade_freight_id, self.blade_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.blade_lot_id, 'destination': self.blade_freight_id}) if self.bar_freight_id: - self.bar_product_id.dismantle_stock_moves(self.bar_freight_id, self.bar_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.bar_lot_id, 'destination': self.bar_freight_id}) elif self.pad_freight_id: - self.pad_product_id.dismantle_stock_moves(self.pad_freight_id, self.pad_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.pad_lot_id, 'destination': self.pad_freight_id}) if self.chuck_freight_id: - self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id, self.chuck_lot_id, location, - location_dest, code) - # ===============删除功能刀具的Rfid字段的值, 赋值给Rfid(已拆解)字段===== + datas['picking'].append({'lot_id': self.chuck_lot_id, 'destination': self.chuck_freight_id}) + self.create_tool_picking_scrap(datas) + # ===============修改功能刀具数据===== self.functional_tool_id.write({ 'rfid_dismantle': self.functional_tool_id.rfid, 'rfid': '', 'functional_tool_status': '已拆除' }) + # 创建功能刀具拆解移动记录 + location_dismantle_id = self.env['stock.location'].search([('name', '=', '拆解')]) # 修改拆解单的值 self.write({ 'rfid_dismantle': self.rfid, @@ -1016,31 +992,120 @@ class FunctionalToolDismantle(models.Model): }) logging.info('【%s】刀具拆解成功!' % self.name) + def create_tool_picking_scrap(self, datas): + scrap_data = datas['scrap'] + picking_data = datas['picking'] + if scrap_data: + for data in scrap_data: + if data: + self.env['stock.scrap'].create_tool_dismantle_stock_scrap(data['lot_id'], self) + if picking_data: + picking_id = self.env['stock.picking'].create_tool_dismantle_picking(self) + self.env['stock.move'].create_tool_stock_move({'data': picking_data, 'picking_id': picking_id}) + # 将刀具物料出库库单的状态更改为就绪 + picking_id.action_confirm() + # 修改刀具物料出库移动历史记录 + self.env['stock.move'].write_tool_stock_move_line({'data': picking_data, 'picking_id': picking_id}) + # 设置数量,并验证完成 + picking_id.action_set_quantities_to_reservation() + picking_id.button_validate() -class ProductProduct(models.Model): - _inherit = 'product.product' - def dismantle_stock_moves(self, shelf_location_id, lot_id, location_id, location_dest_id, code): - # 创建功能刀具拆解单产品库存移动记录 - stock_move_id = self.env['stock.move'].sudo().create({ - 'name': code, - 'product_id': self.id, +class StockPicking(models.Model): + _inherit = 'stock.picking' + + def create_tool_dismantle_picking(self, obj): + """ + 创建刀具物料入库单 + """ + # 获取名称为内部调拨的作业类型 + picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')]) + location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) + location_dest_id = self.env['stock.location'].search([('name', '=', '刀具房')]) + if not location_id: + raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点') + if not location_dest_id: + raise ValidationError('缺少名称为【刀具房】的仓库管理地点') + # 创建刀具物料出库单 + picking_id = self.env['stock.picking'].create({ + 'name': self._get_name_stock1(picking_type_id), + 'picking_type_id': picking_type_id.id, 'location_id': location_id.id, 'location_dest_id': location_dest_id.id, + 'origin': obj.code + }) + + return picking_id + + +class StockMove(models.Model): + _inherit = 'stock.move' + + def create_tool_stock_move(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_stock_move_line(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({ + 'destination_location_id': res.get('destination').id, + 'lot_id': res.get('lot_id').id + }) + return True + + def create_functional_tool_stock_move(self, location_inventory_id, stock_location_id, functional_tool_assembly_id, + name, obj, + tool_groups_id): + """ + 对功能刀具拆解过程的功能刀具进行库存移动,以及创建移动历史 + """ + # 创建库存移动记录 + stock_move_id = self.env['stock.move'].sudo().create({ + 'name': name, + 'product_id': self.product_id.id, + 'location_id': location_inventory_id.id, + 'location_dest_id': stock_location_id.id, 'product_uom_qty': 1.00, 'state': 'done' }) + # 创建移动历史记录 stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.id, - 'lot_id': lot_id.id, + 'product_id': self.product_id.id, + 'functional_tool_name_id': functional_tool_assembly_id, + 'lot_id': self.id, 'move_id': stock_move_id.id, - 'destination_location_id': shelf_location_id.id if shelf_location_id else False, 'install_tool_time': fields.Datetime.now(), 'qty_done': 1.0, 'state': 'done', + 'functional_tool_type_id': False if not obj else obj.functional_tool_type_id.id, + 'diameter': None if not obj else obj.after_assembly_functional_tool_diameter, + 'knife_tip_r_angle': None if not obj else obj.after_assembly_knife_tip_r_angle, + 'code': '' if not obj else obj.code, + 'rfid': '' if not obj else obj.rfid, + 'functional_tool_name': '' if not obj else obj.after_assembly_functional_tool_name, + 'tool_groups_id': False if not tool_groups_id else tool_groups_id.id }) - return stock_move_id, stock_move_line_id @@ -1048,3 +1113,22 @@ class CustomStockScrap(models.Model): _inherit = 'stock.scrap' functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string="功能刀具拆解单") + + def create_tool_dismantle_stock_scrap(self, lot, dismantle_id): + location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) + scrap_location_id = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))]) + if not location_id: + raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点') + if not scrap_location_id: + raise ValidationError('缺少名称为【Scrap】或【Scrap】的仓库管理地点') + stock_scrap_id = self.create({ + 'product_id': lot.product_id.id, + 'lot_id': lot.id, + 'location_id': location_id.id, + 'scrap_location_id': scrap_location_id.id, + 'functional_tool_dismantle_id': dismantle_id.id, + 'origin': dismantle_id.code + }) + # 完成报废单 + stock_scrap_id.action_validate() + return stock_scrap_id diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index 31fe53c9..42f2c3ca 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -711,10 +711,14 @@ - - - - + + + + @@ -791,8 +795,8 @@ attrs="{'readonly': [('state', '=', '已拆解')]}"/> - + + @@ -879,6 +883,9 @@ + + + diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py index 111211c2..4edd3617 100644 --- a/sf_tool_management/wizard/wizard.py +++ b/sf_tool_management/wizard/wizard.py @@ -622,9 +622,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel): # 封装功能刀具数据,用于创建功能刀具记录 desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly) # 创建功能刀具组装入库单 - self.env['stock.picking'].create_stocking_picking(stock_lot, functional_tool_assembly, self) + self.env['stock.picking'].create_tool_stocking_picking(stock_lot, functional_tool_assembly, self) # 创建刀具物料出库单 - self.env['stock.picking'].create_stocking_picking1(self) + self.env['stock.picking'].create_tool_stocking_picking1(self) # ============================创建功能刀具列表、安全库存记录=============================== # 创建功能刀具列表记录 @@ -770,7 +770,7 @@ class FunctionalToolAssemblyOrder(models.TransientModel): class StockPicking(models.Model): _inherit = 'stock.picking' - def create_stocking_picking(self, stock_lot, functional_tool_assembly, obj): + def create_tool_stocking_picking(self, stock_lot, functional_tool_assembly, obj): """ 创建功能刀具组装入库单 """ @@ -821,7 +821,7 @@ class StockPicking(models.Model): num = "%03d" % m return name + str(num) - def create_stocking_picking1(self, obj): + def create_tool_stocking_picking1(self, obj): """ 创建刀具物料出库单 """ @@ -874,10 +874,10 @@ class StockPicking(models.Model): order="id desc" ) if not stock_id: - num = "%03d" % 1 + num = "%05d" % 1 else: m = int(stock_id.name[-3:]) + 1 - num = "%03d" % m + num = "%05d" % m return name + str(num) @@ -950,53 +950,3 @@ class ProductProduct(models.Model): m = int(stock_lot_id.name[-3:]) + 1 num = "%03d" % m return '%s-%s' % (code, num) - - -class StockLot(models.Model): - _inherit = 'stock.lot' - - def create_stock_quant(self, location_inventory_id, stock_location_id, functional_tool_assembly_id, name, obj, - tool_groups_id): - """ - 对功能刀具组装过程的功能刀具和刀具物料进行库存移动,以及创建移动历史 - """ - - # 创建库存移动记录 - stock_move_id = self.env['stock.move'].sudo().create({ - 'name': name, - 'product_id': self.product_id.id, - 'location_id': location_inventory_id.id, - 'location_dest_id': stock_location_id.id, - 'product_uom_qty': 1.00, - 'state': 'done' - }) - - # 创建移动历史记录 - stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.product_id.id, - 'functional_tool_name_id': functional_tool_assembly_id, - 'lot_id': self.id, - 'move_id': stock_move_id.id, - 'install_tool_time': fields.Datetime.now(), - 'qty_done': 1.0, - 'state': 'done', - 'functional_tool_type_id': False if not obj else obj.functional_tool_type_id.id, - 'diameter': None if not obj else obj.after_assembly_functional_tool_diameter, - 'knife_tip_r_angle': None if not obj else obj.after_assembly_knife_tip_r_angle, - 'code': '' if not obj else obj.code, - 'rfid': '' if not obj else obj.rfid, - 'functional_tool_name': '' if not obj else obj.after_assembly_functional_tool_name, - 'tool_groups_id': False if not tool_groups_id else tool_groups_id.id - }) - return stock_move_id, stock_move_line_id - -# class StockQuant(models.Model): -# _inherit = 'stock.quant' -# -# @api.model_create_multi -# def create(self, vals_list): -# records = super(StockQuant, self).create(vals_list) -# for record in records: -# if record.lot_id.product_id.categ_id.name == '刀具': -# record.lot_id.enroll_tool_material_stock() -# return records From 487f6c205493cdd7d23a4a33f44759f15f0fbb7c Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 3 Jul 2024 09:55:47 +0800 Subject: [PATCH 04/74] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=9A=E9=9D=A2?= =?UTF-8?q?=E5=8A=A0=E5=B7=A5=E4=BA=A7=E7=BA=BF=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/controllers/controllers.py | 42 +++++++++---------- sf_manufacturing/models/mrp_production.py | 6 +-- sf_manufacturing/models/mrp_workorder.py | 5 ++- .../views/mrp_production_addional_change.xml | 2 +- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index dd97531f..37ea1792 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -8,7 +8,7 @@ from odoo.http import request class Manufacturing_Connect(http.Controller): - @http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/GetWoInfo', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def get_Work_Info(self, **kw): """ @@ -102,7 +102,7 @@ class Manufacturing_Connect(http.Controller): logging.info('get_ShiftPlan error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/QcCheck', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/QcCheck', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def get_qcCheck(self, **kw): """ @@ -143,7 +143,7 @@ class Manufacturing_Connect(http.Controller): logging.info('get_qcCheck error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def button_Work_START(self, **kw): """ @@ -191,7 +191,7 @@ class Manufacturing_Connect(http.Controller): logging.info('button_Work_START error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def button_Work_End(self, **kw): """ @@ -229,12 +229,12 @@ class Manufacturing_Connect(http.Controller): # request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write( # {'actual_end_time': workorder.date_finished, # 'state': 'finished'}) - # production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)]) - # if production_obj: - # production_obj.sudo().work_order_state = '已完成' - # production_obj.write({'state': 'done'}) - # request.env['sale.order'].sudo().search( - # [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'}) + # production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)]) + # if production_obj: + # production_obj.sudo().work_order_state = '已完成' + # production_obj.write({'state': 'done'}) + # request.env['sale.order'].sudo().search( + # [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'}) except Exception as e: res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} @@ -299,7 +299,7 @@ class Manufacturing_Connect(http.Controller): logging.info('PartQualityInspect error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def CMMProgDolod(self, **kw): """ @@ -339,7 +339,7 @@ class Manufacturing_Connect(http.Controller): logging.info('CMMProgDolod error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def NCProgDolod(self, **kw): """ @@ -380,7 +380,7 @@ class Manufacturing_Connect(http.Controller): logging.info('NCProgDolod error:%s' % e) return json.JSONEncoder().encode(res) - @http.route('/AutoDeviceApi/LocationChange', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, + @http.route('/AutoDeviceApi/LocationChange', type='json', auth='none', methods=['GET', 'POST'], csrf=False, cors="*") def LocationChange(self, **kw): """ @@ -438,7 +438,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): """ @@ -471,10 +471,10 @@ class Manufacturing_Connect(http.Controller): workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc') if workorder: for order in workorder: - if order.production_id.production_line_state == '待上产线': + if order.production_line_state == '待上产线': logging.info( - '制造订单产线状态:%s' % order.production_id.production_line_state) - order.production_id.write({'production_line_state': '已上产线'}) + '制造订单产线状态:%s' % order.production_line_state) + order.write({'production_line_state': '已上产线'}) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( [ ('rfid_code', '=', rfid_code), ('type', '=', '上产线'), @@ -493,7 +493,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): """ @@ -528,10 +528,10 @@ class Manufacturing_Connect(http.Controller): workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc') if workorder: for order in workorder: - if order.production_id.production_line_state == '已上产线': + if order.production_line_state == '已上产线': logging.info( - '制造订单产线状态:%s' % order.production_id.production_line_state) - order.production_id.write({'production_line_state': '已下产线'}) + '制造订单产线状态:%s' % order.production_line_state) + order.write({'production_line_state': '已下产线'}) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( [ ('rfid_code', '=', rfid_code), ('type', '=', '下产线'), diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 95242b54..73782a9c 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -55,9 +55,9 @@ class MrpProduction(models.Model): glb_file = fields.Binary("glb模型文件") production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True) plan_start_processing_time = fields.Datetime('计划开始加工时间') - production_line_state = fields.Selection( - [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')], - string='上/下产线', default='待上产线', tracking=True) + # production_line_state = fields.Selection( + # [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')], + # string='上/下产线', default='待上产线', tracking=True) # 工序状态 # Todo 研究下用法 process_state = fields.Selection([ diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 09bde519..46bcde47 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -193,8 +193,9 @@ class ResMrpWorkOrder(models.Model): production_line_id = fields.Many2one('sf.production.line', related='production_id.production_line_id', string='生产线', store=True, tracking=True) - production_line_state = fields.Selection(related='production_id.production_line_state', - string='上/下产线', store=True, tracking=True) + production_line_state = fields.Selection( + [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')], + string='上/下产线', default='待上产线', tracking=True) detection_report = fields.Binary('检测报告', readonly=True) is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False) is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False) diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index b65dfb2f..b5e348b9 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -87,7 +87,7 @@ - + From fa6303bbef452a08b636b3dee674b03358a9391c Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 3 Jul 2024 10:30:13 +0800 Subject: [PATCH 05/74] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=9A=E9=9D=A2?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E5=8A=A0=E5=B7=A5=E4=BA=A7=E7=BA=BF=E7=8A=B6?= =?UTF-8?q?=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/controllers/controllers.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index 37ea1792..a67be07d 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -473,8 +473,12 @@ class Manufacturing_Connect(http.Controller): for order in workorder: if order.production_line_state == '待上产线': logging.info( - '制造订单产线状态:%s' % order.production_line_state) - order.write({'production_line_state': '已上产线'}) + '工单产线状态:%s' % order.production_line_state) + panel_workorder = request.env['mrp.workorder'].sudo().search( + [('rfid_code', '=', rfid_code), + ('processing_panel', '=', order.processing_panel)]) + if panel_workorder: + panel_workorder.write({'production_line_state': '已上产线'}) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( [ ('rfid_code', '=', rfid_code), ('type', '=', '上产线'), @@ -530,8 +534,12 @@ class Manufacturing_Connect(http.Controller): for order in workorder: if order.production_line_state == '已上产线': logging.info( - '制造订单产线状态:%s' % order.production_line_state) - order.write({'production_line_state': '已下产线'}) + '工单产线状态:%s' % order.production_line_state) + panel_workorder = request.env['mrp.workorder'].sudo().search( + [('rfid_code', '=', rfid_code), + ('processing_panel', '=', order.processing_panel)]) + if panel_workorder: + panel_workorder.write({'production_line_state': '已下产线'}) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( [ ('rfid_code', '=', rfid_code), ('type', '=', '下产线'), From 93c3548b4068cfae0e2d498e59bfbd9fbd79e8f5 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 3 Jul 2024 11:05:55 +0800 Subject: [PATCH 06/74] =?UTF-8?q?=E6=B3=A8=E9=87=8AOCC=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/__manifest__.py | 2 +- sf_manufacturing/models/product_template.py | 4 ++-- sf_sale/models/quick_easy_order.py | 4 ++-- sf_sale/models/quick_easy_order_old.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 54538a7d..0447eb16 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -19,7 +19,7 @@ 'wizard/workpiece_delivery_views.xml', 'views/mrp_views_menus.xml', 'views/stock_lot_views.xml', - 'views/mrp_production_addional_change.xml', + # 'views/mrp_production_addional_change.xml', 'views/mrp_routing_workcenter_view.xml', 'views/production_line_view.xml', 'views/mrp_workcenter_views.xml', diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 4d05de81..f292d694 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -7,8 +7,8 @@ import os from odoo import models, fields, api, _ from odoo.exceptions import ValidationError 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 e3bf3002..11acc06f 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 4a86871039e7ae0ec14048c253fbb4d3cdf66831 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Wed, 3 Jul 2024 17:17:48 +0800 Subject: [PATCH 07/74] =?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=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E8=B0=83=E6=8B=A8=E5=8D=95=E8=B7=B3=E8=BD=AC=E6=8C=89?= =?UTF-8?q?=E9=92=AE=EF=BC=9B=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7=E6=8B=86?= =?UTF-8?q?=E8=A7=A3=E5=8D=95=E6=B7=BB=E5=8A=A0=E8=B0=83=E6=8B=A8=E5=8D=95?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E6=8C=89=E9=92=AE=E5=92=8C=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=88=80=E5=85=B7=E7=A7=BB=E5=8A=A8=E8=B7=B3=E8=BD=AC=E6=8C=89?= =?UTF-8?q?=E9=92=AE=EF=BC=9B2=E3=80=81=E5=8A=9F=E8=83=BD=E5=88=80?= =?UTF-8?q?=E5=85=B7=E6=8B=86=E8=A7=A3=E6=A8=A1=E5=9E=8B=EF=BC=8Cform?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E6=B7=BB=E5=8A=A0=E6=8A=A5=E5=BA=9Fpage?= =?UTF-8?q?=E9=A1=B5=E5=AD=98=E6=94=BE=E5=88=80=E5=85=B7=E7=89=A9=E6=96=99?= =?UTF-8?q?=E6=8A=A5=E5=BA=9F=E5=8D=95=EF=BC=8C=E4=BC=98=E5=8C=96=E6=8B=86?= =?UTF-8?q?=E8=A7=A3=E6=B5=81=E7=A8=8B=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=88=80=E5=85=B7=E6=8B=86=E8=A7=A3=E7=A7=BB=E5=8A=A8=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=EF=BC=9B3=E3=80=81=E5=8A=9F=E8=83=BD=E5=88=80?= =?UTF-8?q?=E5=85=B7=E5=87=BA=E5=85=A5=E5=BA=93=E5=88=97=E8=A1=A8=E8=A7=86?= =?UTF-8?q?=E5=9B=BE=E6=B7=BB=E5=8A=A0=E6=8B=86=E8=A7=A3=E5=8D=95=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E9=93=BE=E6=8E=A5=EF=BC=9B4=E3=80=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=88=80=E5=85=B7=E5=AE=89=E5=85=A8=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=B7=BB=E5=8A=A0=E5=90=8D=E7=A7=B0=E5=94=AF?= =?UTF-8?q?=E4=B8=80=E9=AA=8C=E8=AF=81=EF=BC=8C=E4=BC=98=E5=8C=96=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E8=AE=A1=E7=AE=97=E6=96=B9=E6=B3=95=EF=BC=9B5?= =?UTF-8?q?=E3=80=81=E4=BC=98=E5=8C=96=E5=8A=9F=E8=83=BD=E5=88=80=E5=85=B7?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=88=B0cloud=E7=9A=84=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E8=A7=A3=E5=86=B3=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_tool_management/models/base.py | 103 +++++++++++++----- sf_tool_management/models/functional_tool.py | 41 ++++--- .../models/functional_tool_enroll.py | 12 +- .../views/functional_tool_views.xml | 16 +-- sf_tool_management/views/tool_base_views.xml | 52 ++++++++- 5 files changed, 158 insertions(+), 66 deletions(-) diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 12f7e79e..b77e6469 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -669,6 +669,20 @@ class FunctionalToolAssembly(models.Model): :return: """ + picking_num = fields.Integer('调拨单数量', compute='compute_picking_num', store=True) + + @api.depends('assemble_status') + def compute_picking_num(self): + for item in self: + picking_ids = self.env['stock.picking'].sudo().search([('origin', '=', item.assembly_order_code)]) + item.picking_num = len(picking_ids) + + def open_tool_stock_picking(self): + action = self.env.ref('stock.action_picking_tree_all') + result = action.read()[0] + result['domain'] = [('origin', '=', self.assembly_order_code)] + return result + @api.model_create_multi def create(self, vals): obj = super(FunctionalToolAssembly, self).create(vals) @@ -758,7 +772,7 @@ class FunctionalToolDismantle(models.Model): num = "%03d" % m return 'GNDJ-CJD-%s-%s' % (datetime, num) - functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, + functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True, domain=[('functional_tool_status', '!=', '已拆除'), ('current_location', '=', '刀具房')]) tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, @@ -778,6 +792,16 @@ class FunctionalToolDismantle(models.Model): scrap_ids = fields.One2many('stock.scrap', 'functional_tool_dismantle_id', string='报废单号', readonly=True) grinding_id = fields.Char('磨削单号', readonly=True) + picking_id = fields.Many2one('stock.picking', string='刀具物料调拨单') + picking_num = fields.Integer('调拨单数量', default=0, compute='compute_picking_num', store=True) + + @api.depends('picking_id') + def compute_picking_num(self): + for item in self: + if item.picking_id: + item.picking_num = 1 + else: + item.picking_num = 0 state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True) active = fields.Boolean('有效', default=True) @@ -791,7 +815,14 @@ class FunctionalToolDismantle(models.Model): handle_rfid = fields.Char(string='刀柄Rfid', compute='_compute_functional_tool_num', store=True) handle_lot_id = fields.Many2one('stock.lot', string='刀柄序列号', compute='_compute_functional_tool_num', store=True) - scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True) + scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True, compute='compute_scrap_boolean', + store=True) + + @api.depends('dismantle_cause') + def compute_scrap_boolean(self): + for item in self: + if item.dismantle_cause not in ['寿命到期报废', '崩刀报废']: + item.scrap_boolean = False # 整体式 integral_product_id = fields.Many2one('product.product', string='整体式刀具', @@ -974,20 +1005,19 @@ class FunctionalToolDismantle(models.Model): if self.chuck_freight_id: datas['picking'].append({'lot_id': self.chuck_lot_id, 'destination': self.chuck_freight_id}) self.create_tool_picking_scrap(datas) - # ===============修改功能刀具数据===== + # ===============创建功能刀具拆解移动记录===== + self.env['stock.move'].create_functional_tool_stock_move(self) + # 修改功能刀具数据 self.functional_tool_id.write({ 'rfid_dismantle': self.functional_tool_id.rfid, 'rfid': '', 'functional_tool_status': '已拆除' }) - # 创建功能刀具拆解移动记录 - location_dismantle_id = self.env['stock.location'].search([('name', '=', '拆解')]) # 修改拆解单的值 self.write({ - 'rfid_dismantle': self.rfid, 'dismantle_data': fields.Datetime.now(), 'dismantle_person': self.env.user.name, - 'rfid': '', + 'rfid': '%s(已拆解)' % self.rfid, 'state': '已拆解' }) logging.info('【%s】刀具拆解成功!' % self.name) @@ -1001,6 +1031,7 @@ class FunctionalToolDismantle(models.Model): self.env['stock.scrap'].create_tool_dismantle_stock_scrap(data['lot_id'], self) if picking_data: picking_id = self.env['stock.picking'].create_tool_dismantle_picking(self) + self.picking_id = picking_id.id self.env['stock.move'].create_tool_stock_move({'data': picking_data, 'picking_id': picking_id}) # 将刀具物料出库库单的状态更改为就绪 picking_id.action_confirm() @@ -1010,6 +1041,27 @@ class FunctionalToolDismantle(models.Model): picking_id.action_set_quantities_to_reservation() picking_id.button_validate() + def action_open_reference1(self): + self.ensure_one() + return { + 'res_model': self._name, + 'type': 'ir.actions.act_window', + 'views': [[False, "form"]], + 'res_id': self.id, + } + + def open_function_tool_stock_move_line(self): + action = self.env.ref('sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_act') + result = action.read()[0] + result['domain'] = [('functional_tool_dismantle_id', '=', self.id), ('qty_done', '>', 0)] + return result + + def open_tool_stock_picking(self): + action = self.env.ref('stock.action_picking_tree_all') + result = action.read()[0] + result['domain'] = [('origin', '=', self.code)] + return result + class StockPicking(models.Model): _inherit = 'stock.picking' @@ -1073,38 +1125,39 @@ class StockMove(models.Model): }) return True - def create_functional_tool_stock_move(self, location_inventory_id, stock_location_id, functional_tool_assembly_id, - name, obj, - tool_groups_id): + def create_functional_tool_stock_move(self, dismantle_id): """ 对功能刀具拆解过程的功能刀具进行库存移动,以及创建移动历史 """ + location_dismantle_id = self.env['stock.location'].search([('name', '=', '拆解')]) + if not location_dismantle_id: + raise ValidationError('缺少名称为【拆解】的仓库管理地点') + tool_id = dismantle_id.functional_tool_id # 创建库存移动记录 stock_move_id = self.env['stock.move'].sudo().create({ - 'name': name, - 'product_id': self.product_id.id, - 'location_id': location_inventory_id.id, - 'location_dest_id': stock_location_id.id, + 'name': dismantle_id.code, + 'product_id': tool_id.barcode_id.product_id.id, + 'location_id': tool_id.current_location_id.id, + 'location_dest_id': location_dismantle_id.id, 'product_uom_qty': 1.00, 'state': 'done' }) # 创建移动历史记录 stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.product_id.id, - 'functional_tool_name_id': functional_tool_assembly_id, - 'lot_id': self.id, + 'product_id': tool_id.barcode_id.product_id.id, + 'functional_tool_dismantle_id': dismantle_id.id, + 'lot_id': tool_id.barcode_id.id, 'move_id': stock_move_id.id, - 'install_tool_time': fields.Datetime.now(), 'qty_done': 1.0, 'state': 'done', - 'functional_tool_type_id': False if not obj else obj.functional_tool_type_id.id, - 'diameter': None if not obj else obj.after_assembly_functional_tool_diameter, - 'knife_tip_r_angle': None if not obj else obj.after_assembly_knife_tip_r_angle, - 'code': '' if not obj else obj.code, - 'rfid': '' if not obj else obj.rfid, - 'functional_tool_name': '' if not obj else obj.after_assembly_functional_tool_name, - 'tool_groups_id': False if not tool_groups_id else tool_groups_id.id + 'functional_tool_type_id': tool_id.sf_cutting_tool_type_id.id, + 'diameter': tool_id.functional_tool_diameter, + 'knife_tip_r_angle': tool_id.knife_tip_r_angle, + 'code': tool_id.code, + 'rfid': tool_id.rfid, + 'functional_tool_name': tool_id.name, + 'tool_groups_id': tool_id.tool_groups_id.id }) return stock_move_id, stock_move_line_id diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py index 18f0a466..4e791fe5 100644 --- a/sf_tool_management/models/functional_tool.py +++ b/sf_tool_management/models/functional_tool.py @@ -252,9 +252,7 @@ class FunctionalCuttingToolEntity(models.Model): def open_safety_stock(self): action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act') result = action.read()[0] - result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter), - ('knife_tip_r_angle', '=', self.knife_tip_r_angle), - ('coarse_middle_thin', '=', self.coarse_middle_thin)] + result['domain'] = [('id', '=', self.safe_inventory_id.id)] return result def tool_inventory_displacement_out(self): @@ -372,6 +370,7 @@ class StockMoveLine(models.Model): _order = 'date desc' functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单') + functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string='功能刀具拆解单') functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, group_expand='_read_group_functional_tool_type_id') functional_tool_name = fields.Char('刀具名称') @@ -392,6 +391,9 @@ class StockMoveLine(models.Model): if self.functional_tool_name_id: action = self.functional_tool_name_id.action_open_reference1() return action + if self.functional_tool_dismantle_id: + action = self.functional_tool_dismantle_id.action_open_reference1() + return action elif self.move_id: action = self.move_id.action_open_reference() if action['res_model'] != 'stock.move': @@ -409,13 +411,14 @@ class RealTimeDistributionOfFunctionalTools(models.Model): _inherit = ['mail.thread'] _description = '功能刀具安全库存' - name = fields.Char('名称', readonly=True, compute='_compute_name', store=True) + name = fields.Char('名称', compute='_compute_num', store=True) functional_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True) - tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=False, required=True) - sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False, - group_expand='_read_mrs_cutting_tool_type_ids', store=True) - diameter = fields.Float(string='刀具直径(mm)', readonly=False) - knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False) + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_num', store=True) + sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', + compute='_compute_num', store=True, + group_expand='_read_mrs_cutting_tool_type_ids') + diameter = fields.Float(string='刀具直径(mm)', compute='_compute_num', store=True) + knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', compute='_compute_num', store=True) tool_stock_num = fields.Integer(string='刀具房数量', compute='_compute_stock_num', store=True) side_shelf_num = fields.Integer(string='线边刀库数量', compute='_compute_stock_num', store=True) on_tool_stock_num = fields.Integer(string='机内刀库数量', compute='_compute_stock_num', store=True) @@ -460,22 +463,18 @@ class RealTimeDistributionOfFunctionalTools(models.Model): active = fields.Boolean(string='已归档', default=True) - @api.onchange('functional_name_id') - def _onchange_num(self): + @api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle', + 'functional_name_id.functional_cutting_tool_model_id') + def _compute_num(self): for item in self: if item.functional_name_id: item.tool_groups_id = item.functional_name_id.tool_groups_id.id item.sf_cutting_tool_type_id = item.functional_name_id.functional_cutting_tool_model_id.id item.diameter = item.functional_name_id.diameter item.knife_tip_r_angle = item.functional_name_id.angle - - @api.depends('functional_name_id') - def _compute_name(self): - for obj in self: - if obj.tool_groups_id: - obj.name = obj.functional_name_id.name + item.name = item.functional_name_id.name else: - obj.sudo().name = '' + item.sudo().name = '' @api.constrains('min_stock_num', 'max_stock_num') def _check_stock_num(self): @@ -583,4 +582,10 @@ class RealTimeDistributionOfFunctionalTools(models.Model): for vals in vals_list: vals['status_create'] = False records = super(RealTimeDistributionOfFunctionalTools, self).create(vals_list) + for item in records: + if item: + record = self.search([('functional_name_id', '=', item.functional_name_id.id)]) + if len(record) > 1: + raise ValidationError( + '功能刀具名称为【%s】的安全库存已经存在,请勿重复创建!!!' % item.functional_name_id.name) return records diff --git a/sf_tool_management/models/functional_tool_enroll.py b/sf_tool_management/models/functional_tool_enroll.py index 9c5b1417..ba3553e1 100644 --- a/sf_tool_management/models/functional_tool_enroll.py +++ b/sf_tool_management/models/functional_tool_enroll.py @@ -37,14 +37,10 @@ class ToolDatasync(models.Model): def _cron_tool_datasync_all(self): try: - self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all() - - self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all() - self.env['sf.tool.material.search'].sudo().sync_enroll_tool_material_all() - + self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all() self.env['sf.fixture.material.search'].sudo().sync_enroll_fixture_material_all() - + self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all() self.env['sf.functional.cutting.tool.entity'].sudo().esync_enroll_functional_tool_entity_all() logging.info("已全部同步完成!!!") # self.env['sf.functional.tool.warning'].sudo().sync_enroll_functional_tool_warning_all() @@ -106,7 +102,7 @@ class StockLot(models.Model): logging.info("没有刀具物料序列号信息") except Exception as e: logging.info("刀具物料序列号同步失败:%s" % e) - + class ToolMaterial(models.Model): _inherit = 'sf.tool.material.search' @@ -198,7 +194,7 @@ class FunctionalCuttingToolEntity(models.Model): for item in objs_all: val = { 'id': item.id, - 'code': item.code, + 'code': False if not item.code else item.code.split('-', 1)[1], 'name': item.name, 'rfid': item.rfid, 'tool_groups_name': item.tool_groups_id.name, diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml index 7211548c..103583c2 100644 --- a/sf_tool_management/views/functional_tool_views.xml +++ b/sf_tool_management/views/functional_tool_views.xml @@ -29,7 +29,6 @@ - @@ -61,13 +60,9 @@ +

@@ -763,10 +775,25 @@
+
+ + +

@@ -777,12 +804,13 @@ - - + + + @@ -883,8 +911,22 @@ - - + + + + + + + + + + + + + + From d60fefb2ec2bec9ccfde0aac89a2e2430bb436aa Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Wed, 3 Jul 2024 17:33:29 +0800 Subject: [PATCH 08/74] 1 --- sf_tool_management/views/tool_base_views.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sf_tool_management/views/tool_base_views.xml b/sf_tool_management/views/tool_base_views.xml index c020d33c..c180c917 100644 --- a/sf_tool_management/views/tool_base_views.xml +++ b/sf_tool_management/views/tool_base_views.xml @@ -775,7 +775,7 @@
From a7a8e7361685c8c9355d4619c9d441af0954df0d Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 3 Jul 2024 17:35:29 +0800 Subject: [PATCH 09/74] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=BF=94=E5=B7=A5?= =?UTF-8?q?=E5=90=91=E5=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/__manifest__.py | 3 +- sf_manufacturing/models/mrp_production.py | 35 ++++++++++++++++++- sf_manufacturing/models/mrp_workorder.py | 19 ++++++++-- sf_manufacturing/security/ir.model.access.csv | 3 ++ .../views/mrp_production_addional_change.xml | 23 ++++++++++-- sf_manufacturing/views/mrp_workorder_view.xml | 8 ++--- sf_manufacturing/wizard/__init__.py | 1 + sf_manufacturing/wizard/rework_wizard.py | 30 ++++++++++++++++ .../wizard/rework_wizard_views.xml | 31 ++++++++++++++++ 9 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 sf_manufacturing/wizard/rework_wizard.py create mode 100644 sf_manufacturing/wizard/rework_wizard_views.xml diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 0447eb16..56b8514b 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -17,9 +17,10 @@ 'security/group_security.xml', 'security/ir.model.access.csv', 'wizard/workpiece_delivery_views.xml', + 'wizard/rework_wizard_views.xml', 'views/mrp_views_menus.xml', 'views/stock_lot_views.xml', - # 'views/mrp_production_addional_change.xml', + 'views/mrp_production_addional_change.xml', 'views/mrp_routing_workcenter_view.xml', 'views/production_line_view.xml', 'views/mrp_workcenter_views.xml', diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 73782a9c..30a0770e 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -24,6 +24,7 @@ class MrpProduction(models.Model): work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')], string='工单状态', default='未排') + detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告') # state = fields.Selection(selection_add=[ # ('pending_scheduling', '待排程'), # ('pending_processing', '待加工'), @@ -34,9 +35,10 @@ class MrpProduction(models.Model): ('confirmed', '待排程'), ('pending_cam', '待加工'), ('progress', '加工中'), + ('rework', '返工'), ('to_close', 'To Close'), ('done', 'Done'), - ('cancel', 'Cancelled')], string='State', + ('cancel', '报废')], string='State', compute='_compute_state', copy=False, index=True, readonly=True, store=True, tracking=True, help=" * Draft: The MO is not confirmed yet.\n" @@ -683,3 +685,34 @@ class MrpProduction(models.Model): 'view_mode': 'tree,form', }) return action + + # 返工 + def button_rework(self): + return True + + # 报废 + def button_scrap_new(self): + return True + + # 更新程序 + def button_update_program(self): + return True + + +class sf_detection_result(models.Model): + _name = 'sf.detection.result' + _description = "检测结果" + + production_id = fields.Many2one('mrp.production') + processing_panel = fields.Char('加工面') + routing_type = fields.Selection([ + ('装夹预调', '装夹预调'), + ('CNC加工', 'CNC加工')], string="工序类型") + + rework_reason = fields.Selection( + [("programming", "编程"), ("cutter", "刀具"), ("operate computer", "操机"), + ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) + detailed_reason = fields.Text('详细原因') + test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], default='合格', + string="检测结果", tracking=True) + test_report = fields.Binary('检测报告', readonly=True) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 46bcde47..93d1b853 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -200,8 +200,8 @@ class ResMrpWorkOrder(models.Model): is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False) is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False) reason = fields.Selection( - [("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"), - ("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True) + [("programming", "编程"), ("cutter", "刀具"), ("operate computer", "操机"), + ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) detailed_reason = fields.Text('详细原因') # is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False) @@ -460,6 +460,21 @@ class ResMrpWorkOrder(models.Model): else: raise UserError(_("该工单暂未完成,无法进行工件配送")) + def button_rework_pre(self): + # production_ids |= self.production_id + return { + 'name': _('返工'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'sf.rework.wizard', + 'target': 'new', + 'context': { + 'default_workorder_id': self.id, + 'default_production_ids': [(6, 0, [self.production_id.id])], + }} + + + # 拼接工单对象属性值 def json_workorder_str(self, k, production, route, item): # 计算预计时长duration_expected diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index ec2d5a76..20a58086 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -141,3 +141,6 @@ access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,m access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0 access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0 +access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0 +access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0 + diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index b5e348b9..fef027a7 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -87,7 +87,7 @@ - + @@ -106,7 +106,14 @@ + diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index b77e6469..5156a295 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -313,42 +313,27 @@ class CAMWorkOrderProgramKnifePlan(models.Model): 'applicant': None, 'sf_functional_tool_assembly_id': None}) - def create_cam_work_plan(self, cnc_processing_ids): + def create_cam_work_plan(self, cnc_processing): """ 根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划 """ - for cnc_processing in cnc_processing_ids: - status = False - if cnc_processing.cutting_tool_name: - functional_tools = self.env['sf.real.time.distribution.of.functional.tools'].sudo().search( - [('name', '=', cnc_processing.cutting_tool_name)]) - if functional_tools: - for functional_tool in functional_tools: - if functional_tool.on_tool_stock_num == 0: - if functional_tool.tool_stock_num == 0 and functional_tool.side_shelf_num == 0: - status = True - else: - status = True - if status: - knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({ - 'name': cnc_processing.workorder_id.production_id.name, - 'cam_procedure_code': cnc_processing.program_name, - 'filename': cnc_processing.cnc_id.name, - 'functional_tool_name': cnc_processing.cutting_tool_name, - 'cam_cutter_spacing_code': cnc_processing.cutting_tool_no, - 'process_type': cnc_processing.processing_type, - 'margin_x_y': float(cnc_processing.margin_x_y), - 'margin_z': float(cnc_processing.margin_z), - 'finish_depth': float(cnc_processing.depth_of_processing_z), - 'extension_length': float(cnc_processing.cutting_tool_extension_length), - 'shank_model': cnc_processing.cutting_tool_handle_type, - 'estimated_processing_time': cnc_processing.estimated_processing_time, - }) - logging.info('CAM工单程序用刀计划创建成功!!!') - # 创建装刀请求 - knife_plan.apply_for_tooling() - else: - logging.info('功能刀具【%s】满足CNC用刀需求!!!' % cnc_processing.cutting_tool_name) + knife_plan = self.env['sf.cam.work.order.program.knife.plan'].sudo().create({ + 'name': cnc_processing.workorder_id.production_id.name, + 'cam_procedure_code': cnc_processing.program_name, + 'filename': cnc_processing.cnc_id.name, + 'functional_tool_name': cnc_processing.cutting_tool_name, + 'cam_cutter_spacing_code': cnc_processing.cutting_tool_no, + 'process_type': cnc_processing.processing_type, + 'margin_x_y': float(cnc_processing.margin_x_y), + 'margin_z': float(cnc_processing.margin_z), + 'finish_depth': float(cnc_processing.depth_of_processing_z), + 'extension_length': float(cnc_processing.cutting_tool_extension_length), + 'shank_model': cnc_processing.cutting_tool_handle_type, + 'estimated_processing_time': cnc_processing.estimated_processing_time, + }) + logging.info('CAM工单程序用刀计划创建成功!!!') + # 创建装刀请求 + knife_plan.apply_for_tooling() def unlink_cam_plan(self, production): for item in production: @@ -834,7 +819,7 @@ class FunctionalToolDismantle(models.Model): integral_lot_id = fields.Many2one('stock.lot', string='整体式刀具批次', compute='_compute_functional_tool_num', store=True) integral_freight_id = fields.Many2one('sf.shelf.location', '整体式刀具目标货位', - domain="[('product_id', 'in', (integral_product_id, False))]") + domain="[('product_id', 'in', (integral_product_id, False)),('location_id.name', '=', '刀具房')]") # 刀片 blade_product_id = fields.Many2one('product.product', string='刀片', compute='_compute_functional_tool_num', @@ -844,7 +829,7 @@ class FunctionalToolDismantle(models.Model): blade_brand_id = fields.Many2one('sf.machine.brand', string='刀片品牌', related='blade_product_id.brand_id') blade_lot_id = fields.Many2one('stock.lot', string='刀片批次', compute='_compute_functional_tool_num', store=True) blade_freight_id = fields.Many2one('sf.shelf.location', '刀片目标货位', - domain="[('product_id', 'in', (blade_product_id, False))]") + domain="[('product_id', 'in', (blade_product_id, False)),('location_id.name', '=', '刀具房')]") # 刀杆 bar_product_id = fields.Many2one('product.product', string='刀杆', compute='_compute_functional_tool_num', @@ -854,7 +839,7 @@ class FunctionalToolDismantle(models.Model): bar_brand_id = fields.Many2one('sf.machine.brand', string='刀杆品牌', related='bar_product_id.brand_id') bar_lot_id = fields.Many2one('stock.lot', string='刀杆批次', compute='_compute_functional_tool_num', store=True) bar_freight_id = fields.Many2one('sf.shelf.location', '刀杆目标货位', - domain="[('product_id', 'in', (bar_product_id, False))]") + domain="[('product_id', 'in', (bar_product_id, False)),('location_id.name', '=', '刀具房')]") # 刀盘 pad_product_id = fields.Many2one('product.product', string='刀盘', compute='_compute_functional_tool_num', @@ -864,7 +849,7 @@ class FunctionalToolDismantle(models.Model): pad_brand_id = fields.Many2one('sf.machine.brand', string='刀盘品牌', related='pad_product_id.brand_id') pad_lot_id = fields.Many2one('stock.lot', string='刀盘批次', compute='_compute_functional_tool_num', store=True) pad_freight_id = fields.Many2one('sf.shelf.location', '刀盘目标货位', - domain="[('product_id', 'in', (pad_product_id, False))]") + domain="[('product_id', 'in', (pad_product_id, False)),('location_id.name', '=', '刀具房')]") # 夹头 chuck_product_id = fields.Many2one('product.product', string='夹头', compute='_compute_functional_tool_num', @@ -874,7 +859,7 @@ class FunctionalToolDismantle(models.Model): chuck_brand_id = fields.Many2one('sf.machine.brand', string='夹头品牌', related='chuck_product_id.brand_id') chuck_lot_id = fields.Many2one('stock.lot', string='夹头批次', compute='_compute_functional_tool_num', store=True) chuck_freight_id = fields.Many2one('sf.shelf.location', '夹头目标货位', - domain="[('product_id', 'in', (chuck_product_id, False))]") + domain="[('product_id', 'in', (chuck_product_id, False)),('location_id.name', '=', '刀具房')]") @api.onchange('functional_tool_id') def _onchange_freight(self): diff --git a/sf_tool_management/models/mrp_workorder.py b/sf_tool_management/models/mrp_workorder.py index e21941ca..a2886add 100644 --- a/sf_tool_management/models/mrp_workorder.py +++ b/sf_tool_management/models/mrp_workorder.py @@ -10,6 +10,8 @@ class CNCprocessing(models.Model): _inherit = 'sf.cnc.processing' _description = 'CNC加工用刀检测' + tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='刀具状态', default='0') + # ==========MES装刀指令接口========== # def register_cnc_processing(self, knife_plan): # config = self.env['res.config.settings'].get_values() @@ -29,13 +31,66 @@ class CNCprocessing(models.Model): # else: # raise ValidationError("MES装刀指令发送失败") + def cnc_tool_checkout(self, cnc_processing_ids): + """ + 根据传入的工单信息,查询是否有需要的功能刀具,如果没有则生成CAM工单程序用刀计划 + """ + cam_id = self.env['sf.cam.work.order.program.knife.plan'] + production_ids = [] # 制造订单集 + datas = {} # 缺刀/无效刀集 + for cnc_processing in cnc_processing_ids: + production_name = cnc_processing.production_id.name # 制造订单 + processing_panel = cnc_processing.workorder_id.processing_panel # 加工面 + if not datas.get(production_name): + datas.update({production_name: {}}) + production_ids.append(cnc_processing.production_id) + if not datas.get(production_name).get(processing_panel): + datas.get(production_name).update({processing_panel: {'缺刀': [], '无效刀': []}}) + if cnc_processing.cutting_tool_name: + tool_name = cnc_processing.cutting_tool_name + # 检验CNC用刀是否是功能刀具清单中的刀具 + tool_inventory_id = self.env['sf.tool.inventory'].sudo().search([('name', '=', tool_name)]) + if not tool_inventory_id: + datas[production_name][processing_panel]['无效刀'].append(cnc_processing.cutting_tool_name) + cnc_processing.tool_state = '2' + # todo 无效刀处理逻辑 + + # 跳过本次循环 + continue + # 校验CNC用刀在系统是否存在 + functional_tools = self.env['sf.functional.cutting.tool.entity'].sudo().search( + [('tool_name_id', '=', tool_inventory_id.id), ('functional_tool_status', '=', '正常')]) + # 判断线边、机内是否有满足条件的刀 + if not functional_tools.filtered(lambda p: p.current_location in ('线边刀库', '机内刀库')): + datas[production_name][processing_panel]['缺刀'].append(cnc_processing.cutting_tool_name) + cnc_processing.tool_state = '1' + # 判断是否有满足条件的刀 + if not functional_tools: + # 创建CAM申请装刀记录 + cam_id.create_cam_work_plan(cnc_processing) + logging.info('成功调用CAM工单程序用刀计划创建方法!!!') + for production_id in production_ids: + if production_id: + data = datas.get(production_id.name) + tool_state_remark = '' + # todo 对无效刀信息进行处理 + + # 对缺刀信息进行处理 + for key in data: + if tool_state_remark != '': + tool_state_remark = f'{tool_state_remark}\n{key}缺刀:{data.get(key).get("缺刀")}' + else: + tool_state_remark = f'{key}缺刀:{data.get(key).get("缺刀")}' + if production_id.tool_state == '1': + production_id.write({ + 'tool_state_remark': tool_state_remark + }) + @api.model_create_multi def create(self, vals): obj = super(CNCprocessing, self).create(vals) - for item in obj: - # 调用CAM工单程序用刀计划创建方法 - self.env['sf.cam.work.order.program.knife.plan'].create_cam_work_plan(item) - logging.info('成功调用CAM工单程序用刀计划创建方法!!!') + # 调用CAM工单程序用刀计划创建方法 + self.cnc_tool_checkout(obj) return obj From 134e23ea2fc82c145bfa32d8662ea3308a183b54 Mon Sep 17 00:00:00 2001 From: yuxianghui <3437689193@qq.com> Date: Wed, 10 Jul 2024 16:25:18 +0800 Subject: [PATCH 25/74] =?UTF-8?q?1=E3=80=81=E9=87=8D=E6=9E=84=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=B7=A5=E5=BA=8F=E7=9A=84=E7=94=9F=E6=88=90=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_production.py | 130 +++++++++++++--------- 1 file changed, 75 insertions(+), 55 deletions(-) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index f9492a37..0b4da7cc 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -536,70 +536,90 @@ class MrpProduction(models.Model): def _reset_work_order_sequence(self): for rec in self: - sequence_list = {} + workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')) # 产品模型类型 model_type_id = rec.product_id.product_model_type_id # 产品加工面板 model_processing_panel = rec.product_id.model_processing_panel - if model_type_id: - if model_processing_panel: - tmpl_num = 1 - panel_list = model_processing_panel.split(',') - for panel in panel_list: - panel_sequence_list = {} - # 成品工序 - product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids - if product_routing_tmpl_ids: - for tmpl_id in product_routing_tmpl_ids: - panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) - tmpl_num += 1 - sequence_list.update({panel: panel_sequence_list}) - # 表面工艺工序 - # 模型类型的表面工艺工序模版 - surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids - # 产品选择的表面工艺 - model_process_parameters_ids = rec.product_id.model_process_parameters_ids - process_dict = {} - if model_process_parameters_ids: - for process_parameters_id in model_process_parameters_ids: - process_id = process_parameters_id.process_id - for surface_tmpl_id in surface_tmpl_ids: - if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: - surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name - process_dict.update({int(process_id.category_id.code): '%s-%s' % ( - surface_tmpl_name, process_parameters_id.name)}) - process_list = sorted(process_dict.keys()) - for process_num in process_list: - sequence_list.update({process_dict.get(process_num): tmpl_num}) - tmpl_num += 1 - # 坯料工序 - tmpl_num = 1 - embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids - if embryo_routing_tmpl_ids: - for tmpl_id in embryo_routing_tmpl_ids: - sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + if not workorder_ids: + sequence_list = {} + if model_type_id: + if model_processing_panel: + tmpl_num = 1 + panel_list = model_processing_panel.split(',') + for panel in panel_list: + panel_sequence_list = {} + # 成品工序 + product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids + if product_routing_tmpl_ids: + for tmpl_id in product_routing_tmpl_ids: + panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + tmpl_num += 1 + sequence_list.update({panel: panel_sequence_list}) + # 表面工艺工序 + # 模型类型的表面工艺工序模版 + surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids + # 产品选择的表面工艺 + model_process_parameters_ids = rec.product_id.model_process_parameters_ids + process_dict = {} + if model_process_parameters_ids: + for process_parameters_id in model_process_parameters_ids: + process_id = process_parameters_id.process_id + for surface_tmpl_id in surface_tmpl_ids: + if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: + surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name + process_dict.update({int(process_id.category_id.code): '%s-%s' % ( + surface_tmpl_name, process_parameters_id.name)}) + process_list = sorted(process_dict.keys()) + for process_num in process_list: + sequence_list.update({process_dict.get(process_num): tmpl_num}) tmpl_num += 1 + # 坯料工序 + tmpl_num = 1 + embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids + if embryo_routing_tmpl_ids: + for tmpl_id in embryo_routing_tmpl_ids: + sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + tmpl_num += 1 + else: + raise ValidationError('该产品【加工面板】为空!') else: - raise ValidationError('该产品【加工面板】为空!') + raise ValidationError('该产品没有选择【模版类型】!') - else: - raise ValidationError('该产品没有选择【模版类型】!') - - for work in rec.workorder_ids: - if sequence_list.get(work.name): - work.sequence = sequence_list[work.name] - elif sequence_list.get(work.processing_panel): - processing_panel = sequence_list.get(work.processing_panel) - if processing_panel.get(work.name): - work.sequence = processing_panel[work.name] + for work in rec.workorder_ids: + if sequence_list.get(work.name): + work.sequence = sequence_list[work.name] + elif sequence_list.get(work.processing_panel): + processing_panel = sequence_list.get(work.processing_panel) + if processing_panel.get(work.name): + work.sequence = processing_panel[work.name] + else: + raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) else: raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) - else: - raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) - # if work.name == '获取CNC加工程序': - # work.button_start() - # #work.fetchCNC() - # work.button_finish() + # 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排 + elif rec.workorder_ids.filtered(lambda item: item.sequence == 0): + # 获取新增的返工工单 + work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0) + # 获取当前返工面最后一个工单工序 + sequence_max = sorted( + rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel), + key=lambda item: item.sequence, reverse=True)[0].sequence + # 对当前返工工单之后的工单工序进行重排 + work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max) + for work_id in work_order_ids: + work_id.sequence = work_id.sequence + 3 + # 生成新增的返工工单的工序 + # 成品工序 + panel_sequence_list = {} + product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids + if product_routing_tmpl_ids: + for tmpl_id in product_routing_tmpl_ids: + sequence_max += 1 + panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max}) + for work_id in work_ids: + if panel_sequence_list.get(work_id.name): + work_id.sequence = panel_sequence_list[work_id.name] # 创建工单并进行排序 def _create_workorder(self, item): From 9aca786522da3792a5e5dd50a9efa413aeda24ec Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 10 Jul 2024 17:36:26 +0800 Subject: [PATCH 26/74] =?UTF-8?q?1.=E4=BC=98=E5=8C=96=E5=B7=A5=E5=8D=95?= =?UTF-8?q?=E6=8E=92=E5=BA=8F2.=E6=96=B0=E5=A2=9E=E8=B0=83=E5=8F=96cloud?= =?UTF-8?q?=E7=BC=96=E7=A8=8B=E5=8D=95=E7=9A=84=E7=8A=B6=E6=80=81=E6=96=B9?= =?UTF-8?q?=E6=B3=953.=E5=88=B6=E9=80=A0=E8=AE=A2=E5=8D=95=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=8A=B6=E6=80=81=E4=B8=BA=E8=BF=94=E5=B7=A5=E5=8F=8A?= =?UTF-8?q?=E7=BC=96=E7=A8=8B=E7=8A=B6=E6=80=81=E4=B8=BA"=E5=B7=B2?= =?UTF-8?q?=E7=BC=96=E7=A8=8B"=EF=BC=8C=E2=80=9C=E5=B7=B2=E7=BC=96?= =?UTF-8?q?=E7=A8=8B=E6=9C=AA=E4=B8=8B=E5=8F=91=E2=80=9D=E7=9A=84=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E6=9D=A1=E4=BB=B64=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=BF=94=E5=B7=A5=E4=B8=94=E7=BC=96=E7=A8=8B=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=88=B6=E9=80=A0=E8=AE=A2=E5=8D=95=E5=AE=9A=E6=97=B6=E8=8E=B7?= =?UTF-8?q?=E5=8F=96Cloud=E7=BC=96=E7=A8=8B=E5=8D=95=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9A=84=E5=AE=9A=E6=97=B6=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/data/stock_data.xml | 10 + sf_manufacturing/models/mrp_production.py | 176 ++++++++++++------ .../views/mrp_production_addional_change.xml | 141 +++++++------- sf_manufacturing/views/mrp_workorder_view.xml | 1 - sf_manufacturing/wizard/rework_wizard.py | 3 +- 5 files changed, 203 insertions(+), 128 deletions(-) diff --git a/sf_manufacturing/data/stock_data.xml b/sf_manufacturing/data/stock_data.xml index 07d59708..9220d827 100644 --- a/sf_manufacturing/data/stock_data.xml +++ b/sf_manufacturing/data/stock_data.xml @@ -1,6 +1,16 @@ + + 返工且编程中的制造订单定时获取Cloud编程单状态 + + code + model._cron_get_programming_state() + 3 + minutes + -1 + + 工序编码规则 mrp.routing.workcenter diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 7ac20df1..75411116 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import base64 import logging +import json import re import requests from itertools import groupby @@ -53,10 +54,12 @@ class MrpProduction(models.Model): programming_no = fields.Char('编程单号') work_state = fields.Char('业务状态') programming_state = fields.Selection( - [('编程中', '编程中'), ('已编程', '已编程')], string='编程状态', tracking=True) + [('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发')], string='编程状态', + tracking=True) glb_file = fields.Binary("glb模型文件") production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True) plan_start_processing_time = fields.Datetime('计划开始加工时间') + is_rework = fields.Boolean(string='是否返工', default=False) # production_line_state = fields.Selection( # [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')], # string='上/下产线', default='待上产线', tracking=True) @@ -156,20 +159,49 @@ class MrpProduction(models.Model): for production in self: production.maintenance_count = len(production.request_ids) + # 获取cloud编程单的状态 + def _cron_get_programming_state(self): + try: + reproduction = self.search([('state', '=', 'rework'), ('programming_state', '=', '编程中')]) + if reproduction: + programming_no = [item.programming_no for item in reproduction] + programming_no_str = ','.join(programming_no) + res = {'programming_no': programming_no_str} + logging.info('res=%s:' % res) + configsettings = self.env['res.config.settings'].get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/intelligent_programming/get_state' + config_url = configsettings['sf_url'] + url + ret = requests.post(config_url, json=res, data=None, headers=config_header) + ret = ret.json() + result = json.loads(ret['result']) + logging.info('cron_get_programming_state-ret:%s' % result) + if result['status'] == 1: + for item in result['programming_list']: + production = self.search( + [('state', '=', 'rework'), ('programming_no', '=', item['programming_no'])]) + if production: + production.write({'programming_state': '已编程未下发'}) + else: + raise UserError(ret['message']) + except Exception as e: + logging.info('cron_get_programming_state error:%s' % e) + # 编程单更新 def update_programming_state(self): try: - res = {'programming_no': self.programming_no} + res = {'programming_no': self.programming_no, 'manufacturing_type': 'rework'} logging.info('res=%s:' % res) configsettings = self.env['res.config.settings'].get_values() config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) url = '/api/intelligent_programming/reset_state_again' config_url = configsettings['sf_url'] + url - ret = requests.post(config_url, json={}, data=res, headers=config_header) + ret = requests.post(config_url, json=res, data=None, headers=config_header) ret = ret.json() - logging.info('update_programming_state-ret:%s' % ret) - if ret['status'] == 1: - self.write({'work_state': '已编程'}) + result = json.loads(ret['result']) + logging.info('update_programming_state-ret:%s' % result) + if result['status'] == 1: + self.write({'is_rework': True}) else: raise UserError(ret['message']) except Exception as e: @@ -200,7 +232,7 @@ class MrpProduction(models.Model): 'material_type_code': self.env['sf.materials.model'].search( [('id', '=', cnc.product_id.materials_type_id.id)]).materials_no, 'machining_processing_panel': cnc.product_id.model_processing_panel, - 'machining_precision': cnc.product_id.model_machining_precision, + 'machining_precision': '', 'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length, 'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height, 'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width, @@ -523,70 +555,90 @@ class MrpProduction(models.Model): def _reset_work_order_sequence(self): for rec in self: - sequence_list = {} + workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')) # 产品模型类型 model_type_id = rec.product_id.product_model_type_id # 产品加工面板 model_processing_panel = rec.product_id.model_processing_panel - if model_type_id: - if model_processing_panel: - tmpl_num = 1 - panel_list = model_processing_panel.split(',') - for panel in panel_list: - panel_sequence_list = {} - # 成品工序 - product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids - if product_routing_tmpl_ids: - for tmpl_id in product_routing_tmpl_ids: - panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) - tmpl_num += 1 - sequence_list.update({panel: panel_sequence_list}) - # 表面工艺工序 - # 模型类型的表面工艺工序模版 - surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids - # 产品选择的表面工艺 - model_process_parameters_ids = rec.product_id.model_process_parameters_ids - process_dict = {} - if model_process_parameters_ids: - for process_parameters_id in model_process_parameters_ids: - process_id = process_parameters_id.process_id - for surface_tmpl_id in surface_tmpl_ids: - if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: - surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name - process_dict.update({int(process_id.category_id.code): '%s-%s' % ( - surface_tmpl_name, process_parameters_id.name)}) - process_list = sorted(process_dict.keys()) - for process_num in process_list: - sequence_list.update({process_dict.get(process_num): tmpl_num}) - tmpl_num += 1 - # 坯料工序 - tmpl_num = 1 - embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids - if embryo_routing_tmpl_ids: - for tmpl_id in embryo_routing_tmpl_ids: - sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + if not workorder_ids: + sequence_list = {} + if model_type_id: + if model_processing_panel: + tmpl_num = 1 + panel_list = model_processing_panel.split(',') + for panel in panel_list: + panel_sequence_list = {} + # 成品工序 + product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids + if product_routing_tmpl_ids: + for tmpl_id in product_routing_tmpl_ids: + panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + tmpl_num += 1 + sequence_list.update({panel: panel_sequence_list}) + # 表面工艺工序 + # 模型类型的表面工艺工序模版 + surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids + # 产品选择的表面工艺 + model_process_parameters_ids = rec.product_id.model_process_parameters_ids + process_dict = {} + if model_process_parameters_ids: + for process_parameters_id in model_process_parameters_ids: + process_id = process_parameters_id.process_id + for surface_tmpl_id in surface_tmpl_ids: + if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: + surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name + process_dict.update({int(process_id.category_id.code): '%s-%s' % ( + surface_tmpl_name, process_parameters_id.name)}) + process_list = sorted(process_dict.keys()) + for process_num in process_list: + sequence_list.update({process_dict.get(process_num): tmpl_num}) tmpl_num += 1 + # 坯料工序 + tmpl_num = 1 + embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids + if embryo_routing_tmpl_ids: + for tmpl_id in embryo_routing_tmpl_ids: + sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + tmpl_num += 1 + else: + raise ValidationError('该产品【加工面板】为空!') else: - raise ValidationError('该产品【加工面板】为空!') + raise ValidationError('该产品没有选择【模版类型】!') - else: - raise ValidationError('该产品没有选择【模版类型】!') - - for work in rec.workorder_ids: - if sequence_list.get(work.name): - work.sequence = sequence_list[work.name] - elif sequence_list.get(work.processing_panel): - processing_panel = sequence_list.get(work.processing_panel) - if processing_panel.get(work.name): - work.sequence = processing_panel[work.name] + for work in rec.workorder_ids: + if sequence_list.get(work.name): + work.sequence = sequence_list[work.name] + elif sequence_list.get(work.processing_panel): + processing_panel = sequence_list.get(work.processing_panel) + if processing_panel.get(work.name): + work.sequence = processing_panel[work.name] + else: + raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) else: raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) - else: - raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) - # if work.name == '获取CNC加工程序': - # work.button_start() - # #work.fetchCNC() - # work.button_finish() + # 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排 + elif rec.workorder_ids.filtered(lambda item: item.sequence == 0): + # 获取新增的返工工单 + work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0) + # 获取当前返工面最后一个工单工序 + sequence_max = sorted( + rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel), + key=lambda item: item.sequence, reverse=True)[0].sequence + # 对当前返工工单之后的工单工序进行重排 + work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max) + for work_id in work_order_ids: + work_id.sequence = work_id.sequence + 3 + # 生成新增的返工工单的工序 + # 成品工序 + panel_sequence_list = {} + product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids + if product_routing_tmpl_ids: + for tmpl_id in product_routing_tmpl_ids: + sequence_max += 1 + panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max}) + for work_id in work_ids: + if panel_sequence_list.get(work_id.name): + work_id.sequence = panel_sequence_list[work_id.name] # 创建工单并进行排序 def _create_workorder(self, item): diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index 8ddbb418..91a1eb84 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -1,63 +1,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - custom.mrp.production.tree mrp.production + + + @@ -129,8 +81,10 @@ - + @@ -364,6 +318,16 @@ mrp.workorder + + sequence + + + +

- + + + + + + - - - - - - - - - -
- - - - - - - - - - - - - - - - - -
-
-
- -
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+
+ +
diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py index e3ca710b..e37d460a 100644 --- a/sf_mrs_connect/models/sync_common.py +++ b/sf_mrs_connect/models/sync_common.py @@ -326,6 +326,8 @@ class sfProductionProcess(models.Model): production_process.name = item['name'] production_process.category_id = category.id production_process.remark = item['remark'] + production_process.processing_day = item['processing_day'] + production_process.travel_day = item['travel_day'] production_process.active = item['active'] else: self.create({ @@ -333,6 +335,8 @@ class sfProductionProcess(models.Model): "category_id": category.id, "code": item['code'], "remark": item['remark'], + "processing_day": item['processing_day'], + "travel_day": item['travel_day'], "active": item['active'], }) else: @@ -358,12 +362,16 @@ class sfProductionProcess(models.Model): "category_id": category.id, "code": item['code'], "remark": item['remark'], + "processing_day": item['processing_day'], + "travel_day": item['travel_day'], "active": item['active'], }) else: production_process.name = item['name'] production_process.category_id = category.id production_process.remark = item['remark'] + production_process.processing_day = item['processing_day'] + production_process.travel_day = item['travel_day'] production_process.active = item['active'] else: raise ValidationError("表面工艺认证未通过") @@ -1073,6 +1081,9 @@ class sfProductionProcessParameter(models.Model): [('code', '=', item['process_id_code'])]) if production_process_parameter: production_process_parameter.name = item['name'] + production_process_parameter.process_description = item['process_description'] + production_process_parameter.processing_day = item['processing_day'] + production_process_parameter.travel_day = item['travel_day'] production_process_parameter.active = item['active'] production_process_parameter.process_id = process.id production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search( @@ -1080,6 +1091,9 @@ class sfProductionProcessParameter(models.Model): else: self.create({ "name": item['name'], + "process_description": item['process_description'], + "processing_day": item['processing_day'], + "travel_day": item['travel_day'], "code": item['code'], "active": item['active'], "process_id": process.id, @@ -1107,6 +1121,9 @@ class sfProductionProcessParameter(models.Model): if not production_process_parameter: self.create({ "name": item['name'], + "process_description": item['process_description'], + "processing_day": item['processing_day'], + "travel_day": item['travel_day'], "code": item['code'], "active": item['active'], "process_id": process.id, @@ -1115,6 +1132,9 @@ class sfProductionProcessParameter(models.Model): }) else: production_process_parameter.name = item['name'] + production_process_parameter.process_description = item['process_description'] + production_process_parameter.processing_day = item['processing_day'] + production_process_parameter.travel_day = item['travel_day'] production_process_parameter.process_id = process.id production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search( [('materials_no', 'in', item['materials_model_ids_codes'])]) From 81158e2fb42c85eec3fea02e1627a8be28256615 Mon Sep 17 00:00:00 2001 From: mgw <1392924357@qq.com> Date: Tue, 16 Jul 2024 16:31:18 +0800 Subject: [PATCH 36/74] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=B6=E9=80=A0?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_production.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 6ccd40df..09f55db7 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -122,9 +122,9 @@ class MrpProduction(models.Model): if production.state == 'progress': if all(wo_state not in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')): production.state = 'pending_cam' - if production.state == 'pending_cam': - if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): - production.state = 'done' + # if production.state == 'pending_cam': + # if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): + # production.state = 'done' def action_check(self): """ From c536dde2cc978892293b898bc4d5a8eb48a2f2dd Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Tue, 16 Jul 2024 17:24:15 +0800 Subject: [PATCH 37/74] =?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 --- .../models/res_config_setting.py | 1 - sf_manufacturing/models/mrp_production.py | 40 ++++++++++--------- sf_manufacturing/models/mrp_workorder.py | 8 +++- sf_manufacturing/models/product_template.py | 4 +- .../views/mrp_production_addional_change.xml | 7 ++-- sf_manufacturing/wizard/rework_wizard.py | 5 ++- .../wizard/rework_wizard_views.xml | 3 +- sf_mrs_connect/controllers/controllers.py | 33 +++++++-------- sf_sale/models/quick_easy_order.py | 4 +- sf_sale/models/quick_easy_order_old.py | 4 +- 10 files changed, 62 insertions(+), 47 deletions(-) diff --git a/sf_machine_connect/models/res_config_setting.py b/sf_machine_connect/models/res_config_setting.py index 78d08003..16cc4d77 100644 --- a/sf_machine_connect/models/res_config_setting.py +++ b/sf_machine_connect/models/res_config_setting.py @@ -8,7 +8,6 @@ _logger = logging.getLogger(__name__) class ResBFMConfigSettings(models.TransientModel): _inherit = 'res.config.settings' - lost_agent_id = fields.Char('22') bfm_url = fields.Selection( [("https://bfm.cs.jikimo.com", "开发环境(https://bfm.cs.jikimo.com)"), ("https://bfm.t.jikimo.com", "测试环境(https://bfm.t.jikimo.com)"), diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index be35e436..4a0e6bb9 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -129,13 +129,16 @@ class MrpProduction(models.Model): if production.state == 'progress': if all(wo_state not in ('progress', 'done') for wo_state in production.workorder_ids.mapped('state')): production.state = 'pending_cam' - if production.state == 'pending_cam': - if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): - production.state = 'done' + # if production.state == 'pending_cam': + # if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): + # production.state = 'done' if any( ( - wo.test_results == '返工' and wo.state == 'done' and programming_state == '编程中') or wo.state == 'rework' or ( - wo.is_rework is True and wo.state == 'done') for wo in + wo.test_results == '返工' and wo.state == 'done' and production.programming_state == '已编程') or ( + wo.state == 'rework' and production.programming_state == '编程中') or ( + wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', + '已编程']) + for wo in production.workorder_ids): production.state = 'rework' @@ -198,8 +201,8 @@ class MrpProduction(models.Model): production.write({'programming_state': '已编程未下发' if item[ 'programming_state'] == '已编程' else '编程中'}) else: - self.write({'programming_state': '已编程未下发' if item[ - 'programming_state'] == '已编程' else '编程中'}) + return item['programming_state'] + else: raise UserError(ret['message']) except Exception as e: @@ -760,8 +763,11 @@ class MrpProduction(models.Model): # 返工 def button_rework(self): - if self.programming_state != '已编程': - self._cron_get_programming_state() + cloud_programming_state = None + if self.programming_state != '已编程' and self.reprogramming_num >= 1: + cloud_programming_state = self._cron_get_programming_state() + logging.info('cloud_programming_state:%s' % cloud_programming_state) + logging.info('programming_state:%s' % self.programming_state) return { 'name': _('返工'), 'type': 'ir.actions.act_window', @@ -771,8 +777,12 @@ class MrpProduction(models.Model): 'context': { 'default_production_id': self.id, 'default_product_id': self.product_id.id, - 'default_programming_state': self.programming_state, - 'default_is_reprogramming': True if self.programming_state in ['已编程'] else False + 'default_programming_state': self.programming_state if cloud_programming_state is not None else cloud_programming_state, + 'default_is_reprogramming': False if (cloud_programming_state in ['编程中', + '待编程'] and self.programming_state in [ + '编程中']) + or (cloud_programming_state in [ + '已编程'] and self.programming_state in ['已编程未下发']) else True } } @@ -807,13 +817,7 @@ class MrpProduction(models.Model): lambda m1: m1.state != 'rework' and m1.processing_panel == rework_workorder[0].processing_panel) if not new_pancel_workorder.cnc_ids: production.get_new_program(rework_workorder[0].processing_panel) - rework_production = self.env['mrp.production'].search( - [('state', '=', 'rework'), ('programming_no', '=', production.programming_no)]) - if rework_production: - rework_production.workorder_ids.filtered( - lambda m2: m2.state != 'rework' and m2.processing_panel == rework_workorder[ - 0].processing_panel and m2.routing_type == '装夹预调').write({'state': 'ready'}) - rework_production.write({'state': 'progress'}) + production.write({'state': 'progress', 'programming_state': '已下发'}) # 从cloud获取重新编程过的最新程序 def get_new_program(self, processing_panel): diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index b6571a04..6da74cbc 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -827,6 +827,7 @@ class ResMrpWorkOrder(models.Model): re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id), ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True), ('state', 'in', ['done', 'rework'])]) + logging.info('re_work:%s' % re_work.state) if workorder.state not in ['cancel', 'progress', 'rework']: logging.info('工序:%s' % workorder.routing_type) logging.info('状态:%s' % workorder.state) @@ -840,10 +841,14 @@ class ResMrpWorkOrder(models.Model): # if re_work: # workorder.state = 'rework' if workorder.production_id.state == 'rework': - if (workorder.routing_type == 'CNC加工' and re_work.state == 'done') or ( + if (workorder.routing_type == 'CNC加工' and workorder.state == 'done') or ( workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '编程中' and re_work): logging.info('面111:%s' % workorder.processing_panel) workorder.state = 'waiting' + elif workorder.production_id.state == 'progress': + logging.info('面222:%s' % workorder.processing_panel) + if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已下发' and re_work: + workorder.state = 'ready' # else: # if workorder.state not in ['cancel', 'rework']: # workorder.state = 'rework' @@ -998,6 +1003,7 @@ 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 == '返工' or record.is_rework is True else '', 'test_results': record.test_results, 'test_report': record.detection_report})]}) if record.routing_type == '解除装夹': diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index f292d694..4d05de81 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -7,8 +7,8 @@ import os from odoo import models, fields, api, _ from odoo.exceptions import ValidationError 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_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index ee767897..9ee96732 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -120,9 +120,9 @@

+ attrs='{"invisible": [("routing_type","in",["装夹预调","CNC加工"])],"readonly": [("programming_state","in",["编程中","已编程未下发"])]}'/> Date: Wed, 17 Jul 2024 09:46:23 +0800 Subject: [PATCH 38/74] =?UTF-8?q?=E8=BF=94=E5=B7=A5=E5=90=91=E5=AF=BC?= =?UTF-8?q?=E9=9A=90=E8=97=8F=E5=B7=A5=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/wizard/rework_wizard_views.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sf_manufacturing/wizard/rework_wizard_views.xml b/sf_manufacturing/wizard/rework_wizard_views.xml index b50cec1e..524bb8ae 100644 --- a/sf_manufacturing/wizard/rework_wizard_views.xml +++ b/sf_manufacturing/wizard/rework_wizard_views.xml @@ -9,7 +9,7 @@ - + From 6eaf92b8414162d06e26a4a468ec940817291650 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 17 Jul 2024 10:15:05 +0800 Subject: [PATCH 39/74] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_production.py | 26 +++++++++++------------ sf_manufacturing/wizard/rework_wizard.py | 8 +++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 6554a18f..b965a63e 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -831,17 +831,17 @@ class MrpProduction(models.Model): r = r.json() result = json.loads(r['result']) if result['status'] == 1: - # program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel) - # if os.path.exists(program_path_tmp_panel): - # files_r = os.listdir(program_path_tmp_panel) - # if files_r: - # for file_name in files_r: - # file_path = os.path.join(program_path_tmp_panel, file_name) - # os.remove(file_path) - # download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'], - # processing_panel) - # if download_state is False: - # raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no)) + program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel) + if os.path.exists(program_path_tmp_panel): + files_r = os.listdir(program_path_tmp_panel) + if files_r: + for file_name in files_r: + file_path = os.path.join(program_path_tmp_panel, file_name) + os.remove(file_path) + download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'], + processing_panel) + if download_state is False: + raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no)) productions = self.env['mrp.production'].search( [('programming_no', '=', self.programming_no), ('state', 'not in', ['cancel,done'])]) if productions: @@ -854,8 +854,8 @@ class MrpProduction(models.Model): panel_workorder.cnc_ids.sudo().unlink() self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( productions) - program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', - processing_panel) + # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', + # processing_panel) logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel) files_panel = os.listdir(program_path_tmp_panel) if files_panel: diff --git a/sf_manufacturing/wizard/rework_wizard.py b/sf_manufacturing/wizard/rework_wizard.py index dade9a14..2faf74d0 100644 --- a/sf_manufacturing/wizard/rework_wizard.py +++ b/sf_manufacturing/wizard/rework_wizard.py @@ -66,12 +66,12 @@ class ReworkWizard(models.TransientModel): self.production_id.detection_result_ids.filtered( lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write( {'handle_result': '已处理'}) + if self.is_reprogramming is False: + self.production_id.get_new_program(panel) if self.is_reprogramming is True: self.production_id.update_programming_state() - else: - self.production_id.do_update_program() - if self.production_id.state == 'progress': - self.production_id.write({'programming_state': '已编程'}) + if self.production_id.state == 'progress': + self.production_id.write({'programming_state': '已编程'}) @api.onchange('production_id') def onchange_processing_panel_id(self): From 8726f8cac3da3e9d413493aa23d906fae2458f73 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Wed, 17 Jul 2024 15:20:31 +0800 Subject: [PATCH 40/74] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8A=A5=E5=BA=9F?= =?UTF-8?q?=E5=90=91=E5=AF=BC=EF=BC=9B=E6=B7=BB=E5=8A=A0=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=88=B6=E9=80=A0=E8=AE=A2=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/__manifest__.py | 1 + sf_manufacturing/models/mrp_production.py | 160 +++++++++++++++--- sf_manufacturing/models/mrp_workorder.py | 124 +------------- sf_manufacturing/models/product_template.py | 4 +- sf_manufacturing/security/ir.model.access.csv | 2 + .../views/mrp_production_addional_change.xml | 6 +- sf_manufacturing/wizard/__init__.py | 1 + sf_manufacturing/wizard/production_wizard.py | 21 +++ .../wizard/production_wizard_views.xml | 34 ++++ sf_sale/models/quick_easy_order.py | 4 +- sf_sale/models/quick_easy_order_old.py | 4 +- 11 files changed, 209 insertions(+), 152 deletions(-) create mode 100644 sf_manufacturing/wizard/production_wizard.py create mode 100644 sf_manufacturing/wizard/production_wizard_views.xml diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 985b87a4..39da4482 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -19,6 +19,7 @@ 'security/ir.model.access.csv', 'wizard/workpiece_delivery_views.xml', 'wizard/rework_wizard_views.xml', + 'wizard/production_wizard_views.xml', 'views/mrp_views_menus.xml', 'views/stock_lot_views.xml', 'views/mrp_production_addional_change.xml', diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index b965a63e..f0ba816c 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -82,7 +82,7 @@ class MrpProduction(models.Model): part_drawing = fields.Binary('零件图纸') manual_quotation = fields.Boolean('人工编程', default=False, readonly=True) - rework_production = fields.Many2one('mrp.production', string='返工的制造订单') + is_scrap = fields.Boolean('是否报废', default=False) @api.depends( 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', @@ -211,7 +211,8 @@ class MrpProduction(models.Model): # 编程单更新 def update_programming_state(self): try: - res = {'programming_no': self.programming_no, 'manufacturing_type': 'rework'} + res = {'programming_no': self.programming_no, + 'manufacturing_type': 'rework' if self.is_scrap is False else 'scrap'} logging.info('res=%s:' % res) configsettings = self.env['res.config.settings'].get_values() config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) @@ -486,28 +487,6 @@ class MrpProduction(models.Model): for workorder in production.workorder_ids: workorder.duration_expected = workorder._get_duration_expected() - # 在之前的销售单上重新生成制造订单 - def create_production1_values(self, production): - production_values_str = {'origin': production.origin, - 'product_id': production.product_id.id, - 'product_description_variants': production.product_description_variants, - 'product_qty': production.product_qty, - 'product_uom_id': production.product_uom_id.id, - 'location_src_id': production.location_src_id.id, - 'location_dest_id': production.location_dest_id.id, - 'bom_id': production.bom_id.id, - 'date_deadline': production.date_deadline, - 'date_planned_start': production.date_planned_start, - 'date_planned_finished': production.date_planned_finished, - 'procurement_group_id': False, - 'propagate_cancel': production.propagate_cancel, - 'orderpoint_id': production.orderpoint_id.id, - 'picking_type_id': production.picking_type_id.id, - 'company_id': production.company_id.id, - 'move_dest_ids': production.move_dest_ids.ids, - 'user_id': production.user_id.id} - return production_values_str - # 工单排序 def _reset_work_order_sequence1(self, k): for rec in self: @@ -786,10 +765,6 @@ class MrpProduction(models.Model): } } - # 报废 - def button_scrap_new(self): - return True - # 更新程序 def do_update_program(self): program_production = self @@ -879,6 +854,135 @@ class MrpProduction(models.Model): logging.info('get_new_program error:%s' % e) raise UserError("从云平台获取最新程序失败,请联系管理员") + def recreateManufacturing(self): + """ + 重新生成制造订单 + """ + if self.is_scrap is True: + sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)]) + values = self.env['mrp.production'].create_production1_values(self.production_id) + productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company( + self.production_id.company_id).create( + values) + # self.env['stock.move'].sudo().create(productions._get_moves_raw_values()) + self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) + productions._create_workorder() + productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \ + ( + p.move_dest_ids.procure_method != 'make_to_order' and + not p.move_raw_ids and not p.workorder_ids)).action_confirm() + for production_item in productions: + process_parameter_workorder = self.env['mrp.workorder'].search( + [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id), + ('is_subcontract', '=', True)]) + if process_parameter_workorder: + is_pick = False + consecutive_workorders = [] + m = 0 + sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id) + for i in range(len(sorted_workorders) - 1): + if m == 0: + is_pick = False + if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \ + sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \ + sorted_workorders[i].id == sorted_workorders[i + 1].id - 1: + if sorted_workorders[i] not in consecutive_workorders: + consecutive_workorders.append(sorted_workorders[i]) + consecutive_workorders.append(sorted_workorders[i + 1]) + m += 1 + continue + else: + if m == len(consecutive_workorders) - 1 and m != 0: + self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, + production_item) + if sorted_workorders[i] in consecutive_workorders: + is_pick = True + consecutive_workorders = [] + m = 0 + # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单 + if is_pick is False: + self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], + production_item) + if m == len(consecutive_workorders) - 1 and m != 0: + self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, + production_item) + if sorted_workorders[i] in consecutive_workorders: + is_pick = True + consecutive_workorders = [] + m = 0 + if m == len(consecutive_workorders) - 1 and m != 0: + self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item) + if is_pick is False and m == 0: + if len(sorted_workorders) == 1: + self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item) + else: + self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item) + + for production in productions: + origin_production = production.move_dest_ids and production.move_dest_ids[ + 0].raw_material_production_id or False + orderpoint = production.orderpoint_id + if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual': + production.message_post( + body=_('This production order has been created from Replenishment Report.'), + message_type='comment', + subtype_xmlid='mail.mt_note') + elif orderpoint: + production.message_post_with_view( + 'mail.message_origin_link', + values={'self': production, 'origin': orderpoint}, + subtype_id=self.env.ref('mail.mt_note').id) + elif origin_production: + production.message_post_with_view( + 'mail.message_origin_link', + values={'self': production, 'origin': origin_production}, + subtype_id=self.env.ref('mail.mt_note').id) + + ''' + 创建生产计划 + ''' + # 工单耗时 + workorder_duration = 0 + for workorder in productions.workorder_ids: + workorder_duration += workorder.duration_expected + + if sale_order: + sale_order.mrp_production_ids |= productions + # sale_order.write({'schedule_status': 'to schedule'}) + self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({ + 'name': productions.name, + 'order_deadline': sale_order.deadline_of_delivery, + 'production_id': productions.id, + 'date_planned_start': productions.date_planned_start, + 'origin': productions.origin, + 'product_qty': productions.product_qty, + 'product_id': productions.product_id.id, + 'state': 'draft', + }) + + # 在之前的销售单上重新生成制造订单 + def create_production1_values(self, production, sale_order): + production_values_str = {'origin': production.origin, + 'product_id': production.product_id.id, + 'product_description_variants': production.product_description_variants, + 'product_qty': production.product_qty, + 'product_uom_id': production.product_uom_id.id, + 'location_src_id': production.location_src_id.id, + 'location_dest_id': production.location_dest_id.id, + 'bom_id': production.bom_id.id, + 'date_deadline': production.date_deadline, + 'date_planned_start': production.date_planned_start, + 'date_planned_finished': production.date_planned_finished, + 'procurement_group_id': sale_order.id, + 'propagate_cancel': production.propagate_cancel, + 'orderpoint_id': production.orderpoint_id.id, + 'picking_type_id': production.picking_type_id.id, + 'company_id': production.company_id.id, + 'move_dest_ids': production.move_dest_ids.ids, + 'user_id': production.user_id.id} + return production_values_str + + class sf_detection_result(models.Model): _name = 'sf.detection.result' diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 6da74cbc..a6f80fa2 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -684,118 +684,7 @@ class ResMrpWorkOrder(models.Model): # 'target':'new' # } - def recreateManufacturingOrWorkerOrder(self): - """ - 重新生成制造订单或者重新生成工单 - """ - if self.test_results in ['返工', '报废']: - values = self.env['mrp.production'].create_production1_values(self.production_id) - productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company( - self.production_id.company_id).create( - values) - # self.env['stock.move'].sudo().create(productions._get_moves_raw_values()) - self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) - productions._create_workorder() - productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \ - ( - p.move_dest_ids.procure_method != 'make_to_order' and - not p.move_raw_ids and not p.workorder_ids)).action_confirm() - for production_item in productions: - process_parameter_workorder = self.env['mrp.workorder'].search( - [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id), - ('is_subcontract', '=', True)]) - if process_parameter_workorder: - is_pick = False - consecutive_workorders = [] - m = 0 - sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id) - for i in range(len(sorted_workorders) - 1): - if m == 0: - is_pick = False - if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \ - sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \ - sorted_workorders[i].id == sorted_workorders[i + 1].id - 1: - if sorted_workorders[i] not in consecutive_workorders: - consecutive_workorders.append(sorted_workorders[i]) - consecutive_workorders.append(sorted_workorders[i + 1]) - m += 1 - continue - else: - if m == len(consecutive_workorders) - 1 and m != 0: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, - production_item) - if sorted_workorders[i] in consecutive_workorders: - is_pick = True - consecutive_workorders = [] - m = 0 - # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单 - if is_pick is False: - self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], - production_item) - if m == len(consecutive_workorders) - 1 and m != 0: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, - production_item) - if sorted_workorders[i] in consecutive_workorders: - is_pick = True - consecutive_workorders = [] - m = 0 - if m == len(consecutive_workorders) - 1 and m != 0: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item) - if is_pick is False and m == 0: - if len(sorted_workorders) == 1: - self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item) - else: - self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item) - for production in productions: - origin_production = production.move_dest_ids and production.move_dest_ids[ - 0].raw_material_production_id or False - orderpoint = production.orderpoint_id - if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual': - production.message_post( - body=_('This production order has been created from Replenishment Report.'), - message_type='comment', - subtype_xmlid='mail.mt_note') - elif orderpoint: - production.message_post_with_view( - 'mail.message_origin_link', - values={'self': production, 'origin': orderpoint}, - subtype_id=self.env.ref('mail.mt_note').id) - elif origin_production: - production.message_post_with_view( - 'mail.message_origin_link', - values={'self': production, 'origin': origin_production}, - subtype_id=self.env.ref('mail.mt_note').id) - - ''' - 创建生产计划 - ''' - # 工单耗时 - workorder_duration = 0 - for workorder in productions.workorder_ids: - workorder_duration += workorder.duration_expected - - sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)]) - if sale_order: - sale_order.mrp_production_ids |= productions - # sale_order.write({'schedule_status': 'to schedule'}) - self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({ - 'name': productions.name, - 'order_deadline': sale_order.deadline_of_delivery, - 'production_id': productions.id, - 'date_planned_start': productions.date_planned_start, - 'origin': productions.origin, - 'product_qty': productions.product_qty, - 'product_id': productions.product_id.id, - 'state': 'draft', - }) - # if self.test_results == '返工': - # productions = self.production_id - # # self.env['stock.move'].sudo().create(productions._get_moves_raw_values()) - # # self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) - # productions._create_workorder2(self.processing_panel) - # else: - # self.results = '合格' def json_workorder_str1(self, k, production, route): workorders_values_str = [0, '', { @@ -997,7 +886,7 @@ class ResMrpWorkOrder(models.Model): record.process_state = '待解除装夹' # record.write({'process_state': '待加工'}) record.production_id.process_state = '待解除装夹' - if record.test_results in ['返工']: + if record.test_results in ['返工', '报废']: record.production_id.write({'detection_result_ids': [(0, 0, { 'rework_reason': record.reason, 'detailed_reason': record.detailed_reason, @@ -1005,7 +894,8 @@ class ResMrpWorkOrder(models.Model): 'routing_type': record.routing_type, 'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '', 'test_results': record.test_results, - 'test_report': record.detection_report})]}) + 'test_report': record.detection_report})], + 'is_scrap': True if record.test_results == '报废' else False}) if record.routing_type == '解除装夹': ''' 记录结束时间 @@ -1064,14 +954,16 @@ class ResMrpWorkOrder(models.Model): for workorder in record.production_id.workorder_ids: if workorder.state != 'done': is_production_id = False - if record.routing_type == '解除装夹': + if record.routing_type in ['解除装夹'] or ( + record.is_rework is True and record.routing_type in ['装夹预调']) or ( + record.test_results in ['返工', '报废'] and record.routing_type in ['CNC加工']): for workorder in record.production_id.workorder_ids: if workorder.processing_panel == record.processing_panel: rfid_code = workorder.rfid_code workorder.write({'rfid_code_old': rfid_code, 'rfid_code': False}) - workorder.rfid_code_old = rfid_code - workorder.rfid_code = False + # workorder.rfid_code_old = rfid_code + # workorder.rfid_code = False if is_production_id is True and record.routing_type in ['解除装夹', '表面工艺']: logging.info('product_qty:%s' % record.production_id.product_qty) for move_raw_id in record.production_id.move_raw_ids: diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py index 4d05de81..f292d694 100644 --- a/sf_manufacturing/models/product_template.py +++ b/sf_manufacturing/models/product_template.py @@ -7,8 +7,8 @@ import os from odoo import models, fields, api, _ from odoo.exceptions import ValidationError 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/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index 5cdb72b7..b3a7173a 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -144,5 +144,7 @@ access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_m access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0 access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0 access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_order_user,model_sf_processing_panel,sf_base.group_sf_order_user,1,1,1,0 +access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0 + diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index 9ee96732..9d177542 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -88,6 +88,7 @@ decoration-danger="programming_state =='已编程未下发'"/> + @@ -123,8 +124,9 @@ attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>