mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[16.0][ADD] stock_available_immediately_exclude_location
This commit is contained in:
8
stock_available_immediately_exclude_location/README.rst
Normal file
8
stock_available_immediately_exclude_location/README.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
==================================================
|
||||
Exclude locations from immediately usable quantity
|
||||
==================================================
|
||||
|
||||
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
3
stock_available_immediately_exclude_location/__init__.py
Normal file
3
stock_available_immediately_exclude_location/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import models
|
||||
14
stock_available_immediately_exclude_location/__manifest__.py
Normal file
14
stock_available_immediately_exclude_location/__manifest__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
"name": "Exclude locations from immediately usable quantity",
|
||||
"version": "16.0.1.0.0",
|
||||
"website": "https://github.com/OCA/stock-logistics-availability",
|
||||
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
|
||||
"license": "AGPL-3",
|
||||
"depends": ["stock_available_immediately"],
|
||||
"data": ["views/stock_location.xml"],
|
||||
"category": "Hidden",
|
||||
"installable": True,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * stock_available_immediately_exclude_location
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 16.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-18 14:11+0000\n"
|
||||
"PO-Revision-Date: 2023-01-18 14:11+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: stock_available_immediately_exclude_location
|
||||
#: model:ir.model.fields,field_description:stock_available_immediately_exclude_location.field_stock_location__exclude_from_immediately_usable_qty
|
||||
msgid "Exclude from immediately usable quantity"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_available_immediately_exclude_location
|
||||
#: model_terms:ir.ui.view,arch_db:stock_available_immediately_exclude_location.stock_location_search_view
|
||||
msgid "Excluded from immediately usable quantities"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_available_immediately_exclude_location
|
||||
#: model:ir.model,name:stock_available_immediately_exclude_location.model_stock_location
|
||||
msgid "Inventory Locations"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_available_immediately_exclude_location
|
||||
#: model:ir.model,name:stock_available_immediately_exclude_location.model_product_product
|
||||
msgid "Product Variant"
|
||||
msgstr ""
|
||||
|
||||
#. module: stock_available_immediately_exclude_location
|
||||
#: model:ir.model.fields,help:stock_available_immediately_exclude_location.field_stock_location__exclude_from_immediately_usable_qty
|
||||
msgid "This property is not inherited by children locations"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,4 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import product_product
|
||||
from . import stock_location
|
||||
@@ -0,0 +1,70 @@
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import models
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
|
||||
_inherit = "product.product"
|
||||
|
||||
def _compute_available_quantities_dict(self):
|
||||
"""
|
||||
change the way immediately_usable_qty is computed by deducing the quants
|
||||
in excluded locations
|
||||
"""
|
||||
res, stock_dict = super()._compute_available_quantities_dict()
|
||||
exclude_location_ids = (
|
||||
self._get_locations_excluded_from_immediately_usable_qty().ids
|
||||
)
|
||||
|
||||
if exclude_location_ids:
|
||||
excluded_qty_dict = self.with_context(
|
||||
location=exclude_location_ids, compute_child=False
|
||||
)._compute_quantities_dict(
|
||||
self._context.get("lot_id"),
|
||||
self._context.get("owner_id"),
|
||||
self._context.get("package_id"),
|
||||
self._context.get("from_date"),
|
||||
self._context.get("to_date"),
|
||||
)
|
||||
|
||||
for product_id in res:
|
||||
if exclude_location_ids:
|
||||
res[product_id]["immediately_usable_qty"] -= excluded_qty_dict[
|
||||
product_id
|
||||
]["qty_available"]
|
||||
return res, stock_dict
|
||||
|
||||
def _get_locations_excluded_from_immediately_usable_qty(self):
|
||||
return self.env["stock.location"].search(
|
||||
self._get_domain_location_excluded_from_immediately_usable_qty()
|
||||
)
|
||||
|
||||
def _get_domain_location_excluded_from_immediately_usable_qty(self):
|
||||
"""
|
||||
Parses the context and returns a list of location_ids based on it that
|
||||
should be excluded from the immediately_usable_qty
|
||||
"""
|
||||
quant_domain = self.env["product.product"]._get_domain_locations()[0]
|
||||
# Adapt the domain on quants (which normally contains only company_id and
|
||||
# location_id) for stock.location and add the criteria on
|
||||
# exclude_from_immediately_usable_qty to it.
|
||||
# Be sure to exclude potential fields that couldn't belong to stock.location
|
||||
# and replace such term by something True domain compatible
|
||||
location_domain = []
|
||||
location_fields = self.env["stock.location"].fields_get()
|
||||
for element in quant_domain:
|
||||
if expression.is_leaf(element) and element[0] not in location_fields:
|
||||
# exclude the element from domain and replace by something True
|
||||
location_domain.append((1, "=", 1)) # True and domain compatible
|
||||
elif expression.is_leaf(element):
|
||||
location_domain.append(
|
||||
(element[0].replace("location_id.", ""), element[1], element[2])
|
||||
)
|
||||
else:
|
||||
location_domain.append(element)
|
||||
return expression.AND(
|
||||
[location_domain, [("exclude_from_immediately_usable_qty", "=", True)]]
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockLocation(models.Model):
|
||||
|
||||
_inherit = "stock.location"
|
||||
|
||||
exclude_from_immediately_usable_qty = fields.Boolean(
|
||||
"Exclude from immediately usable quantity",
|
||||
default=False,
|
||||
index=True,
|
||||
help="This property is not inherited by children locations",
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
* Author: Hughes Damry <hughes.damry@acsone.eu>
|
||||
@@ -0,0 +1,2 @@
|
||||
This module adds a boolean to exclude the tagged stock locations
|
||||
from the stock available immediately.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.2 KiB |
@@ -0,0 +1 @@
|
||||
from . import test_stock_exclude_location
|
||||
@@ -0,0 +1,61 @@
|
||||
# Copyright 2023 ACSONE SA/NV
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestStockLogisticsWarehouse(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.move_model = cls.env["stock.move"]
|
||||
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
||||
cls.product = cls.env["product.product"].create(
|
||||
{
|
||||
"name": "Product Test",
|
||||
"uom_id": cls.uom_unit.id,
|
||||
"type": "product",
|
||||
}
|
||||
)
|
||||
cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
|
||||
cls.stock_location = cls.env.ref("stock.stock_location_stock")
|
||||
cls.pack_location = cls.env.ref("stock.location_pack_zone")
|
||||
(cls.stock_location | cls.pack_location).write({"active": True})
|
||||
|
||||
def test01(self):
|
||||
location_ids = (self.stock_location | self.pack_location).ids
|
||||
ctx_loc = {"location": location_ids}
|
||||
move_stock = self.move_model.create(
|
||||
{
|
||||
"location_id": self.supplier_location.id,
|
||||
"location_dest_id": self.stock_location.id,
|
||||
"name": "MOVE STOCK ",
|
||||
"product_id": self.product.id,
|
||||
"product_uom": self.product.uom_id.id,
|
||||
"product_uom_qty": 15,
|
||||
}
|
||||
)
|
||||
move_pack = self.move_model.create(
|
||||
{
|
||||
"location_id": self.supplier_location.id,
|
||||
"location_dest_id": self.pack_location.id,
|
||||
"name": "MOVE PACK ",
|
||||
"product_id": self.product.id,
|
||||
"product_uom": self.product.uom_id.id,
|
||||
"product_uom_qty": 5,
|
||||
}
|
||||
)
|
||||
(move_stock | move_pack)._action_confirm()
|
||||
(move_stock | move_pack)._action_assign()
|
||||
move_stock.move_line_ids.write({"qty_done": 7.0})
|
||||
move_stock._action_done()
|
||||
q = self.product.with_context(**ctx_loc).immediately_usable_qty
|
||||
self.assertEqual(q, 7.0)
|
||||
move_pack.move_line_ids.write({"qty_done": 4.0})
|
||||
move_pack._action_done()
|
||||
q = self.product.with_context(**ctx_loc).immediately_usable_qty
|
||||
self.assertEqual(q, 11.0)
|
||||
self.pack_location.exclude_from_immediately_usable_qty = True
|
||||
self.product.invalidate_recordset() # force recompute
|
||||
q = self.product.with_context(**ctx_loc).immediately_usable_qty
|
||||
self.assertEqual(q, 7.0)
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 ACSONE SA/NV
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="stock_location_form_view">
|
||||
<field name="name">stock.location.form (in
|
||||
alc_stock_available_immediately)
|
||||
</field>
|
||||
<field name="model">stock.location</field>
|
||||
<field name="inherit_id" ref="stock.view_location_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='removal_strategy_id']" position="after">
|
||||
<field name="exclude_from_immediately_usable_qty" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="stock_location_search_view">
|
||||
<field
|
||||
name="name"
|
||||
>stock.location.search (in alc_stock_available_immediately)</field>
|
||||
<field name="model">stock.location</field>
|
||||
<field name="inherit_id" ref="stock.view_location_search" />
|
||||
<field name="arch" type="xml">
|
||||
<filter name="inactive" position="after">
|
||||
<filter
|
||||
string="Excluded from immediately usable quantities"
|
||||
name="exclude_from_immediately_usable_qty"
|
||||
domain="[('exclude_from_immediately_usable_qty','=',True)]"
|
||||
/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user