Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deltatech_product_reordering_limit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
16 changes: 16 additions & 0 deletions deltatech_product_reordering_limit/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Product Reordering Limit",
"summary": "Custom reordering limits for products",
"version": "18.0.1.0.0",
"author": "Terrabit, Voicu Stefan",
"website": "https://www.terrabit.ro",
"category": "Inventory",
"depends": ["product", "stock"],
"license": "OPL-1",
"data": [
"views/product_template_view.xml",
],
"installable": True,
"development_status": "Beta",
"maintainers": ["VoicuStefan2001"],
}
1 change: 1 addition & 0 deletions deltatech_product_reordering_limit/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import product_template
57 changes: 57 additions & 0 deletions deltatech_product_reordering_limit/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from odoo import api, fields, models


class ProductTemplate(models.Model):
_inherit = "product.template"

total_minimum = fields.Float(string="Total Minimum", default=0.0)
total_maximum = fields.Float(string="Total Maximum", default=0.0)

def _search_is_below_min(self, operator, value):
if operator not in ("=", "!=") or not isinstance(value, bool):
raise NotImplementedError("Unsupported operator or value for _search_is_below_min")

# We need to find templates where qty_available < total_minimum.
# Since we can't do this easily in a domain with a computed field vs another field,
# and a raw SQL might miss Odoo's complex stock logic (locations, etc.),
# but for a general "Below Minimum" filter, we can use a reasonable SQL approximation
# that aggregates variant quantities to the template level.

# We use a subquery to sum quantities from stock_quant grouped by product,
# then join with product_product to group by template.
# We only count internal locations as per requirement.
self.env.cr.execute("""
SELECT pt.id
FROM product_template pt
JOIN (
SELECT pp.product_tmpl_id, SUM(sq.qty) as total_qty_available
FROM product_product pp
LEFT JOIN (
SELECT sq.product_id, SUM(sq.quantity) as qty
FROM stock_quant sq
JOIN stock_location sl ON sl.id = sq.location_id
WHERE sl.usage = 'internal'
GROUP BY sq.product_id
) sq ON sq.product_id = pp.id
GROUP BY pp.product_tmpl_id
) t_qty ON t_qty.product_tmpl_id = pt.id
WHERE pt.total_minimum > 0 AND COALESCE(t_qty.total_qty_available, 0.0) < pt.total_minimum
""")
ids = [r[0] for r in self.env.cr.fetchall()]

if (operator == "=" and value) or (operator == "!=" and not value):
return [("id", "in", ids)]
else:
return [("id", "not in", ids)]

is_below_min = fields.Boolean(
string="Is Below Minimum", compute="_compute_is_below_min", search="_search_is_below_min"
)

@api.depends("qty_available", "total_minimum")
def _compute_is_below_min(self):
for template in self:
# qty_available on template level in Odoo 18 (when called without specific context)
# aggregates qty_available of all its variants.
# Product variants' qty_available already filters by internal locations by default.
template.is_below_min = template.total_minimum > 0 and template.qty_available < template.total_minimum
3 changes: 3 additions & 0 deletions deltatech_product_reordering_limit/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
26 changes: 26 additions & 0 deletions deltatech_product_reordering_limit/views/product_template_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.reordering.limit.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view" />
<field name="arch" type="xml">
<group name="group_general" position="inside">
<field name="total_minimum" />
<field name="total_maximum" />
</group>
</field>
</record>

<record id="product_template_search_view" model="ir.ui.view">
<field name="name">product.template.reordering.limit.search</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view" />
<field name="arch" type="xml">
<xpath expr="//filter[@name='filter_to_sell']" position="after">
<separator />
<filter string="Below Minimum" name="below_minimum" domain="[('is_below_min', '=', True)]" />
</xpath>
</field>
</record>
</odoo>