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 maintenance_plan/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Contributors
- Alexei Rivera <arivera@archeti.com>
- Yann Papouin <ypa@decgroupe.com>
- Yannick Payot <yannick.payot@acsone.eu>
- Samir Guesmi <samir.guesmi@acsone.eu>

Maintainers
-----------
Expand Down
20 changes: 6 additions & 14 deletions maintenance_plan/models/maintenance_equipment.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,21 +125,13 @@ def _create_new_request(self, mtn_plan):
)
# We check maintenance request already created and create until
# planning horizon is met
start_maintenance_date_plan = mtn_plan.start_maintenance_date
furthest_maintenance_request = self.env["maintenance.request"].search(
[
("maintenance_plan_id", "=", mtn_plan.id),
("request_date", ">=", start_maintenance_date_plan),
],
order="request_date desc",
limit=1,
)
furthest_maintenance_request = mtn_plan._get_furthest_maintenance_request()
if furthest_maintenance_request:
next_maintenance_date = (
furthest_maintenance_request.request_date
+ mtn_plan.get_relativedelta(
mtn_plan.interval, mtn_plan.interval_step or "year"
)
anchor_date = mtn_plan._get_maintenance_recurrence_anchor(
furthest_maintenance_request
)
next_maintenance_date = anchor_date + mtn_plan.get_relativedelta(
mtn_plan.interval, mtn_plan.interval_step or "year"
)
else:
next_maintenance_date = mtn_plan.next_maintenance_date
Expand Down
89 changes: 54 additions & 35 deletions maintenance_plan/models/maintenance_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,50 +159,69 @@ def get_relativedelta(self, interval, step):
elif step == "year":
return relativedelta(years=interval)

def _get_furthest_maintenance_request(self):
"""Return the latest maintenance request of the current plan period."""
self.ensure_one()
return self.env["maintenance.request"].search(
[
("maintenance_plan_id", "=", self.id),
("request_date", ">=", self.start_maintenance_date),
],
order="request_date desc",
limit=1,
)

def _get_maintenance_recurrence_anchor(self, request):
"""Return the date from which the next recurrence must be computed.

Defaults to the request theoretical due date (``request_date``).
Override to anchor the recurrence on another date, e.g. the date the
maintenance was actually performed.
"""
self.ensure_one()
return request.request_date

def _get_next_maintenance_date(self):
"""Return the plan's next due date"""
self.ensure_one()
interval_timedelta = self.get_relativedelta(
self.interval, self.interval_step or "year"
)
next_maintenance_todo = self.env["maintenance.request"].search(
[
("maintenance_plan_id", "=", self.id),
("stage_id.done", "!=", True),
("close_date", "=", False),
("request_date", ">=", self.start_maintenance_date),
],
order="request_date asc",
limit=1,
)
if next_maintenance_todo:
return next_maintenance_todo.request_date
furthest_request = self._get_furthest_maintenance_request()
if furthest_request:
next_date = (
self._get_maintenance_recurrence_anchor(furthest_request)
+ interval_timedelta
)
else:
next_date = self.start_maintenance_date
while next_date < fields.Date.today():
next_date = next_date + interval_timedelta
return next_date

@api.depends(
"interval",
"interval_step",
"start_maintenance_date",
"maintenance_ids.request_date",
"maintenance_ids.close_date",
"maintenance_ids.stage_id",
)
def _compute_next_maintenance(self):
for plan in self.filtered(lambda x: x.interval > 0):
interval_timedelta = plan.get_relativedelta(
plan.interval, plan.interval_step
)

next_maintenance_todo = self.env["maintenance.request"].search(
[
("maintenance_plan_id", "=", plan.id),
("stage_id.done", "!=", True),
("close_date", "=", False),
("request_date", ">=", plan.start_maintenance_date),
],
order="request_date asc",
limit=1,
)

if next_maintenance_todo:
plan.next_maintenance_date = next_maintenance_todo.request_date
else:
last_maintenance_done = self.env["maintenance.request"].search(
[
("maintenance_plan_id", "=", plan.id),
("request_date", ">=", plan.start_maintenance_date),
],
order="request_date desc",
limit=1,
)
if last_maintenance_done:
plan.next_maintenance_date = (
last_maintenance_done.request_date + interval_timedelta
)
else:
next_date = plan.start_maintenance_date
while next_date < fields.Date.today():
next_date = next_date + interval_timedelta
plan.next_maintenance_date = next_date
plan.next_maintenance_date = plan._get_next_maintenance_date()

@api.constrains("company_id", "equipment_id")
def _check_company_id(self):
Expand Down
1 change: 1 addition & 0 deletions maintenance_plan/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
- Alexei Rivera \<<arivera@archeti.com>\>
- Yann Papouin \<<ypa@decgroupe.com>\>
- Yannick Payot \<<yannick.payot@acsone.eu>\>
- Samir Guesmi \<<samir.guesmi@acsone.eu>\>
1 change: 1 addition & 0 deletions maintenance_plan/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ <h3><a class="toc-backref" href="#toc-entry-7">Contributors</a></h3>
<li>Alexei Rivera &lt;<a class="reference external" href="mailto:arivera&#64;archeti.com">arivera&#64;archeti.com</a>&gt;</li>
<li>Yann Papouin &lt;<a class="reference external" href="mailto:ypa&#64;decgroupe.com">ypa&#64;decgroupe.com</a>&gt;</li>
<li>Yannick Payot &lt;<a class="reference external" href="mailto:yannick.payot&#64;acsone.eu">yannick.payot&#64;acsone.eu</a>&gt;</li>
<li>Samir Guesmi &lt;<a class="reference external" href="mailto:samir.guesmi&#64;acsone.eu">samir.guesmi&#64;acsone.eu</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
Expand Down
63 changes: 63 additions & 0 deletions maintenance_plan/tests/test_maintenance_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,66 @@ def test_maintenance_plan_button_manual_request_generation(self):
self.assertEqual(len(self.maintenance_plan_1.maintenance_ids), 0)
self.maintenance_plan_1.button_manual_request_generation()
self.assertEqual(len(self.maintenance_plan_1.maintenance_ids), 3)

def test_next_maintenance_date_matches_generated_request(self):
"""The plan's next_maintenance_date is the date the next generated
request actually carries, before and after completion."""
plan = self.maintenance_plan_1
# No request yet: shown date == first generated request_date
shown_before = plan.next_maintenance_date
with self.enter_registry_test_mode():
self.cron.method_direct_trigger()
first_request = self.maintenance_request_obj.search(
[("maintenance_plan_id", "=", plan.id)], order="request_date asc", limit=1
)
self.assertEqual(first_request.request_date, shown_before)
# Complete the whole pending series, then the shown date must equal
# the request_date the next cron run will generate.
plan.maintenance_ids.stage_id = self.done_stage
plan.maintenance_plan_horizon = 4
shown_after = plan.next_maintenance_date
with self.enter_registry_test_mode():
self.cron.method_direct_trigger()
new_request = self.maintenance_request_obj.search(
[
("maintenance_plan_id", "=", plan.id),
("stage_id.done", "!=", True),
],
order="request_date asc",
limit=1,
)
self.assertEqual(new_request.request_date, shown_after)

def test_next_maintenance_date_advances_past_today(self):
"""With no pending request, an overdue recurrence is advanced to the
first occurrence on or after today - the one the generator creates."""
plan = self.maintenance_plan_1
plan.start_maintenance_date = fields.Date.from_string("2022-06-10")
# A single request, done well before today.
self.maintenance_request_obj.create(
{
"name": "Old done maintenance",
"maintenance_plan_id": plan.id,
"equipment_id": plan.equipment_id.id,
"request_date": fields.Date.from_string("2022-06-10"),
"stage_id": self.done_stage.id,
}
)
# today is frozen to 2023-01-25, interval is 1 month: 2022-07-10,
# 2022-08-10, ... first occurrence >= today is 2023-02-10.
self.assertEqual(
plan.next_maintenance_date, fields.Date.from_string("2023-02-10")
)
with self.enter_registry_test_mode():
self.cron.method_direct_trigger()
first_generated = self.maintenance_request_obj.search(
[
("maintenance_plan_id", "=", plan.id),
("stage_id.done", "!=", True),
],
order="request_date asc",
limit=1,
)
self.assertEqual(
first_generated.request_date, fields.Date.from_string("2023-02-10")
)
Loading