[11.0][IMP] stock_request: create request order from products

This commit allows the user to select multiple products from
a product list view and to create a Stock Request Order with
an action. If the user selects templates, all of those templates'
variants will be added to the order.

This is useful as it allows the user to filter products beforehand
based on their stocks, and to create a request order for all
those products whose stock is too low.
This commit is contained in:
Atte Isopuro
2018-04-12 13:47:28 +03:00
committed by hveficent
parent 84600684cb
commit 45e4fd2d52
5 changed files with 159 additions and 10 deletions

View File

@@ -72,6 +72,7 @@ Contributors
* Jordi Ballester (EFICENT) <jordi.ballester@eficent.com>.
* Enric Tobella <etobella@creublanca.es>
* Atte Isopuro <atte.isopuro@avoin.systems>
Maintainer
----------

View File

@@ -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",

View File

@@ -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

View File

@@ -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)

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_variant_generate_stock_request_orders" model="ir.actions.server">
<field name="name">Request Stock</field>
<field name="type">ir.actions.server</field>
<field name="state">code</field>
<field name="model_id" ref="product.model_product_product"/>
<field name="binding_model_id" ref="product.model_product_product"/>
<field name="code">
action = records.env['stock.request.order']._create_from_product_multiselect(records)
</field>
</record>
<record id="action_template_generate_stock_request_orders" model="ir.actions.server">
<field name="name">Request Stock</field>
<field name="type">ir.actions.server</field>
<field name="state">code</field>
<field name="model_id" ref="product.model_product_template"/>
<field name="binding_model_id" ref="product.model_product_template"/>
<field name="code">
action = records.env['stock.request.order']._create_from_product_multiselect(records)
</field>
</record>
</odoo>