Compare commits
No commits in common. "17cc64c522e8ab2f78690ac2fd17be95e05f0c21" and "93ccb20a17005780b61461e5efdddb01ca161c42" have entirely different histories.
17cc64c522
...
93ccb20a17
20 changed files with 0 additions and 650 deletions
|
|
@ -8,7 +8,6 @@ class ProductTemplate(models.Model):
|
||||||
code_balance = fields.Integer("Balance Code")
|
code_balance = fields.Integer("Balance Code")
|
||||||
ingredients = fields.Text()
|
ingredients = fields.Text()
|
||||||
expiration_date = fields.Date("Expiration Date")
|
expiration_date = fields.Date("Expiration Date")
|
||||||
to_print = fields.Boolean(tracking=True)
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_price_member(self):
|
def get_price_member(self):
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
from . import models
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
{
|
|
||||||
"name": "Stock Inventory Category Concurrency",
|
|
||||||
"version": "16.0.1.0.0",
|
|
||||||
"summary": "Permite abrir simultáneamente ajustes de inventario en categorías distintas",
|
|
||||||
"category": "Inventory",
|
|
||||||
"license": "AGPL-3",
|
|
||||||
"author": "Your Company",
|
|
||||||
"depends": ["stock_inventory"],
|
|
||||||
"data": [],
|
|
||||||
"installable": True,
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
from . import stock_inventory
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
from odoo import _, models
|
|
||||||
from odoo.exceptions import ValidationError
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryAdjustmentsGroup(models.Model):
|
|
||||||
_inherit = "stock.inventory"
|
|
||||||
|
|
||||||
def action_state_to_in_progress(self):
|
|
||||||
"""
|
|
||||||
Relaja el bloqueo por categoría: si el ajuste actual es por categoría,
|
|
||||||
solo bloquea otros ajustes en progreso que afecten a la MISMA categoría exacta,
|
|
||||||
no a categorías hermanas distintas ni a todas sus hijas.
|
|
||||||
"""
|
|
||||||
self.ensure_one()
|
|
||||||
# Reusar la lógica original, pero parchear el caso de category
|
|
||||||
search_filter = [
|
|
||||||
(
|
|
||||||
"location_id",
|
|
||||||
"child_of" if not self.exclude_sublocation else "in",
|
|
||||||
self.location_ids.ids,
|
|
||||||
),
|
|
||||||
("to_do", "=", True),
|
|
||||||
]
|
|
||||||
|
|
||||||
error_field = "location_id"
|
|
||||||
error_message = _(
|
|
||||||
"There's already an Adjustment in Process "
|
|
||||||
"using one requested Location: %(names)s. "
|
|
||||||
"Blocking adjustments: %(blocking_names)s"
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.product_ids:
|
|
||||||
search_filter.append(("product_id", "in", self.product_ids.ids))
|
|
||||||
error_field = "product_id"
|
|
||||||
error_message = _(
|
|
||||||
"There are active adjustments for the requested products: %(names)s. "
|
|
||||||
"Blocking adjustments: %(blocking_names)s"
|
|
||||||
)
|
|
||||||
elif self.category_id:
|
|
||||||
# Solo misma categoría exacta, sin OR hijos
|
|
||||||
search_filter.append(("product_id.categ_id", "=", self.category_id.id))
|
|
||||||
error_field = "category_id"
|
|
||||||
error_message = _(
|
|
||||||
"There are active adjustments for the requested category: %(names)s. "
|
|
||||||
"Blocking adjustments: %(blocking_names)s"
|
|
||||||
)
|
|
||||||
|
|
||||||
quants = self.env["stock.quant"].search(search_filter)
|
|
||||||
if quants:
|
|
||||||
inventory_ids = self.env["stock.inventory"].search(
|
|
||||||
[("stock_quant_ids", "in", quants.ids), ("state", "=", "in_progress")]
|
|
||||||
)
|
|
||||||
if inventory_ids:
|
|
||||||
blocking_names = ", ".join(inventory_ids.mapped("name"))
|
|
||||||
names = self._get_quant_joined_names(quants, error_field)
|
|
||||||
raise ValidationError(
|
|
||||||
error_message % {"names": names, "blocking_names": blocking_names}
|
|
||||||
)
|
|
||||||
|
|
||||||
quants = self._get_quants(self.location_ids)
|
|
||||||
self.write({"state": "in_progress", "stock_quant_ids": [(6, 0, quants.ids)]})
|
|
||||||
quants.write(
|
|
||||||
{
|
|
||||||
"to_do": True,
|
|
||||||
"user_id": self.responsible_id,
|
|
||||||
"inventory_date": self.date,
|
|
||||||
"current_inventory_id": self.id,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
==========================================
|
|
||||||
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 +0,0 @@
|
||||||
from . import models
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"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 +0,0 @@
|
||||||
from . import stock_inventory
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
# 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")
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
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 +0,0 @@
|
||||||
The module requires no additional configuration. The `include_exhausted_products` field defaults to `False` to maintain compatibility with the original behavior.
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
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>`_
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
* **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
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
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 +0,0 @@
|
||||||
from . import test_stock_inventory_exhausted
|
|
||||||
|
|
@ -1,227 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<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