add stock_inventory_product_exhausted: prevent for removing zero quantity stock quants. Add exhausted products to inventory adjustments
This commit is contained in:
parent
e4050a6e31
commit
17cc64c522
15 changed files with 565 additions and 0 deletions
133
stock_inventory_product_exhausted/README.rst
Normal file
133
stock_inventory_product_exhausted/README.rst
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
==========================================
|
||||||
|
Stock Inventory Exhausted Products
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
..
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! This file is generated by oca-gen-addon-readme !!
|
||||||
|
!! changes will be overwritten. !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! source digest: sha256:...
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||||
|
:target: https://odoo-community.org/page/development-status
|
||||||
|
:alt: Beta
|
||||||
|
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github
|
||||||
|
:target: https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_inventory_product_exhausted
|
||||||
|
:alt: OCA/stock-logistics-warehouse
|
||||||
|
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||||
|
:target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-16-0/stock-logistics-warehouse-16-0-stock_inventory_product_exhausted
|
||||||
|
:alt: Translate me on Weblate
|
||||||
|
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||||
|
:target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&target_branch=16.0
|
||||||
|
:alt: Try me on Runboat
|
||||||
|
|
||||||
|
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||||
|
|
||||||
|
This module extends the functionality of the `stock_inventory` module from the OCA/stock-logistics-warehouse repository to include exhausted products (with zero quantity) in inventory adjustments.
|
||||||
|
|
||||||
|
In recent versions of Odoo, when a product reaches zero available quantity, its `stock.quant` is automatically deleted. This module allows creating quants with zero quantity for exhausted products when creating a new inventory adjustment, facilitating the physical counting of these products.
|
||||||
|
|
||||||
|
**Table of contents**
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Features
|
||||||
|
========
|
||||||
|
|
||||||
|
* **Configurable field**: Includes a boolean field `include_exhausted_products` to enable/disable the functionality
|
||||||
|
* **Default behavior**: Maintains the original behavior of the base module when the option is disabled
|
||||||
|
* **Support for all selection types**: Works with manual selection, by category, specific product, or all products
|
||||||
|
* **Storable products only**: Creates quants only for products of type `product`, not for services
|
||||||
|
* **Prevents duplicates**: Does not create duplicate quants if they already exist for the product and location
|
||||||
|
* **User interface**: Field visible in the inventory adjustment form
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
The module requires no additional configuration. The `include_exhausted_products` field defaults to `False` to maintain compatibility with the original behavior.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
To use this module:
|
||||||
|
|
||||||
|
#. Go to **Inventory > Operations > Inventory Adjustments**
|
||||||
|
#. Create a new inventory adjustment
|
||||||
|
#. Configure the desired locations and product selection
|
||||||
|
#. **Enable** the "Include Exhausted Products" field if you want to include products without stock
|
||||||
|
#. Click on "Start Inventory"
|
||||||
|
|
||||||
|
Use cases
|
||||||
|
---------
|
||||||
|
|
||||||
|
**With the field enabled:**
|
||||||
|
|
||||||
|
* Zero quantity quants will be created for products that have no stock in the selected locations
|
||||||
|
* Allows physically counting products that are exhausted in the system
|
||||||
|
* Useful for complete inventories where you want to verify that there really is no stock of certain products
|
||||||
|
|
||||||
|
**With the field disabled:**
|
||||||
|
|
||||||
|
* Standard behavior of the original module
|
||||||
|
* Only products that already have existing quants are included
|
||||||
|
|
||||||
|
Known issues / Roadmap
|
||||||
|
======================
|
||||||
|
|
||||||
|
* Review the necessity of the StockQuant class override
|
||||||
|
* Consider performance optimizations for large product catalogs
|
||||||
|
* Add option to filter by product categories when including exhausted products
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
============
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/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 <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_inventory_product_exhausted%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Authors
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Criptomart
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Luis Norte <luisnore@example.com>
|
||||||
|
|
||||||
|
Maintainers
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: https://odoo-community.org
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. |maintainer-luisnore| image:: https://github.com/luisnore.png?size=40px
|
||||||
|
:target: https://github.com/luisnore
|
||||||
|
:alt: luisnore
|
||||||
|
|
||||||
|
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||||
|
|
||||||
|
|maintainer-luisnore|
|
||||||
|
|
||||||
|
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_inventory_product_exhausted>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||||
1
stock_inventory_product_exhausted/__init__.py
Normal file
1
stock_inventory_product_exhausted/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import models
|
||||||
16
stock_inventory_product_exhausted/__manifest__.py
Normal file
16
stock_inventory_product_exhausted/__manifest__.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "Stock Inventory Exhausted Products",
|
||||||
|
"version": "16.0.1.0.0",
|
||||||
|
"development_status": "Beta",
|
||||||
|
"depends": ["stock_inventory"],
|
||||||
|
"author": "Criptomart, Odoo Community Association (OCA)",
|
||||||
|
"maintainers": ["luisnore"],
|
||||||
|
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"category": "Warehouse",
|
||||||
|
"summary": "Create quants for products with zero quantity in stock adjustments",
|
||||||
|
"data": [
|
||||||
|
"views/stock_inventory.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
}
|
||||||
1
stock_inventory_product_exhausted/models/__init__.py
Normal file
1
stock_inventory_product_exhausted/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import stock_inventory
|
||||||
113
stock_inventory_product_exhausted/models/stock_inventory.py
Normal file
113
stock_inventory_product_exhausted/models/stock_inventory.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Copyright 2025 Your Name or Company
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import _, models, api, fields
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class StockInventory(models.Model):
|
||||||
|
_inherit = "stock.inventory"
|
||||||
|
|
||||||
|
include_exhausted_products = fields.Boolean(
|
||||||
|
string="Include Exhausted Products",
|
||||||
|
help="If enabled, products with zero stock will be included "
|
||||||
|
"in the inventory adjustment by creating zero-quantity quants.",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _create_zero_quants(self, locations, products):
|
||||||
|
"""Create zero-quantity quants for products without existing quants.
|
||||||
|
|
||||||
|
This method creates stock quants with zero quantity for storable products
|
||||||
|
that don't have existing quants in the specified locations. This allows
|
||||||
|
these products to be included in inventory adjustments even when they
|
||||||
|
have no current stock.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
locations (stock.location recordset): Locations to check
|
||||||
|
products (product.product recordset): Products to process
|
||||||
|
"""
|
||||||
|
Quant = self.env["stock.quant"]
|
||||||
|
|
||||||
|
# Get existing quants to avoid duplicates
|
||||||
|
existing_quants = Quant.search(
|
||||||
|
[
|
||||||
|
("product_id", "in", products.ids),
|
||||||
|
("location_id", "in", locations.ids),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
existing_combinations = {
|
||||||
|
(q.product_id.id, q.location_id.id) for q in existing_quants
|
||||||
|
}
|
||||||
|
|
||||||
|
quants_to_create = []
|
||||||
|
for location in locations:
|
||||||
|
for product in products.filtered(lambda p: p.type == "product"):
|
||||||
|
if (product.id, location.id) not in existing_combinations:
|
||||||
|
_logger.debug(
|
||||||
|
"Creating zero quant for product %s in location %s",
|
||||||
|
product.name,
|
||||||
|
location.name,
|
||||||
|
)
|
||||||
|
quants_to_create.append(
|
||||||
|
{
|
||||||
|
"product_id": product.id,
|
||||||
|
"location_id": location.id,
|
||||||
|
"quantity": 0,
|
||||||
|
"company_id": location.company_id.id or self.env.company.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if quants_to_create:
|
||||||
|
Quant.create(quants_to_create)
|
||||||
|
|
||||||
|
def _get_quants(self, locations):
|
||||||
|
"""Override to include zero-quantity quants when requested.
|
||||||
|
|
||||||
|
This method extends the base functionality to optionally include
|
||||||
|
products with zero stock by creating zero-quantity quants before
|
||||||
|
retrieving the final quant list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
locations (stock.location recordset): Locations for inventory
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
stock.quant recordset: Quants to include in inventory
|
||||||
|
"""
|
||||||
|
quants = super()._get_quants(locations)
|
||||||
|
|
||||||
|
if self.include_exhausted_products:
|
||||||
|
# Determine products based on selection criteria
|
||||||
|
if self.product_selection == "category":
|
||||||
|
products = self.env["product.product"].search(
|
||||||
|
[("categ_id", "child_of", self.category_id.id)]
|
||||||
|
)
|
||||||
|
elif self.product_selection in ("manual", "one", "lot"):
|
||||||
|
products = self.product_ids
|
||||||
|
else: # "all"
|
||||||
|
products = self.env["product.product"].search(
|
||||||
|
[("type", "=", "product")]
|
||||||
|
)
|
||||||
|
|
||||||
|
self._create_zero_quants(locations, products)
|
||||||
|
# Re-fetch quants to include newly created ones
|
||||||
|
return super()._get_quants(locations)
|
||||||
|
|
||||||
|
return quants
|
||||||
|
|
||||||
|
|
||||||
|
class StockQuant(models.Model):
|
||||||
|
_inherit = "stock.quant"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _unlink_zero_quants(self):
|
||||||
|
"""Prevent automatic unlinking of zero quants.
|
||||||
|
|
||||||
|
This method overrides the default behavior to prevent
|
||||||
|
zero quants from being automatically removed.
|
||||||
|
This may need review to ensure it doesn't conflict
|
||||||
|
with standard Odoo behavior.
|
||||||
|
"""
|
||||||
|
_logger.debug("Preventing automatic unlinking of zero quants")
|
||||||
6
stock_inventory_product_exhausted/readme/BUGTRACKER.rst
Normal file
6
stock_inventory_product_exhausted/readme/BUGTRACKER.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/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 <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_inventory_product_exhausted%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||||
|
|
||||||
|
Do not contact contributors directly about support or help with technical issues.
|
||||||
1
stock_inventory_product_exhausted/readme/CONFIGURE.rst
Normal file
1
stock_inventory_product_exhausted/readme/CONFIGURE.rst
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
The module requires no additional configuration. The `include_exhausted_products` field defaults to `False` to maintain compatibility with the original behavior.
|
||||||
21
stock_inventory_product_exhausted/readme/CONTRIBUTORS.rst
Normal file
21
stock_inventory_product_exhausted/readme/CONTRIBUTORS.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: https://odoo-community.org
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. |maintainer-yourusername| image:: https://github.com/yourusername.png?size=40px
|
||||||
|
:target: https://github.com/yourusername
|
||||||
|
:alt: yourusername
|
||||||
|
|
||||||
|
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||||
|
|
||||||
|
|maintainer-yourusername|
|
||||||
|
|
||||||
|
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_inventory_product_exhausted>`_ project on GitHub.
|
||||||
|
|
||||||
|
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||||
3
stock_inventory_product_exhausted/readme/DESCRIPTION.rst
Normal file
3
stock_inventory_product_exhausted/readme/DESCRIPTION.rst
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
This module extends the functionality of the `stock_inventory` module from the OCA/stock-logistics-warehouse repository to include exhausted products (with zero quantity) in inventory adjustments.
|
||||||
|
|
||||||
|
In recent versions of Odoo, when a product reaches zero available quantity, its `stock.quant` is automatically deleted. This module allows creating quants with zero quantity for exhausted products when creating a new inventory adjustment, facilitating the physical counting of these products.
|
||||||
3
stock_inventory_product_exhausted/readme/DEVELOPMENT.rst
Normal file
3
stock_inventory_product_exhausted/readme/DEVELOPMENT.rst
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
This is a beta version, the data model and design can change at any time without warning.
|
||||||
|
Only for development or testing purpose, do not use in production.
|
||||||
|
`More details on development status <https://odoo-community.org/page/development-status>`_
|
||||||
6
stock_inventory_product_exhausted/readme/FEATURES.rst
Normal file
6
stock_inventory_product_exhausted/readme/FEATURES.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
* **Configurable field**: Includes a boolean field `include_exhausted_products` to enable/disable the functionality
|
||||||
|
* **Default behavior**: Maintains the original behavior of the base module when the option is disabled
|
||||||
|
* **Support for all selection types**: Works with manual selection, by category, specific product, or all products
|
||||||
|
* **Storable products only**: Creates quants only for products of type `product`, not for services
|
||||||
|
* **Prevents duplicates**: Does not create duplicate quants if they already exist for the product and location
|
||||||
|
* **User interface**: Field visible in the inventory adjustment form
|
||||||
21
stock_inventory_product_exhausted/readme/USAGE.rst
Normal file
21
stock_inventory_product_exhausted/readme/USAGE.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
To use this module:
|
||||||
|
|
||||||
|
#. Go to **Inventory > Operations > Inventory Adjustments**
|
||||||
|
#. Create a new inventory adjustment
|
||||||
|
#. Configure the desired locations and product selection
|
||||||
|
#. **Enable** the "Include Exhausted Products" field if you want to include products without stock
|
||||||
|
#. Click on "Start Inventory"
|
||||||
|
|
||||||
|
Use cases
|
||||||
|
---------
|
||||||
|
|
||||||
|
**With the field enabled:**
|
||||||
|
|
||||||
|
* Zero quantity quants will be created for products that have no stock in the selected locations
|
||||||
|
* Allows physically counting products that are exhausted in the system
|
||||||
|
* Useful for complete inventories where you want to verify that there really is no stock of certain products
|
||||||
|
|
||||||
|
**With the field disabled:**
|
||||||
|
|
||||||
|
* Standard behavior of the original module
|
||||||
|
* Only products that already have existing quants are included
|
||||||
1
stock_inventory_product_exhausted/tests/__init__.py
Normal file
1
stock_inventory_product_exhausted/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from . import test_stock_inventory_exhausted
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestStockInventoryExhausted(TransactionCase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
# Create test products
|
||||||
|
self.product_with_stock = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Product With Stock",
|
||||||
|
"type": "product",
|
||||||
|
"categ_id": self.env.ref("product.product_category_all").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.product_without_stock = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Product Without Stock",
|
||||||
|
"type": "product",
|
||||||
|
"categ_id": self.env.ref("product.product_category_all").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create test location
|
||||||
|
self.location = self.env["stock.location"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Location",
|
||||||
|
"usage": "internal",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create initial stock for one product
|
||||||
|
self.env["stock.quant"].create(
|
||||||
|
{
|
||||||
|
"product_id": self.product_with_stock.id,
|
||||||
|
"location_id": self.location.id,
|
||||||
|
"quantity": 10.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_inventory_without_exhausted_products_flag(self):
|
||||||
|
"""Test that without the flag, zero stock products are not included"""
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Inventory without flag",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
"product_selection": "all",
|
||||||
|
"include_exhausted_products": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory.action_start()
|
||||||
|
|
||||||
|
# Should only have quants for products with existing stock
|
||||||
|
self.assertTrue(
|
||||||
|
any(
|
||||||
|
q.product_id == self.product_with_stock
|
||||||
|
for q in inventory.stock_quant_ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
any(
|
||||||
|
q.product_id == self.product_without_stock
|
||||||
|
for q in inventory.stock_quant_ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_inventory_with_exhausted_products_flag(self):
|
||||||
|
"""Test that with the flag, zero stock products are included"""
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Inventory with flag",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
"product_selection": "all",
|
||||||
|
"include_exhausted_products": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory.action_start()
|
||||||
|
|
||||||
|
# Should have quants for both products (including zero stock product)
|
||||||
|
self.assertTrue(
|
||||||
|
any(
|
||||||
|
q.product_id == self.product_with_stock
|
||||||
|
for q in inventory.stock_quant_ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
any(
|
||||||
|
q.product_id == self.product_without_stock
|
||||||
|
for q in inventory.stock_quant_ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check that zero stock product has quantity 0
|
||||||
|
zero_quant = inventory.stock_quant_ids.filtered(
|
||||||
|
lambda q: q.product_id == self.product_without_stock
|
||||||
|
)
|
||||||
|
self.assertEqual(zero_quant.quantity, 0.0)
|
||||||
|
|
||||||
|
def test_inventory_manual_selection_with_exhausted_flag(self):
|
||||||
|
"""Test manual product selection with exhausted products flag"""
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Manual Selection",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
"product_selection": "manual",
|
||||||
|
"product_ids": [(6, 0, [self.product_without_stock.id])],
|
||||||
|
"include_exhausted_products": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory.action_start()
|
||||||
|
|
||||||
|
# Should have quant for the selected product even if it has zero stock
|
||||||
|
self.assertTrue(
|
||||||
|
any(
|
||||||
|
q.product_id == self.product_without_stock
|
||||||
|
for q in inventory.stock_quant_ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
zero_quant = inventory.stock_quant_ids.filtered(
|
||||||
|
lambda q: q.product_id == self.product_without_stock
|
||||||
|
)
|
||||||
|
self.assertEqual(zero_quant.quantity, 0.0)
|
||||||
|
|
||||||
|
def test_inventory_category_selection_with_exhausted_flag(self):
|
||||||
|
"""Test category selection with exhausted products flag"""
|
||||||
|
# Create a specific category
|
||||||
|
test_category = self.env["product.category"].create({"name": "Test Category"})
|
||||||
|
|
||||||
|
# Create product in this category
|
||||||
|
product_in_category = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Product In Category",
|
||||||
|
"type": "product",
|
||||||
|
"categ_id": test_category.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Category Selection",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
"product_selection": "category",
|
||||||
|
"category_id": test_category.id,
|
||||||
|
"include_exhausted_products": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory.action_start()
|
||||||
|
|
||||||
|
# Should have quant for the product in category even if it has zero stock
|
||||||
|
self.assertTrue(
|
||||||
|
any(q.product_id == product_in_category for q in inventory.stock_quant_ids)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_create_zero_quants_only_for_product_type(self):
|
||||||
|
"""Test that zero quants are only created for products with type 'product'"""
|
||||||
|
# Create a service product
|
||||||
|
service_product = self.env["product.product"].create(
|
||||||
|
{
|
||||||
|
"name": "Service Product",
|
||||||
|
"type": "service",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Service Product",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
"product_selection": "manual",
|
||||||
|
"product_ids": [(6, 0, [service_product.id])],
|
||||||
|
"include_exhausted_products": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory.action_start()
|
||||||
|
|
||||||
|
# Should not create quants for service products
|
||||||
|
self.assertFalse(
|
||||||
|
any(q.product_id == service_product for q in inventory.stock_quant_ids)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_no_duplicate_quants_created(self):
|
||||||
|
"""Test that no duplicate quants are created if they already exist"""
|
||||||
|
# Create a quant with zero quantity manually
|
||||||
|
existing_quant = self.env["stock.quant"].create(
|
||||||
|
{
|
||||||
|
"product_id": self.product_without_stock.id,
|
||||||
|
"location_id": self.location.id,
|
||||||
|
"quantity": 0.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test No Duplicates",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
"product_selection": "manual",
|
||||||
|
"product_ids": [(6, 0, [self.product_without_stock.id])],
|
||||||
|
"include_exhausted_products": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
inventory.action_start()
|
||||||
|
|
||||||
|
# Should use existing quant, not create a new one
|
||||||
|
quants_for_product = self.env["stock.quant"].search(
|
||||||
|
[
|
||||||
|
("product_id", "=", self.product_without_stock.id),
|
||||||
|
("location_id", "=", self.location.id),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(len(quants_for_product), 1)
|
||||||
|
self.assertEqual(quants_for_product.id, existing_quant.id)
|
||||||
|
|
||||||
|
def test_field_default_value(self):
|
||||||
|
"""Test that include_exhausted_products field defaults to False"""
|
||||||
|
inventory = self.env["stock.inventory"].create(
|
||||||
|
{
|
||||||
|
"name": "Test Default Value",
|
||||||
|
"location_ids": [(6, 0, [self.location.id])],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertFalse(inventory.include_exhausted_products)
|
||||||
12
stock_inventory_product_exhausted/views/stock_inventory.xml
Normal file
12
stock_inventory_product_exhausted/views/stock_inventory.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<odoo>
|
||||||
|
<record id="view_stock_inventory_form_exhausted" model="ir.ui.view">
|
||||||
|
<field name="name">stock.inventory.form.exhausted</field>
|
||||||
|
<field name="model">stock.inventory</field>
|
||||||
|
<field name="inherit_id" ref="stock_inventory.view_inventory_group_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="exclude_sublocation" position="after">
|
||||||
|
<field name="include_exhausted_products"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue