mirror of
https://github.com/OCA/manufacture.git
synced 2025-01-28 16:37:15 +02:00
1
mrp_production_component_availability_search/README.rst
Normal file
1
mrp_production_component_availability_search/README.rst
Normal file
@@ -0,0 +1 @@
|
||||
TO BE GENERATED
|
||||
1
mrp_production_component_availability_search/__init__.py
Normal file
1
mrp_production_component_availability_search/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
16
mrp_production_component_availability_search/__manifest__.py
Normal file
16
mrp_production_component_availability_search/__manifest__.py
Normal 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"],
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
from . import mrp_production
|
||||
@@ -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)],
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
* `Camptocamp <https://www.camptocamp.com>`_
|
||||
|
||||
* Iván Todorovich <ivan.todorovich@camptocamp.com>
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
from . import test_search
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
../../../../mrp_production_component_availability_search
|
||||
@@ -0,0 +1,6 @@
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
||||
Reference in New Issue
Block a user