Merge PR #913 into 15.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2022-12-17 21:28:25 +00:00
12 changed files with 207 additions and 0 deletions

View File

@@ -0,0 +1 @@
TO BE GENERATED

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,16 @@
# Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
# @author Iván Todorovich <ivan.todorovich@camptocamp.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "MRP Component Availability Search",
"summary": "Filter manufacturing orders by their components availability state",
"version": "15.0.1.0.0",
"author": "Camptocamp, Odoo Community Association (OCA)",
"maintainers": ["ivantodorovich"],
"website": "https://github.com/OCA/manufacture",
"license": "AGPL-3",
"category": "Manufacture",
"depends": ["mrp"],
"data": ["views/mrp_production.xml"],
}

View File

@@ -0,0 +1 @@
from . import mrp_production

View File

@@ -0,0 +1,45 @@
# Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
# @author Iván Todorovich <ivan.todorovich@camptocamp.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.osv.expression import OR, distribute_not, normalize_domain
class MrpProduction(models.Model):
_inherit = "mrp.production"
components_availability_state = fields.Selection(
search="_search_components_availability_state"
)
@api.model
def _search_components_availability_state(self, operator, value):
if operator not in ("=", "!="):
raise UserError(_("Invalid domain operator %s", operator))
if not isinstance(value, str) and value is not False:
raise UserError(_("Invalid domain right operand %s", value))
# Search all orders for which we need to compute the products availability.
# The value would be ``False`` if the mo doesn't fall under this list.
domain = [("state", "in", ["confirmed", "progress"])]
# Special case for "is set" / "is not set" domains
if operator == "=" and value is False:
return distribute_not(["!"] + normalize_domain(domain))
if operator == "!=" and value is False:
return domain
# Perform a search and compute values to filter on-the-fly
res_ids = []
orders = self.with_context(prefetch_fields=False).search(domain)
for order in orders:
if order.components_availability_state == value:
res_ids.append(order.id)
if operator == "=":
return [("id", "in", res_ids)]
else:
return OR(
[
distribute_not(["!"] + normalize_domain(domain)),
[("id", "not in", res_ids)],
]
)

View File

@@ -0,0 +1,3 @@
* `Camptocamp <https://www.camptocamp.com>`_
* Iván Todorovich <ivan.todorovich@camptocamp.com>

View File

@@ -0,0 +1,7 @@
Allows to search or filter manufacturing orders by their Components Availability State.
To do so, a search method is implemented on that computed field, to compute and filter
records on the fly.
For convenience, a filter named **Components Availability Issues** is added to the
search view.

View File

@@ -0,0 +1 @@
from . import test_search

View File

@@ -0,0 +1,102 @@
# Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
# @author Iván Todorovich <ivan.todorovich@camptocamp.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import Command
from odoo.tests import Form, TransactionCase
class TestSearch(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.partner = cls.env.ref("base.res_partner_1")
cls.component = cls.env["product.product"].create(
{"name": "Component", "type": "product"}
)
cls.product = cls.env["product.product"].create(
{"name": "Product", "type": "product"}
)
cls.product_bom = cls.env["mrp.bom"].create(
{
"product_tmpl_id": cls.product.product_tmpl_id.id,
"product_qty": 1.0,
"product_uom_id": cls.product.uom_id.id,
"bom_line_ids": [
Command.create(
{
"product_id": cls.component.id,
"product_qty": 1.0,
"product_uom_id": cls.component.uom_id.id,
}
)
],
}
)
# Create some initial stocks
cls.location_stock = cls.env.ref("stock.stock_location_stock")
cls.env["stock.quant"].create(
{
"product_id": cls.component.id,
"product_uom_id": cls.component.uom_id.id,
"location_id": cls.location_stock.id,
"quantity": 10.00,
}
)
# Create some manufacturing orders
cls.mo_draft = cls._create_mo(confirm=False)
cls.mo_confirm = cls._create_mo(confirm=True)
cls.mo_unavailable = cls._create_mo(quantity=1000.0, confirm=True)
@classmethod
def _create_mo(cls, product=None, bom=None, quantity=1.0, confirm=False):
if product is None:
product = cls.product
if bom is None:
bom = cls.product_bom
mo_form = Form(cls.env["mrp.production"])
mo_form.product_id = product
mo_form.bom_id = bom
mo_form.product_qty = quantity
mo_form.product_uom_id = product.uom_id
mo = mo_form.save()
if confirm:
mo.action_confirm()
return mo
def test_search_is_set(self):
records = self.env["mrp.production"].search(
[
("product_id", "=", self.product.id),
("components_availability_state", "!=", False),
]
)
self.assertEqual(records, self.mo_confirm + self.mo_unavailable)
def test_search_is_not_set(self):
records = self.env["mrp.production"].search(
[
("product_id", "=", self.product.id),
("components_availability_state", "=", False),
]
)
self.assertEqual(records, self.mo_draft)
def test_search_is_available(self):
records = self.env["mrp.production"].search(
[
("product_id", "=", self.product.id),
("components_availability_state", "=", "available"),
]
)
self.assertEqual(records, self.mo_confirm)
def test_search_is_not_available(self):
records = self.env["mrp.production"].search(
[
("product_id", "=", self.product.id),
("components_availability_state", "!=", "available"),
]
)
self.assertEqual(records, self.mo_draft + self.mo_unavailable)

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2022 Camptocamp SA (https://www.camptocamp.com).
@author Iván Todorovich <ivan.todorovich@camptocamp.com>
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_mrp_production_filter" model="ir.ui.view">
<field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.view_mrp_production_filter" />
<field name="arch" type="xml">
<filter name="planning_issues" position="after">
<filter
name="component_availability_issues"
string="Component Availability Issues"
domain="[('components_availability_state', '=', 'late')]"
/>
</filter>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
../../../../mrp_production_component_availability_search

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)