Skip to content

Commit

Permalink
[9.0][ADD] stock_inventory_exclude_sublocation (OCA#240)
Browse files Browse the repository at this point in the history
* [ADD] stock_inventory_exclude_sublocation
  • Loading branch information
LoisRForgeFlow authored and Urvisha-OSI committed Mar 22, 2023
1 parent a08226a commit 29224da
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 0 deletions.
69 changes: 69 additions & 0 deletions stock_inventory_exclude_sublocation/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

===================================
Stock Inventory Exclude Sublocation
===================================

This module extends the functionality of Inventory Adjustment to allow you to
exclude all the sublocations when doing an inventory adjustment for a
given location.

Sometimes we just want to make an inventory adjustment of just one shelf, or
space and forget about extra subdivisions in the location. In other cases we
do inventories of smaller locations contained in our stock, so we don't want
to count them again when doing an inventory adjustment of the parent location.
E.g. if we apply a cycle count strategy.


Usage
=====

To use this module, you simply need to:

#. Create a new inventory adjustment.
#. Select the option inventory of all products.
#. Check the box "Exclude Sublocations".

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/153/9.0


Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/{project_repo}/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.

Credits
=======

Images
------

* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* Lois Rilo Antelo <[email protected]>


Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.
7 changes: 7 additions & 0 deletions stock_inventory_exclude_sublocation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import models
from . import tests
21 changes: 21 additions & 0 deletions stock_inventory_exclude_sublocation/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Stock Inventory Exclude Sublocation",
"summary": "Allow to perform inventories of a location without including "
"its child locations.",
"version": "9.0.1.0.0",
"author": "Eficent,"
"Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"category": "Warehouse Management",
"depends": ["stock"],
"data": [
'views/stock_inventory_view.xml'
],
"license": "AGPL-3",
'installable': True,
'application': False,
}
6 changes: 6 additions & 0 deletions stock_inventory_exclude_sublocation/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import stock_inventory
53 changes: 53 additions & 0 deletions stock_inventory_exclude_sublocation/models/stock_inventory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from openerp import fields, models, api


class StockInventory(models.Model):
_inherit = 'stock.inventory'

exclude_sublocation = fields.Boolean(string='Exclude Sublocations',
default=False)

@api.model
def _get_inventory_lines(self, inventory):
if inventory.exclude_sublocation:
product_obj = self.env['product.product']
domain = ' location_id = %s'
args = (tuple(inventory.location_id.ids))
if inventory.partner_id:
domain += ' and owner_id = %s'
args += (inventory.partner_id.id,)
if inventory.lot_id:
domain += ' and lot_id = %s'
args += (inventory.lot_id.id,)
if inventory.product_id:
domain += ' and product_id = %s'
args += (inventory.product_id.id,)
if inventory.package_id:
domain += ' and package_id = %s'
args += (inventory.package_id.id,)

self.env.cr.execute('''
SELECT product_id, sum(qty) as product_qty, location_id, lot_id
as prod_lot_id, package_id, owner_id as partner_id
FROM stock_quant WHERE''' + domain + '''
GROUP BY product_id, location_id, lot_id, package_id, partner_id
''', args)
vals = []
for product_line in self.env.cr.dictfetchall():
for key, value in product_line.items():
if not value:
product_line[key] = False
product_line['inventory_id'] = inventory.id
product_line['theoretical_qty'] = product_line['product_qty']
if product_line['product_id']:
product = product_obj.browse(product_line['product_id'])
product_line['product_uom_id'] = product.uom_id.id
vals.append(product_line)
return vals
else:
return super(StockInventory, self)._get_inventory_lines(inventory)
6 changes: 6 additions & 0 deletions stock_inventory_exclude_sublocation/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import test_exclude_sublocation
187 changes: 187 additions & 0 deletions stock_inventory_exclude_sublocation/tests/test_exclude_sublocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

import openerp.tests.common as common


class TestStockInventoryExcludeSublocation(common.TransactionCase):

def setUp(self):
super(TestStockInventoryExcludeSublocation, self).setUp()
self.inventory_model = self.env['stock.inventory']
self.location_model = self.env['stock.location']
self.lot_model = self.env['stock.production.lot']
self.quant_model = self.env['stock.quant']
self.package_model = self.env['stock.quant.package']
self.res_users_model = self.env['res.users']

self.company = self.env.ref('base.main_company')
self.partner = self.ref('base.res_partner_4')
self.grp_stock_manager = self.env.ref('stock.group_stock_manager')
self.grp_tracking_owner = self.env.ref('stock.group_tracking_owner')
self.grp_production_lot = self.env.ref('stock.group_production_lot')
self.grp_tracking_lot = self.env.ref('stock.group_tracking_lot')

self.user = self.res_users_model.create({
'name': 'Test Account User',
'login': 'user_1',
'password': 'demo',
'email': '[email protected]',
'company_id': self.company.id,
'company_ids': [(4, self.company.id)],
'groups_id': [(6, 0, [
self.grp_stock_manager.id,
self.grp_tracking_owner.id,
self.grp_production_lot.id,
self.grp_tracking_lot.id
])]
})

self.product1 = self.env['product.product'].create({
'name': 'Product for parent location',
'type': 'product',
'default_code': 'PROD1',
})
self.product2 = self.env['product.product'].create({
'name': 'Product for child location',
'type': 'product',
'default_code': 'PROD2',
})
self.location = self.location_model.create({
'name': 'Inventory tests',
'usage': 'internal',
})
self.sublocation = self.location_model.create({
'name': 'Inventory sublocation test',
'usage': 'internal',
'location_id': self.location.id
})
self.lot_a = self.lot_model.create({
'name': 'Lot for product1',
'product_id': self.product1.id
})
self.package = self.package_model.create({'name': 'PACK00TEST1'})

# Add a product in each location
starting_inv = self.inventory_model.create({
'name': 'Starting inventory',
'filter': 'product',
'line_ids': [
(0, 0, {
'product_id': self.product1.id,
'product_uom_id': self.env.ref(
"product.product_uom_unit").id,
'product_qty': 2.0,
'location_id': self.location.id,
'prod_lot_id': self.lot_a.id
}),
(0, 0, {
'product_id': self.product2.id,
'product_uom_id': self.env.ref(
"product.product_uom_unit").id,
'product_qty': 4.0,
'location_id': self.sublocation.id,
'prod_lot_id': self.lot_a.id
}),
],
})
starting_inv.action_done()

def _create_inventory_all_products(self, name, location,
exclude_sublocation):
inventory = self.inventory_model.create({
'name': name,
'filter': 'none',
'location_id': location.id,
'exclude_sublocation': exclude_sublocation
})
return inventory

def test_not_excluding_sublocations(self):
'''Check if products in sublocations are included into the inventory
if the excluding sublocations option is disabled.'''
inventory_location = self._create_inventory_all_products(
'location inventory', self.location, False)
inventory_location.prepare_inventory()
inventory_location.action_done()
lines = inventory_location.line_ids
self.assertEqual(len(lines), 2, 'Not all expected products are '
'included')

def test_excluding_sublocations(self):
'''Check if products in sublocations are not included if the exclude
sublocations is enabled.'''
inventory_location = self._create_inventory_all_products(
'location inventory', self.location, True)
inventory_sublocation = self._create_inventory_all_products(
'sublocation inventory', self.sublocation, True)
inventory_location.prepare_inventory()
inventory_location.action_done()
inventory_sublocation.prepare_inventory()
inventory_sublocation.action_done()
lines_location = inventory_location.line_ids
lines_sublocation = inventory_sublocation.line_ids
self.assertEqual(len(lines_location), 1,
'The products in the sublocations are not excluded')
self.assertEqual(len(lines_sublocation), 1,
'The products in the sublocations are not excluded')

def test_lot_excluding_sublocation(self):
'''Check if the sublocations are excluded when using lots.'''
inventory = self.inventory_model.sudo(self.user.id).create({
'name': 'Inventory lot',
'filter': 'lot',
'location_id': self.location.id,
'lot_id': self.lot_a.id,
'exclude_sublocation': True
})
inventory.prepare_inventory()
inventory.action_done()
lines = inventory.line_ids
self.assertEqual(len(lines), 1, 'The products in the sublocations are '
'not excluded with lots.')

def test_product_and_owner_excluding_sublocation(self):
'''Check if sublocations are excluded when filtering by owner and
product.'''
self.quant_model.create({
'product_id': self.product1.id,
'location_id': self.location.id,
'qty': 1,
'owner_id': self.partner,
})
inventory = self.inventory_model.sudo(self.user.id).create({
'name': 'Inventory lot',
'filter': 'product_owner',
'location_id': self.location.id,
'product_id': self.product1.id,
'partner_id': self.partner,
'exclude_sublocation': True
})
inventory.prepare_inventory()
lines = inventory.line_ids
self.assertEqual(len(lines), 1,
'The products in the sublocations are '
'not excluded with product and owner filter.')

def test_pack_excluding_sublocation(self):
'''Check if sublocations are excluded when filtering by package.'''
self.quant_model.create({
'product_id': self.product1.id,
'location_id': self.location.id,
'qty': 1,
'package_id': self.package.id
})
inventory = self.inventory_model.sudo(self.user.id).create({
'name': 'Inventory lot',
'filter': 'pack',
'location_id': self.location.id,
'package_id': self.package.id,
'exclude_sublocation': True
})
inventory.prepare_inventory()
lines = inventory.line_ids
self.assertEqual(len(lines), 1, 'The products in the sublocations are '
'not excluded with package filter.')
18 changes: 18 additions & 0 deletions stock_inventory_exclude_sublocation/views/stock_inventory_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2017 Eficent Business and IT Consulting Services S.L.
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->

<odoo>

<record id="view_inventory_form" model="ir.ui.view">
<field name="name">Inventory form view - cycle count extension </field>
<field name="model">stock.inventory</field>
<field name="inherit_id" ref="stock.view_inventory_form"/>
<field name="arch" type="xml">
<field name="filter" position="after">
<field name="exclude_sublocation"/>
</field>
</field>
</record>

</odoo>

0 comments on commit 29224da

Please sign in to comment.