mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[ADD] stock_free_qty
This commit is contained in:
1
setup/stock_free_quantity/odoo/addons/stock_free_quantity
Symbolic link
1
setup/stock_free_quantity/odoo/addons/stock_free_quantity
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../../../stock_free_quantity
|
||||||
6
setup/stock_free_quantity/setup.py
Normal file
6
setup/stock_free_quantity/setup.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import setuptools
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
setup_requires=['setuptools-odoo'],
|
||||||
|
odoo_addon=True,
|
||||||
|
)
|
||||||
3
stock_free_quantity/__init__.py
Normal file
3
stock_free_quantity/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from . import models
|
||||||
17
stock_free_quantity/__manifest__.py
Normal file
17
stock_free_quantity/__manifest__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Stock Free Quantity",
|
||||||
|
"version": "14.0.1.0.0",
|
||||||
|
"author": "akretion,Odoo Community Association (OCA)",
|
||||||
|
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||||
|
"development_status": "Production/Stable",
|
||||||
|
"category": "Warehouse",
|
||||||
|
"depends": ["stock"],
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"data": [
|
||||||
|
"views/product_template_view.xml",
|
||||||
|
"views/product_product_view.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
}
|
||||||
1
stock_free_quantity/models/__init__.py
Normal file
1
stock_free_quantity/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import product_template
|
||||||
33
stock_free_quantity/models/product_template.py
Normal file
33
stock_free_quantity/models/product_template.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ProductTemplate(models.Model):
|
||||||
|
_inherit = "product.template"
|
||||||
|
|
||||||
|
def _compute_quantities_dict(self):
|
||||||
|
prod_available = super()._compute_quantities_dict()
|
||||||
|
for template in self:
|
||||||
|
free_qty = 0
|
||||||
|
for p in template.with_context(active_test=False).product_variant_ids:
|
||||||
|
free_qty += p.free_qty
|
||||||
|
prod_available[template.id]["free_qty"] = free_qty
|
||||||
|
return prod_available
|
||||||
|
|
||||||
|
def _compute_quantities(self):
|
||||||
|
super()._compute_quantities()
|
||||||
|
res = self._compute_quantities_dict()
|
||||||
|
for template in self:
|
||||||
|
template.free_qty = res[template.id]["free_qty"]
|
||||||
|
|
||||||
|
def _search_free_qty(self, operator, value):
|
||||||
|
return [("product_variant_ids.free_qty", operator, value)]
|
||||||
|
|
||||||
|
free_qty = fields.Float(
|
||||||
|
"Free Quantity",
|
||||||
|
compute="_compute_quantities",
|
||||||
|
search="_search_free_qty",
|
||||||
|
compute_sudo=False,
|
||||||
|
digits="Product Unit of Measure",
|
||||||
|
)
|
||||||
1
stock_free_quantity/readme/CONTRIBUTORS.rst
Normal file
1
stock_free_quantity/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* Florian da Costa <florian.dacost@akretion.com>
|
||||||
1
stock_free_quantity/readme/DESCRIPTION.rst
Normal file
1
stock_free_quantity/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Add Product variant free quantity in Product form view and add the same on Product templates.
|
||||||
1
stock_free_quantity/tests/__init__.py
Normal file
1
stock_free_quantity/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import test_template_free_qty
|
||||||
35
stock_free_quantity/tests/test_template_free_qty.py
Normal file
35
stock_free_quantity/tests/test_template_free_qty.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||||
|
|
||||||
|
from odoo.tests import common
|
||||||
|
|
||||||
|
|
||||||
|
class TestTemplateFreeQty(common.SavepointCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
|
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||||
|
cls.template_with_variant = cls.env.ref(
|
||||||
|
"product.product_product_11_product_template"
|
||||||
|
)
|
||||||
|
cls.template_with_no_variant = cls.env.ref(
|
||||||
|
"product.product_product_10_product_template"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_template_free_qty(self):
|
||||||
|
# template has 2 variants with 26 and 30 free qty, template should have 56
|
||||||
|
self.assertEqual(self.template_with_variant.free_qty, 56)
|
||||||
|
self.assertEqual(
|
||||||
|
self.template_with_no_variant.free_qty,
|
||||||
|
self.template_with_no_variant.product_variant_ids.free_qty,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_search_template_free_qty(self):
|
||||||
|
template_with_free_qty_ids = (
|
||||||
|
self.env["product.template"].search([("free_qty", ">", 0)]).ids
|
||||||
|
)
|
||||||
|
self.assertIn(self.template_with_variant.id, template_with_free_qty_ids)
|
||||||
|
self.assertIn(self.template_with_no_variant.id, template_with_free_qty_ids)
|
||||||
|
template_with_no_free_qty = self.env.ref(
|
||||||
|
"product.product_product_22_product_template"
|
||||||
|
)
|
||||||
|
self.assertNotIn(template_with_no_free_qty.id, template_with_free_qty_ids)
|
||||||
36
stock_free_quantity/views/product_product_view.xml
Normal file
36
stock_free_quantity/views/product_product_view.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="product_normal_form_view">
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
<field name="inherit_id" ref="stock.product_form_view_procurement_button" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath
|
||||||
|
expr="//button[@name='action_product_forecast_report']"
|
||||||
|
position="after"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="object"
|
||||||
|
name="action_open_quants"
|
||||||
|
attrs="{'invisible':[('type', 'not in', ['product'])]}"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-building-o"
|
||||||
|
>
|
||||||
|
<div class="o_field_widget o_stat_info">
|
||||||
|
<span class="o_stat_value" widget="statinfo">
|
||||||
|
<field
|
||||||
|
name="free_qty"
|
||||||
|
widget="statinfo"
|
||||||
|
nolabel="1"
|
||||||
|
class="mr4"
|
||||||
|
/>
|
||||||
|
<field name="uom_name" />
|
||||||
|
</span>
|
||||||
|
<span class="o_stat_text">Free</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
84
stock_free_quantity/views/product_template_view.xml
Normal file
84
stock_free_quantity/views/product_template_view.xml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record model="ir.ui.view" id="view_stock_available_form">
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field
|
||||||
|
name="inherit_id"
|
||||||
|
ref="stock.product_template_form_view_procurement_button"
|
||||||
|
/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath
|
||||||
|
expr="//button[@name='action_product_tmpl_forecast_report']"
|
||||||
|
position="after"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="object"
|
||||||
|
name="action_open_quants"
|
||||||
|
attrs="{'invisible':[('type', 'not in', ['product'])]}"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-building-o"
|
||||||
|
>
|
||||||
|
<div class="o_field_widget o_stat_info">
|
||||||
|
<span class="o_stat_value" widget="statinfo">
|
||||||
|
<field
|
||||||
|
name="free_qty"
|
||||||
|
widget="statinfo"
|
||||||
|
nolabel="1"
|
||||||
|
class="mr4"
|
||||||
|
/>
|
||||||
|
<field name="uom_name" />
|
||||||
|
</span>
|
||||||
|
<span class="o_stat_text">Free</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="product_template_kanban_free_qty_view">
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_kanban_view" />
|
||||||
|
<field name="priority" eval="15" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[@name='product_lst_price']" position="after">
|
||||||
|
<div t-if="record.type.raw_value == 'product'">Free: <field
|
||||||
|
name="free_qty"
|
||||||
|
/> <field name="uom_id" /></div>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_stock_product_template_free_qty_tree" model="ir.ui.view">
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_stock_product_template_tree" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="qty_available" position="after">
|
||||||
|
<field
|
||||||
|
name="free_qty"
|
||||||
|
attrs="{'invisible':[('type', '!=', 'product')]}"
|
||||||
|
optional="show"
|
||||||
|
decoration-danger="free_qty < 0"
|
||||||
|
decoration-bf="1"
|
||||||
|
/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_stock_product_product_free_qty_tree" model="ir.ui.view">
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_stock_product_tree" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="qty_available" position="after">
|
||||||
|
<field
|
||||||
|
name="free_qty"
|
||||||
|
attrs="{'invisible':[('type', '!=', 'product')]}"
|
||||||
|
optional="show"
|
||||||
|
decoration-danger="free_qty < 0"
|
||||||
|
decoration-warning="free_qty == 0"
|
||||||
|
decoration-bf="1"
|
||||||
|
/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user