From 820fbd99738dd55f7bb1cd79fbe1af496d820e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Voet=20=28ryv=29?= Date: Wed, 4 Aug 2021 09:37:43 +0000 Subject: [PATCH] [ADD] mrp_subcontracting_purchase: new module Add a new module to add smart buttons between a subcontracting purchase order and the ressuply component delivery. task-2486811 Part-of: odoo/odoo#75041 --- .../mrp_subcontracting_purchase/__init__.py | 4 ++ .../__manifest__.py | 19 +++++++++ .../models/__init__.py | 5 +++ .../models/purchase_order.py | 25 ++++++++++++ .../models/stock_picking.py | 40 +++++++++++++++++++ .../tests/__init__.py | 3 ++ .../tests/test_mrp_subcontracting_purchase.py | 35 ++++++++++++++++ .../views/purchase_order_views.xml | 20 ++++++++++ .../views/stock_picking_views.xml | 20 ++++++++++ addons/purchase_stock/models/purchase.py | 18 ++++----- 10 files changed, 180 insertions(+), 9 deletions(-) create mode 100644 addons/mrp_subcontracting_purchase/__init__.py create mode 100644 addons/mrp_subcontracting_purchase/__manifest__.py create mode 100644 addons/mrp_subcontracting_purchase/models/__init__.py create mode 100644 addons/mrp_subcontracting_purchase/models/purchase_order.py create mode 100644 addons/mrp_subcontracting_purchase/models/stock_picking.py create mode 100644 addons/mrp_subcontracting_purchase/tests/__init__.py create mode 100644 addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py create mode 100644 addons/mrp_subcontracting_purchase/views/purchase_order_views.xml create mode 100644 addons/mrp_subcontracting_purchase/views/stock_picking_views.xml diff --git a/addons/mrp_subcontracting_purchase/__init__.py b/addons/mrp_subcontracting_purchase/__init__.py new file mode 100644 index 00000000000..dc5e6b693d1 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import models diff --git a/addons/mrp_subcontracting_purchase/__manifest__.py b/addons/mrp_subcontracting_purchase/__manifest__.py new file mode 100644 index 00000000000..2e4ac2e70bc --- /dev/null +++ b/addons/mrp_subcontracting_purchase/__manifest__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Purchase and Subcontracting Management', + 'version': '0.1', + 'category': 'Manufacturing/Purchase', + 'description': """ + This bridge module adds some smart buttons between Purchase and Subcontracting + """, + 'depends': ['mrp_subcontracting', 'purchase'], + 'data': [ + 'views/purchase_order_views.xml', + 'views/stock_picking_views.xml', + ], + 'installable': True, + 'auto_install': True, + 'license': 'LGPL-3', +} diff --git a/addons/mrp_subcontracting_purchase/models/__init__.py b/addons/mrp_subcontracting_purchase/models/__init__.py new file mode 100644 index 00000000000..eee25d6fe21 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import stock_picking +from . import purchase_order diff --git a/addons/mrp_subcontracting_purchase/models/purchase_order.py b/addons/mrp_subcontracting_purchase/models/purchase_order.py new file mode 100644 index 00000000000..7283167c3c1 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/models/purchase_order.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, fields, api + + +class PurchaseOrder(models.Model): + _inherit = 'purchase.order' + + subcontracting_resupply_picking_count = fields.Integer( + "Count of Subcontracting Resupply", compute='_compute_subcontracting_resupply_picking_count', + help="Count of Subcontracting Resupply for component") + + @api.depends('order_line.move_ids') + def _compute_subcontracting_resupply_picking_count(self): + for purchase in self: + purchase.subcontracting_resupply_picking_count = len(purchase._get_subcontracting_resupplies()) + + def action_view_subcontracting_resupply(self): + return self._get_action_view_picking(self._get_subcontracting_resupplies()) + + def _get_subcontracting_resupplies(self): + moves_subcontracted = self.order_line.move_ids.filtered(lambda m: m.is_subcontract) + subcontracted_productions = moves_subcontracted.move_orig_ids.production_id + return subcontracted_productions.picking_ids diff --git a/addons/mrp_subcontracting_purchase/models/stock_picking.py b/addons/mrp_subcontracting_purchase/models/stock_picking.py new file mode 100644 index 00000000000..e168034ecbf --- /dev/null +++ b/addons/mrp_subcontracting_purchase/models/stock_picking.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, fields, api, _ + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + subcontracting_source_purchase_count = fields.Integer( + "Number of subcontracting PO Source", compute='_compute_subcontracting_source_purchase_count', + help="Number of subcontracting Purchase Order Source") + + @api.depends('move_lines.move_dest_ids.raw_material_production_id') + def _compute_subcontracting_source_purchase_count(self): + for picking in self: + picking.subcontracting_source_purchase_count = len(picking._get_subcontracting_source_purchase()) + + def action_view_subcontracting_source_purchase(self): + purchase_order_ids = self._get_subcontracting_source_purchase().ids + action = { + 'res_model': 'purchase.order', + 'type': 'ir.actions.act_window', + } + if len(purchase_order_ids) == 1: + action.update({ + 'view_mode': 'form', + 'res_id': purchase_order_ids[0], + }) + else: + action.update({ + 'name': _("Source PO of %s", self.name), + 'domain': [('id', 'in', purchase_order_ids)], + 'view_mode': 'tree,form', + }) + return action + + def _get_subcontracting_source_purchase(self): + moves_subcontracted = self.move_lines.move_dest_ids.raw_material_production_id.move_finished_ids.move_dest_ids.filtered(lambda m: m.is_subcontract) + return moves_subcontracted.purchase_line_id.order_id diff --git a/addons/mrp_subcontracting_purchase/tests/__init__.py b/addons/mrp_subcontracting_purchase/tests/__init__.py new file mode 100644 index 00000000000..5fb08a3fea4 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_mrp_subcontracting_purchase diff --git a/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py b/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py new file mode 100644 index 00000000000..cfd4f253bc8 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/tests/test_mrp_subcontracting_purchase.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import Command + +from odoo.addons.mrp_subcontracting.tests.common import TestMrpSubcontractingCommon + + +class MrpSubcontractingPurchaseTest(TestMrpSubcontractingCommon): + + def test_count_smart_buttons(self): + resupply_sub_on_order_route = self.env['stock.location.route'].search([('name', '=', 'Resupply Subcontractor on Order')]) + (self.comp1 + self.comp2).write({'route_ids': [Command.link(resupply_sub_on_order_route.id)]}) + + # I create a draft Purchase Order for first in move for 10 kg at 50 euro + po = self.env['purchase.order'].create({ + 'partner_id': self.subcontractor_partner1.id, + 'order_line': [Command.create({ + 'name': 'finished', + 'product_id': self.finished.id, + 'product_qty': 1.0, + 'product_uom': self.finished.uom_id.id, + 'price_unit': 50.0} + )], + }) + + po.button_confirm() + + self.assertEqual(po.subcontracting_resupply_picking_count, 1) + action1 = po.action_view_subcontracting_resupply() + picking = self.env[action1['res_model']].browse(action1['res_id']) + self.assertEqual(picking.subcontracting_source_purchase_count, 1) + action2 = picking.action_view_subcontracting_source_purchase() + po_action2 = self.env[action2['res_model']].browse(action2['res_id']) + self.assertEqual(po_action2, po) diff --git a/addons/mrp_subcontracting_purchase/views/purchase_order_views.xml b/addons/mrp_subcontracting_purchase/views/purchase_order_views.xml new file mode 100644 index 00000000000..1c783b25438 --- /dev/null +++ b/addons/mrp_subcontracting_purchase/views/purchase_order_views.xml @@ -0,0 +1,20 @@ + + + + purchase.order.inherited.form.mrp.subcontracting.purchase + purchase.order + + + + + + + + diff --git a/addons/mrp_subcontracting_purchase/views/stock_picking_views.xml b/addons/mrp_subcontracting_purchase/views/stock_picking_views.xml new file mode 100644 index 00000000000..6b78a88238d --- /dev/null +++ b/addons/mrp_subcontracting_purchase/views/stock_picking_views.xml @@ -0,0 +1,20 @@ + + + + stock.picking.inherited.form.mrp.subcontracting + stock.picking + + + + + + + + diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index 511be890f3d..9c55bea574e 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -118,23 +118,23 @@ class PurchaseOrder(models.Model): return super(PurchaseOrder, self).button_cancel() def action_view_picking(self): + return self._get_action_view_picking(self.picking_ids) + + def _get_action_view_picking(self, pickings): """ This function returns an action that display existing picking orders of given purchase order ids. When only one found, show the picking immediately. """ + self.ensure_one() result = self.env["ir.actions.actions"]._for_xml_id('stock.action_picking_tree_all') # override the context to get rid of the default filtering on operation type result['context'] = {'default_partner_id': self.partner_id.id, 'default_origin': self.name, 'default_picking_type_id': self.picking_type_id.id} - pick_ids = self.mapped('picking_ids') # choose the view_mode accordingly - if not pick_ids or len(pick_ids) > 1: - result['domain'] = "[('id','in',%s)]" % (pick_ids.ids) - elif len(pick_ids) == 1: + if not pickings or len(pickings) > 1: + result['domain'] = [('id', 'in', pickings.ids)] + elif len(pickings) == 1: res = self.env.ref('stock.view_picking_form', False) form_view = [(res and res.id or False, 'form')] - if 'views' in result: - result['views'] = form_view + [(state,view) for state,view in result['views'] if view != 'form'] - else: - result['views'] = form_view - result['res_id'] = pick_ids.id + result['views'] = form_view + [(state, view) for state, view in result.get('views', []) if view != 'form'] + result['res_id'] = pickings.id return result def _prepare_invoice(self):