From ef60f36c90d16263766203a536c43c9e25f678c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Wed, 4 Dec 2024 20:42:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B7=A5=E8=89=BA=E5=A4=96?= =?UTF-8?q?=E5=8D=8F=E5=B7=A5=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_production.py | 26 ++-- sf_manufacturing/models/mrp_workorder.py | 168 +++++++++++++--------- sf_manufacturing/models/stock.py | 88 ++++++------ sf_sale/views/quick_easy_order_view.xml | 4 +- 4 files changed, 163 insertions(+), 123 deletions(-) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 9156dc38..d1349671 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -721,6 +721,7 @@ class MrpProduction(models.Model): for product_id, pd in grouped_product_ids.items(): product_id_to_production_names[product_id] = [p.name for p in pd] for production in production_all: + proc_workorders = [] process_parameter_workorder = self.env['mrp.workorder'].search( [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), ('is_subcontract', '=', True)], order='sequence asc') @@ -737,10 +738,11 @@ class MrpProduction(models.Model): else: # 处理连续组,如果它不为空 if consecutive_workorders: + proc_workorders.append(consecutive_workorders) # 创建外协出入库单和采购订单 - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production) - self.env['purchase.order'].get_purchase_order(consecutive_workorders, production, - product_id_to_production_names) + # self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production, sorted_workorders) + # self.env['purchase.order'].get_purchase_order(consecutive_workorders, production, + # product_id_to_production_names) if i < len(sorted_workorders) - 1: # 重置连续组,并添加当前工作订单 consecutive_workorders = [workorder] @@ -751,18 +753,22 @@ class MrpProduction(models.Model): i - 1].supplier_id.id: consecutive_workorders = [workorder] else: + proc_workorders.append([workorder]) # 立即创建外协出入库单和采购订单 - self.env['stock.picking'].create_outcontract_picking(workorder, production) - self.env['purchase.order'].get_purchase_order(workorder, production, - product_id_to_production_names) + # self.env['stock.picking'].create_outcontract_picking(workorder, production) + # self.env['purchase.order'].get_purchase_order(workorder, production, + # product_id_to_production_names) consecutive_workorders = [] # 处理最后一个组,即使它可能只有一个工作订单 if consecutive_workorders: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production) - self.env['purchase.order'].get_purchase_order(consecutive_workorders, production, - product_id_to_production_names) - + proc_workorders.append(consecutive_workorders) + # self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production) + # self.env['purchase.order'].get_purchase_order(consecutive_workorders, production, + # product_id_to_production_names) + for workorders in reversed(proc_workorders): + self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders) + self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names) # 工单排序 def _reset_work_order_sequence1(self, k): for rec in self: diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 7bd9d1c8..ee5a034b 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -313,14 +313,7 @@ class ResMrpWorkOrder(models.Model): # if technology_design.is_auto is False: # domain = [('origin', '=', self.production_id.name)] # else: - domain = [('origin', '=', self.production_id.name), ('purchase_type', '=', 'consignment')] - purchase_orders = self.env['purchase.order'].search(domain) - purchase_orders_id = None - for po in purchase_orders: - for line in po.order_line: - if line.product_id.server_product_process_parameters_id == self.surface_technics_parameters_id: - if line.product_qty == 1: - purchase_orders_id = line.order_id.id + purchase_orders_id = self._get_surface_technics_purchase_ids() result = { "type": "ir.actions.act_window", "res_model": "purchase.order", @@ -330,6 +323,17 @@ class ResMrpWorkOrder(models.Model): 'view_mode': 'form', } return result + + def _get_surface_technics_purchase_ids(self): + domain = [('origin', '=', self.production_id.name), ('purchase_type', '=', 'consignment')] + purchase_orders = self.env['purchase.order'].search(domain) + purchase_orders_id = None + for po in purchase_orders: + for line in po.order_line: + if line.product_id.server_product_process_parameters_id == self.surface_technics_parameters_id: + if line.product_qty == 1: + purchase_orders_id = line.order_id.id + return purchase_orders_id supplier_id = fields.Many2one('res.partner', string='外协供应商') equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True) @@ -1027,47 +1031,47 @@ class ResMrpWorkOrder(models.Model): 'production_id.programming_state') def _compute_state(self): # super()._compute_state() - for workorder in self: - if workorder.sequence != 1: - previous_workorder = self.env['mrp.workorder'].search( - [('production_id', '=', workorder.production_id.id), - ('sequence', '=', workorder.sequence - 1)]) - if workorder.state == 'pending': - if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): - if workorder.production_id.reservation_state == 'assigned' and workorder.production_id.schedule_state == '已排': - if ((workorder.sequence == 1 and not workorder.blocked_by_workorder_ids) - or (workorder.blocked_by_workorder_ids.state in ('done', 'cancel') - and workorder.blocked_by_workorder_ids.test_results not in ['报废', '返工']) - or (previous_workorder.state in ('done', 'cancel') - and not workorder.blocked_by_workorder_ids - and previous_workorder.test_results not in ['报废', '返工']) - ): - workorder.state = 'ready' - continue - if workorder.production_id.schedule_state == '未排' and workorder.state in ('waiting', 'ready'): - if workorder.sequence != 1: - workorder.state = 'pending' - continue - if workorder.state not in ('waiting', 'ready'): - continue - if workorder.state in ( - 'waiting') and workorder.sequence == 1 and workorder.production_id.schedule_state == '已排': - workorder.state = 'ready' - continue - if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): - workorder.state = 'pending' - if workorder.state in ['waiting']: - if previous_workorder.state == 'waiting': - workorder.state = 'pending' - if workorder.sequence == 1 and workorder.state == 'pending': - workorder.state = 'waiting' - continue - if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'): - continue - if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting' and workorder.production_id.schedule_state == '已排': - workorder.state = 'ready' - elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready': - workorder.state = 'waiting' + # for workorder in self: + # if workorder.sequence != 1: + # previous_workorder = self.env['mrp.workorder'].search( + # [('production_id', '=', workorder.production_id.id), + # ('sequence', '=', workorder.sequence - 1)]) + # if workorder.state == 'pending': + # if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): + # if workorder.production_id.reservation_state == 'assigned' and workorder.production_id.schedule_state == '已排': + # if ((workorder.sequence == 1 and not workorder.blocked_by_workorder_ids) + # or (workorder.blocked_by_workorder_ids.state in ('done', 'cancel') + # and workorder.blocked_by_workorder_ids.test_results not in ['报废', '返工']) + # or (previous_workorder.state in ('done', 'cancel') + # and not workorder.blocked_by_workorder_ids + # and previous_workorder.test_results not in ['报废', '返工']) + # ): + # workorder.state = 'ready' + # continue + # if workorder.production_id.schedule_state == '未排' and workorder.state in ('waiting', 'ready'): + # if workorder.sequence != 1: + # workorder.state = 'pending' + # continue + # if workorder.state not in ('waiting', 'ready'): + # continue + # if workorder.state in ( + # 'waiting') and workorder.sequence == 1 and workorder.production_id.schedule_state == '已排': + # workorder.state = 'ready' + # continue + # if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): + # workorder.state = 'pending' + # if workorder.state in ['waiting']: + # if previous_workorder.state == 'waiting': + # workorder.state = 'pending' + # if workorder.sequence == 1 and workorder.state == 'pending': + # workorder.state = 'waiting' + # continue + # if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'): + # continue + # if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting' and workorder.production_id.schedule_state == '已排': + # workorder.state = 'ready' + # elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready': + # workorder.state = 'waiting' for workorder in self: # 如果工单的工序没有进行排序则跳出循环 @@ -1118,21 +1122,27 @@ class ResMrpWorkOrder(models.Model): if workorder.is_subcontract is False: workorder.state = 'ready' else: - production_programming = self.env['mrp.production'].search( - [('origin', '=', self.production_id.origin)], order='name asc') - production_no_remanufacture = production_programming.filtered( - lambda a: a.is_remanufacture is False) - production_list = [production.name for production in production_programming] - purchase_orders = self.env['purchase.order'].search( - [('origin', 'ilike', ','.join(production_list))]) - for line in purchase_orders.order_line: - if ( - line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id - and line.product_qty == len(production_no_remanufacture)): - if all(pur_order.state == 'purchase' for pur_order in purchase_orders): - workorder.state = 'ready' - else: - workorder.state = 'waiting' + # production_programming = self.env['mrp.production'].search( + # [('origin', '=', self.production_id.origin)], order='name asc') + # production_no_remanufacture = production_programming.filtered( + # lambda a: a.is_remanufacture is False) + # production_list = [production.name for production in production_programming] + # purchase_orders = self.env['purchase.order'].search( + # [('origin', 'ilike', ','.join(production_list))]) + # for line in purchase_orders.order_line: + # if ( + # line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id + # and line.product_qty == len(production_no_remanufacture)): + # if all(pur_order.state == 'purchase' for pur_order in purchase_orders): + # workorder.state = 'ready' + # else: + # workorder.state = 'waiting' + purchase_orders_id = self._get_surface_technics_purchase_ids() + if purchase_orders_id: + purchase_order = self.env['purchase.order'].browse(purchase_orders_id) + workorder.state = 'ready' if purchase_order.state == 'purchase' else 'waiting' + else: + workorder.state = 'waiting' # re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id), # ('processing_panel', '=', workorder.processing_panel), @@ -1241,12 +1251,13 @@ class ResMrpWorkOrder(models.Model): # 表面工艺外协出库单 if self.routing_type == '表面工艺': if self.is_subcontract is True: - move_out = self.env['stock.move'].search( - [('location_id', '=', self.env['stock.location'].search( - [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id), - ('location_dest_id', '=', self.env['stock.location'].search( - [('barcode', 'ilike', 'VL-SPOC')]).id), - ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])]) + move_out = self.move_subcontract_workorder_ids[1] + # move_out = self.env['stock.move'].search( + # [('location_id', '=', self.env['stock.location'].search( + # [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id), + # ('location_dest_id', '=', self.env['stock.location'].search( + # [('barcode', 'ilike', 'VL-SPOC')]).id), + # ('origin', '=', self.production_id.name), ('state', 'not in', ['cancel', 'done'])]) for mo in move_out: pick = self.env['stock.picking'].search([('id', '=', mo.picking_id.id), ('name', 'ilike', 'OCOUT'), ('partner_id', '=', self.supplier_id.id)]) @@ -1254,6 +1265,21 @@ class ResMrpWorkOrder(models.Model): if mo.state != 'done': mo.write({'state': 'assigned', 'production_id': False}) self.env['stock.move.line'].create(mo.get_move_line(self.production_id, self)) + product_qty = mo.product_uom._compute_quantity( + mo.product_uom_qty, mo.product_id.uom_id, rounding_method='HALF-UP') + available_quantity = self.env['stock.quant']._get_available_quantity( + mo.product_id, + mo.location_id, + lot_id=mo.move_line_ids.lot_id, + strict=False, + ) + mo._update_reserved_quantity( + product_qty, + available_quantity, + mo.location_id, + lot_id=mo.move_line_ids.lot_id, + strict=False, + ) # move_out._action_assign() if self.state == 'waiting' or self.state == 'ready' or self.state == 'progress': @@ -1526,6 +1552,8 @@ class ResMrpWorkOrder(models.Model): 'default_confirm_button': '确认解除', # 'default_feeder_station_start_id': feeder_station_start_id, }} + + move_subcontract_workorder_ids = fields.One2many('stock.move', 'subcontract_workorder_id', string='组件') class CNCprocessing(models.Model): diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 50c7e672..cfa35887 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -634,71 +634,74 @@ class StockPicking(models.Model): num = "%04d" % m return '%s%s' % (rescode, num) - def button_validate(self): - res = super().button_validate() - if res is True and self.picking_type_id.sequence_code == 'OCOUT': - # if self.id == move_out.picking_id.id: - # if move_out.move_line_ids.workorder_id.state == 'progress': - move_in = self.env['stock.move'].search( - [('location_dest_id', '=', self.env['stock.location'].search( - [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id), - ('location_id', '=', self.env['stock.location'].search( - [('barcode', 'ilike', 'VL-SPOC')]).id), - ('origin', '=', self.origin), ('state', 'not in', ['cancel', 'done'])]) - production = self.env['mrp.production'].search([('name', '=', self.origin)]) - for mi in move_in: - pick = self.env['stock.picking'].search([('id', '=', mi.picking_id.id), ('name', 'ilike', 'OCIN'), - ('partner_id', '=', self.partner_id.id)]) - # if pick: - # if mi.state != 'done': - # mi.write({'state': 'assigned'}) - # self.env['stock.move.line'].create(mi.get_move_line(production, None)) + # def button_validate(self): + # res = super().button_validate() + # if res is True and self.picking_type_id.sequence_code == 'OCOUT': + # # if self.id == move_out.picking_id.id: + # # if move_out.move_line_ids.workorder_id.state == 'progress': + # move_in = self.env['stock.move'].search( + # [('location_dest_id', '=', self.env['stock.location'].search( + # [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id), + # ('location_id', '=', self.env['stock.location'].search( + # [('barcode', 'ilike', 'VL-SPOC')]).id), + # ('origin', '=', self.origin), ('state', 'not in', ['cancel', 'done'])]) + # production = self.env['mrp.production'].search([('name', '=', self.origin)]) + # for mi in move_in: + # pick = self.env['stock.picking'].search([('id', '=', mi.picking_id.id), ('name', 'ilike', 'OCIN'), + # ('partner_id', '=', self.partner_id.id)]) + # if pick: + # if mi.state != 'done': + # mi.write({'state': 'assigned'}) + # self.env['stock.move.line'].create(mi.get_move_line(production, None)) - return res + # return res # 创建 外协出库入单 - def create_outcontract_picking(self, sorted_workorders_arr, item): + def create_outcontract_picking(self, workorders, item, sorted_workorders): domain = [('origin', '=', item.name), ('name', 'ilike', 'OCOUT')] - if len(sorted_workorders_arr) > 1: - sorted_workorders_arr = sorted_workorders_arr[0] + if len(workorders) > 1: + workorders = workorders[0] else: domain += [ - ('surface_technics_parameters_id', '=', sorted_workorders_arr[0].surface_technics_parameters_id.id)] + ('surface_technics_parameters_id', '=', workorders[0].surface_technics_parameters_id.id)] stock_picking = self.env['stock.picking'].search(domain) if not stock_picking: - for sorted_workorders in sorted_workorders_arr: + for workorder in workorders: # pick_ids = [] - if not sorted_workorders.picking_ids: + if not workorder.picking_ids: # outcontract_stock_move = self.env['stock.move'].search([('production_id', '=', item.id)]) # if not outcontract_stock_move: # 创建一个新的补货组 procurement_group_id = self.env['procurement.group'].create({ - 'name': sorted_workorders.name, + 'name': workorder.name, 'partner_id': self.partner_id.id, }) + move_dest_id = False + # 如果当前工单是制造订单的最后一个工单 + # if workorder == item.workorder_ids[-1]: + # # 找到制造订单对应的move + # move_dest_id = item.move_raw_ids[0].move_orig_ids[0].id + # else: + # # 从sorted_workorders中找到上一工单的move + # move_dest_id = sorted_workorders[sorted_workorders.index(workorder) - 1].move_subcontract_workorder_ids[1].id new_picking = True - location_id = self.env['stock.location'].search( - [('barcode', 'ilike', 'VL-SPOC')]).id, - location_dest_id = self.env['stock.location'].search( - [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id, outcontract_picking_type_in = self.env.ref( 'sf_manufacturing.outcontract_picking_in').id, outcontract_picking_type_out = self.env.ref( 'sf_manufacturing.outcontract_picking_out').id, moves_in = self.env['stock.move'].sudo().create( - self.env['stock.move']._get_stock_move_values_Res(item, location_id, location_dest_id, - outcontract_picking_type_in, procurement_group_id.id)) + self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_in, procurement_group_id.id, move_dest_id)) picking_in = self.create( - moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/')) + moves_in._get_new_picking_values_Res(item, workorder, 'WH/OCIN/')) # pick_ids.append(picking_in.id) moves_in.write( {'picking_id': picking_in.id, 'state': 'waiting'}) moves_in._assign_picking_post_process(new=new_picking) moves_out = self.env['stock.move'].sudo().create( - self.env['stock.move']._get_stock_move_values_Res(item, location_dest_id, location_id, - outcontract_picking_type_out, procurement_group_id.id, moves_in.id)) + self.env['stock.move']._get_stock_move_values_Res(item, outcontract_picking_type_out, procurement_group_id.id, moves_in.id)) + workorder.write({'move_subcontract_workorder_ids': [(6, 0, [moves_in.id, moves_out.id])]}) picking_out = self.create( - moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/')) + moves_out._get_new_picking_values_Res(item, workorder, 'WH/OCOUT/')) # pick_ids.append(picking_out.id) moves_out.write( {'picking_id': picking_out.id, 'state': 'waiting'}) @@ -713,16 +716,17 @@ class ReStockMove(models.Model): materiel_width = fields.Float(string='物料宽度', digits=(16, 4)) materiel_height = fields.Float(string='物料高度', digits=(16, 4)) - def _get_stock_move_values_Res(self, item, location_src_id, location_dest_id, picking_type_id, group_id, move_dest_ids=False): - route = self.env['stock.route'].sudo().search([('name', '=', '表面工艺外协')]) + def _get_stock_move_values_Res(self, item, picking_type_id, group_id, move_dest_ids=False): + route_id = self.env.ref('sf_manufacturing.route_surface_technology_outsourcing').id + stock_rule = self.env['stock.rule'].sudo().search([('route_id', '=', route_id), ('picking_type_id', '=', picking_type_id)]) move_values = { 'name': '推', 'company_id': item.company_id.id, 'product_id': item.bom_id.bom_line_ids.product_id.id, 'product_uom': item.bom_id.bom_line_ids.product_uom_id.id, 'product_uom_qty': 1.0, - 'location_id': location_src_id, - 'location_dest_id': location_dest_id, + 'location_id': stock_rule.location_src_id.id, + 'location_dest_id': stock_rule.location_dest_id.id, 'origin': item.name, 'group_id': group_id, 'move_dest_ids': [(6, 0, [move_dest_ids])] if move_dest_ids else False, @@ -991,6 +995,8 @@ class ReStockMove(models.Model): res['origin'] = ','.join(productions.mapped('name')) res['retrospect_ref'] = production.product_id.name return res + + subcontract_workorder_id = fields.Many2one('mrp.workorder', '外协工单组件', check_company=True, index='btree_not_null') class ReStockQuant(models.Model): diff --git a/sf_sale/views/quick_easy_order_view.xml b/sf_sale/views/quick_easy_order_view.xml index 3b386062..e0fa6dbd 100644 --- a/sf_sale/views/quick_easy_order_view.xml +++ b/sf_sale/views/quick_easy_order_view.xml @@ -128,9 +128,9 @@ - + groups="sales_team.group_sale_salesman,sf_base.group_sale_salemanager,sf_base.group_sale_director"/> --> \ No newline at end of file