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