diff --git a/stock_request/README.rst b/stock_request/README.rst index 34182e2ed..518182be6 100644 --- a/stock_request/README.rst +++ b/stock_request/README.rst @@ -72,6 +72,7 @@ Contributors * Jordi Ballester (EFICENT) . * Enric Tobella +* Atte Isopuro Maintainer ---------- diff --git a/stock_request/__manifest__.py b/stock_request/__manifest__.py index e1461e73d..abdd8def7 100644 --- a/stock_request/__manifest__.py +++ b/stock_request/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Stock Request", "summary": "Internal request for stock", - "version": "11.0.2.1.0", + "version": "11.0.2.2.0", "license": "LGPL-3", "website": "https://github.com/stock-logistics-warehouse", "author": "Eficent, " @@ -16,6 +16,7 @@ "data": [ "security/stock_request_security.xml", "security/ir.model.access.csv", + "views/product.xml", "views/stock_request_views.xml", "views/stock_request_allocation_views.xml", "views/stock_move_views.xml", diff --git a/stock_request/models/stock_request_order.py b/stock_request/models/stock_request_order.py index 771cccfd8..a41239404 100644 --- a/stock_request/models/stock_request_order.py +++ b/stock_request/models/stock_request_order.py @@ -2,7 +2,7 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). from odoo import api, fields, models, _ -from odoo.exceptions import UserError, ValidationError +from odoo.exceptions import UserError, ValidationError, AccessError REQUEST_STATES = [ ('draft', 'Draft'), @@ -271,3 +271,41 @@ class StockRequestOrder(models.Model): raise ValidationError( _('The company of the stock request must match with ' 'that of the location.')) + + @api.model + def _create_from_product_multiselect(self, products): + if not products: + return False + if products._name not in ('product.product', 'product.template'): + raise ValidationError( + "This action only works in the context of products") + if products._name == 'product.template': + # search instead of mapped so we don't include archived variants + products = self.env['product.product'].search([ + ('product_tmpl_id', 'in', products.ids) + ]) + expected = self.default_get(['expected_date'])['expected_date'] + try: + order = self.env['stock.request.order'].create(dict( + expected_date=expected, + stock_request_ids=[(0, 0, dict( + product_id=product.id, + product_uom_id=product.uom_id.id, + product_uom_qty=0.0, + expected_date=expected, + )) for product in products] + )) + except AccessError: + # TODO: if there is a nice way to hide the action from the + # Action-menu if the user doesn't have the necessary rights, + # that would be a better way of doing this + raise UserError(_( + "Unfortunately it seems you do not have the necessary rights " + "for creating stock requests. Please contact your " + "administrator.")) + action = self.env.ref('stock_request.stock_request_order_action' + ).read()[0] + action['views'] = [( + self.env.ref('stock_request.stock_request_order_form').id, 'form')] + action['res_id'] = order.id + return action diff --git a/stock_request/tests/test_stock_request.py b/stock_request/tests/test_stock_request.py index b2999079e..2664590a5 100644 --- a/stock_request/tests/test_stock_request.py +++ b/stock_request/tests/test_stock_request.py @@ -3,6 +3,7 @@ from odoo.tests import common from odoo import fields, exceptions +from collections import Counter class TestStockRequest(common.TransactionCase): @@ -109,14 +110,15 @@ class TestStockRequest(common.TransactionCase): 'company_ids': [(6, 0, company_ids)] }) - def _create_product(self, default_code, name, company_id): - return self.env['product.product'].create({ - 'name': name, - 'default_code': default_code, - 'uom_id': self.env.ref('product.product_uom_unit').id, - 'company_id': company_id, - 'type': 'product', - }) + def _create_product(self, default_code, name, company_id, **vals): + return self.env['product.product'].create(dict( + name=name, + default_code=default_code, + uom_id=self.env.ref('product.product_uom_unit').id, + company_id=company_id, + type='product', + **vals + )) def test_defaults(self): vals = { @@ -735,3 +737,84 @@ class TestStockRequest(common.TransactionCase): # Cannot assign a route that belongs to another company with self.assertRaises(exceptions.ValidationError): stock_request.route_id = self.route_2 + + def test_stock_request_order_from_products(self): + product_A1 = self._create_product('CODEA1', 'Product A1', + self.main_company.id) + template_A = product_A1.product_tmpl_id + product_A2 = self._create_product( + 'CODEA2', 'Product A2', self.main_company.id, + product_tmpl_id=template_A.id) + product_A3 = self._create_product( + 'CODEA3', 'Product A3', self.main_company.id, + product_tmpl_id=template_A.id) + product_B1 = self._create_product('CODEB1', 'Product B1', + self.main_company.id) + template_B = product_B1.product_tmpl_id + # One archived variant of B + self._create_product( + 'CODEB2', 'Product B2', self.main_company.id, + product_tmpl_id=template_B.id, active=False) + Order = self.env['stock.request.order'] + + # Selecting some variants and creating an order + preexisting = Order.search([]) + wanted_products = product_A1 + product_A2 + product_B1 + action = Order._create_from_product_multiselect(wanted_products) + new_order = Order.search([]) - preexisting + self.assertEqual(len(new_order), 1) + self.assertEqual(action['res_id'], new_order.id, + msg="Returned action references the wrong record") + self.assertEqual( + Counter(wanted_products), + Counter(new_order.stock_request_ids.mapped('product_id')), + msg="Not all wanted products were ordered" + ) + + # Selecting a template and creating an order + preexisting = Order.search([]) + action = Order._create_from_product_multiselect(template_A) + new_order = Order.search([]) - preexisting + self.assertEqual(len(new_order), 1) + self.assertEqual(action['res_id'], new_order.id, + msg="Returned action references the wrong record") + self.assertEqual( + Counter(product_A1 + product_A2 + product_A3), + Counter(new_order.stock_request_ids.mapped('product_id')), + msg="Not all of the template's variants were ordered" + ) + + # Selecting a template + preexisting = Order.search([]) + action = Order._create_from_product_multiselect( + template_A + template_B) + new_order = Order.search([]) - preexisting + self.assertEqual(len(new_order), 1) + self.assertEqual(action['res_id'], new_order.id, + msg="Returned action references the wrong record") + self.assertEqual( + Counter(product_A1 + product_A2 + product_A3 + product_B1), + Counter(new_order.stock_request_ids.mapped('product_id')), + msg="Inactive variant was ordered though it shouldn't have been" + ) + + # If a user does not have stock request rights, they can still trigger + # the action from the products, so test that they get a friendlier + # error message. + self.stock_request_user.groups_id -= self.stock_request_user_group + with self.assertRaisesRegexp( + exceptions.UserError, + "Unfortunately it seems you do not have the necessary rights " + "for creating stock requests. Please contact your " + "administrator."): + Order.sudo( + self.stock_request_user + )._create_from_product_multiselect(template_A + template_B) + + # Empty recordsets should just return False + self.assertFalse(Order._create_from_product_multiselect( + self.env['product.product'])) + + # Wrong model should just raise ValidationError + with self.assertRaises(exceptions.ValidationError): + Order._create_from_product_multiselect(self.stock_request_user) diff --git a/stock_request/views/product.xml b/stock_request/views/product.xml new file mode 100644 index 000000000..ac60d5cf5 --- /dev/null +++ b/stock_request/views/product.xml @@ -0,0 +1,26 @@ + + + + + Request Stock + ir.actions.server + code + + + +action = records.env['stock.request.order']._create_from_product_multiselect(records) + + + + + Request Stock + ir.actions.server + code + + + +action = records.env['stock.request.order']._create_from_product_multiselect(records) + + + +