Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0][IMP] maintenance_plan: Create requests from a domain #357

Merged
merged 1 commit into from
Oct 23, 2023
Merged
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
28 changes: 27 additions & 1 deletion maintenance_plan/models/maintenance_equipment.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
string="Maintenance Plan Count",
store=True,
)
search_maintenance_plan_count = fields.Integer(
compute="_compute_search_maintenance_plan_count",
string="Maintenance All Plan Count",
)
maintenance_team_required = fields.Boolean(compute="_compute_team_required")
notes = fields.Text(string="Notes")

Expand All @@ -30,6 +34,15 @@
equipment.with_context(active_test=False).maintenance_plan_ids
)

@api.depends("maintenance_plan_ids")
def _compute_search_maintenance_plan_count(self):
for equipment in self:
equipment.search_maintenance_plan_count = (

Check warning on line 40 in maintenance_plan/models/maintenance_equipment.py

View check run for this annotation

Codecov / codecov/patch

maintenance_plan/models/maintenance_equipment.py#L40

Added line #L40 was not covered by tests
self.env["maintenance.plan"]
.with_context(active_test=False)
.search_count([("search_equipment_id", "=", equipment.id)])
)

@api.depends("maintenance_plan_ids")
def _compute_team_required(self):
for equipment in self:
Expand All @@ -55,6 +68,19 @@
)
)

def _prepare_requests_from_plan(self, maintenance_plan, next_maintenance_date):
if self:
return self._prepare_request_from_plan(
maintenance_plan, next_maintenance_date
)
equipments = maintenance_plan._get_maintenance_equipments()
return [
equipment._prepare_request_from_plan(
maintenance_plan, next_maintenance_date
)
for equipment in equipments
]

def _prepare_request_from_plan(self, maintenance_plan, next_maintenance_date):
team_id = maintenance_plan.maintenance_team_id.id or self.maintenance_team_id.id
request_model = self.env["maintenance.request"]
Expand Down Expand Up @@ -121,7 +147,7 @@
# Create maintenance request until we reach planning horizon
while next_maintenance_date <= horizon_date:
if next_maintenance_date >= fields.Date.today():
vals = self._prepare_request_from_plan(mtn_plan, next_maintenance_date)
vals = self._prepare_requests_from_plan(mtn_plan, next_maintenance_date)
requests |= request_model.create(vals)
next_maintenance_date = next_maintenance_date + mtn_plan.get_relativedelta(
mtn_plan.interval, mtn_plan.interval_step or "year"
Expand Down
49 changes: 49 additions & 0 deletions maintenance_plan/models/maintenance_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools import safe_eval


class MaintenancePlan(models.Model):
Expand Down Expand Up @@ -81,6 +82,44 @@
skip_notify_follower_on_requests = fields.Boolean(
string="Do not notify to follower when creating requests?", default=True
)
generate_with_domain = fields.Boolean()
generate_domain = fields.Char(string="Apply on")
search_equipment_id = fields.Many2one(
comodel_name="maintenance.equipment",
compute="_compute_search_equipment",
search="_search_search_equipment",
)

@api.model
def _search_search_equipment(self, operator, value):
if operator != "=" or not value:
raise ValueError(_("Unsupported search operator"))
plans = self.search([("generate_with_domain", "=", True)])
plan_ids = []
equipment = self.env["maintenance.equipment"].browse(value)

Check warning on line 99 in maintenance_plan/models/maintenance_plan.py

View check run for this annotation

Codecov / codecov/patch

maintenance_plan/models/maintenance_plan.py#L96-L99

Added lines #L96 - L99 were not covered by tests
for plan in plans:
if equipment.filtered_domain(
safe_eval.safe_eval(
plan.generate_domain or "[]", plan._get_eval_context()
)
):
plan_ids.append(plan.id)
return ["|", ("equipment_id", "=", value), ("id", "in", plan_ids)]

Check warning on line 107 in maintenance_plan/models/maintenance_plan.py

View check run for this annotation

Codecov / codecov/patch

maintenance_plan/models/maintenance_plan.py#L106-L107

Added lines #L106 - L107 were not covered by tests

@api.depends("equipment_id")
def _compute_search_equipment(self):
for record in self:
record.search_equipment_id = record.equipment_id

Check warning on line 112 in maintenance_plan/models/maintenance_plan.py

View check run for this annotation

Codecov / codecov/patch

maintenance_plan/models/maintenance_plan.py#L112

Added line #L112 was not covered by tests

def _get_eval_context(self):
"""Prepare the context used when evaluating python code
:returns: dict -- evaluation context given to safe_eval
"""
return {
"datetime": safe_eval.datetime,
"dateutil": safe_eval.dateutil,
"time": safe_eval.time,
}

def name_get(self):
result = []
Expand Down Expand Up @@ -209,3 +248,13 @@
for plan in self:
equipment = plan.equipment_id
equipment._create_new_request(plan)

def _get_maintenance_equipments(self):
self.ensure_one()
if self.generate_with_domain and not self.equipment_id:
return self.env["maintenance.equipment"].search(
safe_eval.safe_eval(
self.generate_domain or "[]", self._get_eval_context()
)
)
return [self.equipment_id]
4 changes: 4 additions & 0 deletions maintenance_plan/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ planning horizon. Therefore, the maintenance manager can have a proper planning
of how many maintenance requests are programming for the future. Leaving planning
horizon to 0 will only create those maintenance request that are scheduled for
today.

We can also create maintenance requests from a plan using a domain for selecting the equipments.
This way, we might have a single plan that will generate all the requests.
In order to use it, we need to mark the `Generate with Domain` field.
1 change: 1 addition & 0 deletions maintenance_plan/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import test_maintenance_plan
from . import test_maintenance_plan_domain
9 changes: 9 additions & 0 deletions maintenance_plan/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,13 @@ def setUpClass(cls):
"planning_step": "month",
}
)
cls.maintenance_plan_5 = cls.maintenance_plan_obj.create(
{
"start_maintenance_date": today,
"interval": 1,
"interval_step": "month",
"maintenance_plan_horizon": 2,
"planning_step": "month",
}
)
cls.report_obj = cls.env["ir.actions.report"]
35 changes: 35 additions & 0 deletions maintenance_plan/tests/test_maintenance_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,41 @@ def test_generate_requests2(self):
self.today_date + relativedelta(weeks=9),
)

def test_generate_requests_no_equipment(self):
self.cron.method_direct_trigger()
generated_requests = self.maintenance_request_obj.search(
[("maintenance_plan_id", "=", self.maintenance_plan_5.id)],
order="schedule_date asc",
)

self.assertEqual(len(generated_requests), 3)

# We set plan start_maintenanca_date to a future one. New requests should take
# into account this new date.

self.maintenance_plan_5.write(
{
"start_maintenance_date": fields.Date.to_string(
self.today_date + timedelta(weeks=9)
),
"maintenance_plan_horizon": 3,
}
)

self.cron.method_direct_trigger()

generated_requests = self.maintenance_request_obj.search(
[("maintenance_plan_id", "=", self.maintenance_plan_5.id)],
order="schedule_date asc",
)

self.assertEqual(len(generated_requests), 4)
self.assertEqual(
generated_requests[-1].request_date,
self.today_date + relativedelta(weeks=9),
)
self.assertFalse(generated_requests.mapped("equipment_id"))

def test_get_relativedelta(self):
plan = self.maintenance_plan_1
result = plan.get_relativedelta(1, "day")
Expand Down
38 changes: 38 additions & 0 deletions maintenance_plan/tests/test_maintenance_plan_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2023 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import json

from odoo.addons.maintenance_plan.tests.common import TestMaintenancePlanBase


class TestMaintenancePlanDomain(TestMaintenancePlanBase):
def test_generate_requests_no_domain(self):
self.cron.method_direct_trigger()
generated_requests = self.maintenance_request_obj.search(
[("maintenance_plan_id", "=", self.maintenance_plan_5.id)],
order="schedule_date asc",
)

self.assertEqual(len(generated_requests), 3)
self.assertFalse(generated_requests.mapped("equipment_id"))

def test_generate_requests_domain(self):
equipment_2 = self.maintenance_equipment_obj.create({"name": "Laptop 2"})
self.maintenance_plan_5.write(
{
"generate_with_domain": True,
"generate_domain": json.dumps(
[("id", "in", [equipment_2.id, self.equipment_1.id])]
),
}
)
self.cron.method_direct_trigger()
generated_requests = self.maintenance_request_obj.search(
[("maintenance_plan_id", "=", self.maintenance_plan_5.id)],
order="schedule_date asc",
)

self.assertEqual(len(generated_requests), 6)
self.assertIn(equipment_2, generated_requests.mapped("equipment_id"))
self.assertIn(self.equipment_1, generated_requests.mapped("equipment_id"))
2 changes: 1 addition & 1 deletion maintenance_plan/views/maintenance_equipment_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
>
<field
string="Plans"
name="maintenance_plan_count"
name="search_maintenance_plan_count"
widget="statinfo"
/>
</button>
Expand Down
12 changes: 11 additions & 1 deletion maintenance_plan/views/maintenance_plan_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@
name="maintenance_team_id"
attrs="{'required': [('equipment_id', '=', False)]}"
/>
<field
name="generate_with_domain"
attrs="{'invisible': [('equipment_id', '!=', False)]}"
/>
<field
name="generate_domain"
widget="domain"
options="{'model': 'maintenance.equipment', 'in_dialog': True}"
attrs="{'invisible': ['|', ('equipment_id', '!=', False), ('generate_with_domain', '=', False)]}"
/>
</group>
<group>
<field name="skip_notify_follower_on_requests" />
Expand Down Expand Up @@ -181,7 +191,7 @@
'default_equipment_id': active_id, 'hide_equipment_id': 0
}</field>
<field name="domain">['|', ('active', '=', True), ('active', '=',
False), ('equipment_id', '=', active_id)]
False), ('search_equipment_id', '=', active_id)]
</field>
</record>
<menuitem
Expand Down
Loading