diff --git a/resource_calendar_schedule_iteration/README.rst b/resource_calendar_schedule_iteration/README.rst new file mode 100644 index 000000000..20e9df858 --- /dev/null +++ b/resource_calendar_schedule_iteration/README.rst @@ -0,0 +1,94 @@ +==================================== +Resource Calendar Schedule Iteration +==================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/11.0/resource_calendar_schedule_iteration + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-11-0/server-tools-11-0-resource_calendar_schedule_iteration + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +When you want to plan a number of days in the past or in the future considering +a working calendar, Odoo limits the number of days/hours that you can plan +ahead. + +In case that you want to plan days forward/backward, it currently restricts +to 100 iterations. + +This module allows you to increase the iteration limit used in the resource +calendar to schedule days or hours by means of a system parameter defined +by the administrator. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +#. Go to *Settings* and activate the developer mode. + +#. Go to *Settings / Technical / Parameters / System Parameters* and define, + new values for the parameters *resource.calendar.schedule.days.iteration.limit* + and/or *resource.calendar.schedule.hours.iteration.limit* + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Eficent + +Contributors +~~~~~~~~~~~~ + +* Eficent (https://www.eficent.com) + + * Jordi Ballester Alomar + * Lois Rilo Antelo + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/resource_calendar_schedule_iteration/__init__.py b/resource_calendar_schedule_iteration/__init__.py new file mode 100644 index 000000000..cd4cb62c7 --- /dev/null +++ b/resource_calendar_schedule_iteration/__init__.py @@ -0,0 +1,2 @@ +from . import model +from .hooks import post_load_hook diff --git a/resource_calendar_schedule_iteration/__manifest__.py b/resource_calendar_schedule_iteration/__manifest__.py new file mode 100644 index 000000000..9b64b1e5e --- /dev/null +++ b/resource_calendar_schedule_iteration/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2019 Eficent Business and IT Consulting Services S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +{ + "name": "Resource Calendar Schedule Iteration", + "author": "Eficent, Odoo Community Association (OCA)", + "version": "11.0.1.0.0", + 'category': 'Hidden', + "website": "https://github.com/OCA/server-tools", + "depends": [ + 'resource', + ], + "data": [ + 'data/ir_config_parameter_data.xml', + ], + "license": 'LGPL-3', + "post_load": "post_load_hook", + "installable": True +} diff --git a/resource_calendar_schedule_iteration/data/ir_config_parameter_data.xml b/resource_calendar_schedule_iteration/data/ir_config_parameter_data.xml new file mode 100644 index 000000000..7432a3d3e --- /dev/null +++ b/resource_calendar_schedule_iteration/data/ir_config_parameter_data.xml @@ -0,0 +1,18 @@ + + + + + + resource.calendar.schedule.days.iteration.limit + 100 + + + + resource.calendar.schedule.hours.iteration.limit + 1000 + + + diff --git a/resource_calendar_schedule_iteration/hooks.py b/resource_calendar_schedule_iteration/hooks.py new file mode 100644 index 000000000..f8140501f --- /dev/null +++ b/resource_calendar_schedule_iteration/hooks.py @@ -0,0 +1,119 @@ +# Copyright 2019 Eficent Business and IT Consulting Services S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +import datetime + +from datetime import timedelta + +from odoo.addons.resource.models.resource import ResourceCalendar +from odoo.addons.resource.models.resource import to_naive_user_tz +from odoo.tools.float_utils import float_compare + + +def post_load_hook(): + + def _new_schedule_days(self, days, day_dt, compute_leaves=False, + resource_id=None): + if not hasattr(self, '_get_schedule_days_iteration_limit'): + return self._schedule_days_original( + days, day_dt, compute_leaves=compute_leaves, + resource_id=resource_id) + # START OF HOOK: Introduce here the iterations limit + iterations_limit = self._get_schedule_days_iteration_limit() or 100 + # END OF HOOK + backwards = (days < 0) + intervals = [] + planned_days, iterations = 0, 0 + + day_dt_tz = to_naive_user_tz(day_dt, self.env.user) + current_datetime = day_dt_tz.replace( + hour=0, minute=0, second=0, microsecond=0) + + # HOOK. Use the iterations_limit here + while planned_days < abs(days) and iterations < iterations_limit: + working_intervals = self._get_day_work_intervals( + current_datetime.date(), + compute_leaves=compute_leaves, resource_id=resource_id) + if not self or working_intervals: + # no calendar -> no working hours, but day is + # considered as worked + planned_days += 1 + intervals += working_intervals + # get next day + if backwards: + current_datetime = self._get_previous_work_day( + current_datetime) + else: + current_datetime = self._get_next_work_day(current_datetime) + # avoid infinite loops + iterations += 1 + + return intervals + + def _new_schedule_hours(self, hours, day_dt, compute_leaves=False, + resource_id=None): + if not hasattr(self, '_get_schedule_hours_iteration_limit'): + return self._schedule_hours_original( + hours, day_dt, compute_leaves=compute_leaves, + resource_id=resource_id) + self.ensure_one() + # START OF HOOK: Introduce here the iterations limit + iterations_limit = self._get_schedule_hours_iteration_limit() or 1000 + # END OF HOOK + backwards = (hours < 0) + intervals = [] + remaining_hours, iterations = abs(hours * 1.0), 0 + + day_dt_tz = to_naive_user_tz(day_dt, self.env.user) + current_datetime = day_dt_tz + + call_args = dict(compute_leaves=compute_leaves, + resource_id=resource_id) + + # HOOK. Use the iterations_limit here + while float_compare(remaining_hours, 0.0, precision_digits=2) in ( + 1, 0) and iterations < iterations_limit: + if backwards: + call_args['end_time'] = current_datetime.time() + else: + call_args['start_time'] = current_datetime.time() + + working_intervals = self._get_day_work_intervals( + current_datetime.date(), **call_args) + + if working_intervals: + new_working_intervals = self._interval_schedule_hours( + working_intervals, remaining_hours, backwards=backwards) + + res = timedelta() + for interval in working_intervals: + res += interval[1] - interval[0] + remaining_hours -= res.total_seconds() / 3600.0 + + intervals = intervals + new_working_intervals if \ + not backwards else new_working_intervals + intervals + # get next day + if backwards: + current_datetime = datetime.datetime.combine( + self._get_previous_work_day(current_datetime), + datetime.time(23, 59, 59)) + else: + current_datetime = datetime.datetime.combine( + self._get_next_work_day(current_datetime), + datetime.time()) + # avoid infinite loops + iterations += 1 + + return intervals + + if not hasattr(ResourceCalendar, '_schedule_days_original'): + ResourceCalendar._schedule_days_original = \ + ResourceCalendar._schedule_days + + ResourceCalendar._patch_method("_schedule_days", _new_schedule_days) + + if not hasattr(ResourceCalendar, '_schedule_hours_original'): + ResourceCalendar._schedule_hours_original = \ + ResourceCalendar._schedule_hours + + ResourceCalendar._patch_method("_schedule_hours", _new_schedule_hours) diff --git a/resource_calendar_schedule_iteration/model/__init__.py b/resource_calendar_schedule_iteration/model/__init__.py new file mode 100644 index 000000000..81793ddba --- /dev/null +++ b/resource_calendar_schedule_iteration/model/__init__.py @@ -0,0 +1 @@ +from . import resource_calendar diff --git a/resource_calendar_schedule_iteration/model/resource_calendar.py b/resource_calendar_schedule_iteration/model/resource_calendar.py new file mode 100644 index 000000000..605bffa1d --- /dev/null +++ b/resource_calendar_schedule_iteration/model/resource_calendar.py @@ -0,0 +1,22 @@ +# Copyright 2019 Eficent Business and IT Consulting Services S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +from odoo import api, models + + +class ResourceCalendar(models.Model): + _inherit = 'resource.calendar' + + @api.multi + def _get_schedule_days_iteration_limit(self): + self.ensure_one() + limit = self.env['ir.config_parameter'].sudo().get_param( + 'resource.calendar.schedule.days.iteration.limit', default=False) + return int(limit) or False + + @api.multi + def _get_schedule_hours_iteration_limit(self): + self.ensure_one() + limit = self.env['ir.config_parameter'].sudo().get_param( + 'resource.calendar.schedule.hours.iteration.limit', default=False) + return int(limit) or False diff --git a/resource_calendar_schedule_iteration/readme/CONFIGURE.rst b/resource_calendar_schedule_iteration/readme/CONFIGURE.rst new file mode 100644 index 000000000..471402c08 --- /dev/null +++ b/resource_calendar_schedule_iteration/readme/CONFIGURE.rst @@ -0,0 +1,5 @@ +#. Go to *Settings* and activate the developer mode. + +#. Go to *Settings / Technical / Parameters / System Parameters* and define, + new values for the parameters *resource.calendar.schedule.days.iteration.limit* + and/or *resource.calendar.schedule.hours.iteration.limit* diff --git a/resource_calendar_schedule_iteration/readme/CONTRIBUTORS.rst b/resource_calendar_schedule_iteration/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..149826cd3 --- /dev/null +++ b/resource_calendar_schedule_iteration/readme/CONTRIBUTORS.rst @@ -0,0 +1,4 @@ +* Eficent (https://www.eficent.com) + + * Jordi Ballester Alomar + * Lois Rilo Antelo diff --git a/resource_calendar_schedule_iteration/readme/DESCRIPTION.rst b/resource_calendar_schedule_iteration/readme/DESCRIPTION.rst new file mode 100644 index 000000000..46c4754a0 --- /dev/null +++ b/resource_calendar_schedule_iteration/readme/DESCRIPTION.rst @@ -0,0 +1,10 @@ +When you want to plan a number of days in the past or in the future considering +a working calendar, Odoo limits the number of days/hours that you can plan +ahead. + +In case that you want to plan days forward/backward, it currently restricts +to 100 iterations. + +This module allows you to increase the iteration limit used in the resource +calendar to schedule days or hours by means of a system parameter defined +by the administrator. diff --git a/resource_calendar_schedule_iteration/static/description/icon.png b/resource_calendar_schedule_iteration/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/resource_calendar_schedule_iteration/static/description/icon.png differ diff --git a/resource_calendar_schedule_iteration/static/description/index.html b/resource_calendar_schedule_iteration/static/description/index.html new file mode 100644 index 000000000..99c5db03a --- /dev/null +++ b/resource_calendar_schedule_iteration/static/description/index.html @@ -0,0 +1,440 @@ + + + + + + +Resource Calendar Schedule Iteration + + + +
+

Resource Calendar Schedule Iteration

+ + +

Beta License: LGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

+

When you want to plan a number of days in the past or in the future considering +a working calendar, Odoo limits the number of days/hours that you can plan +ahead.

+

In case that you want to plan days forward/backward, it currently restricts +to 100 iterations.

+

This module allows you to increase the iteration limit used in the resource +calendar to schedule days or hours by means of a system parameter defined +by the administrator.

+

Table of contents

+ +
+

Configuration

+
    +
  1. Go to Settings and activate the developer mode.
  2. +
  3. Go to Settings / Technical / Parameters / System Parameters and define, +new values for the parameters resource.calendar.schedule.days.iteration.limit +and/or resource.calendar.schedule.hours.iteration.limit
  4. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Eficent
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/server-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/resource_calendar_schedule_iteration/tests/__init__.py b/resource_calendar_schedule_iteration/tests/__init__.py new file mode 100644 index 000000000..b839f4dd5 --- /dev/null +++ b/resource_calendar_schedule_iteration/tests/__init__.py @@ -0,0 +1 @@ +from . import test_resource_calendar_schedule_iteration diff --git a/resource_calendar_schedule_iteration/tests/test_resource_calendar_schedule_iteration.py b/resource_calendar_schedule_iteration/tests/test_resource_calendar_schedule_iteration.py new file mode 100644 index 000000000..74566f0eb --- /dev/null +++ b/resource_calendar_schedule_iteration/tests/test_resource_calendar_schedule_iteration.py @@ -0,0 +1,46 @@ +# Copyright 2019 Eficent Business and IT Consulting Services S.L. +# (http://www.eficent.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from datetime import datetime, timedelta + +import odoo.tests.common as common + + +class TestResourceCalendarScheduleIteration(common.TransactionCase): + + def setUp(self): + super(TestResourceCalendarScheduleIteration, self).setUp() + + self.icp = self.env['ir.config_parameter'] + + self.calendar = self.env.ref('resource.resource_calendar_std') + self.icp.set_param( + "resource.calendar.schedule.days.iteration.limit", 200) + + def test_01_days_iteration(self): + days = 150 + calendar_day = self.calendar.plan_days(-1 * days - 1, datetime.today()) + aprox_date = datetime.today() - timedelta(days=days) + # Without more iteration limit the date returned will be only 100 + # days back using calendar (default iteration limit) instead of 150. + self.assertLess(calendar_day, aprox_date) + + def test_02_hours_iteration(self): + hours = 1500 * 8 + hours_2 = 1700 * 8 + limit_hour = self.calendar.plan_hours(-1 * hours - 1, datetime.today()) + limit_hour_2 = self.calendar.plan_hours( + -1 * hours_2 - 1, datetime.today()) + # Both hour computation exceeded the limit so they should be the + # same (which is incorrect). + self.assertEqual(limit_hour, limit_hour_2) + self.icp.set_param( + "resource.calendar.schedule.hours.iteration.limit", 2000) + correct_hour = self.calendar.plan_hours( + -1 * hours - 1, datetime.today()) + correct_hour_2 = self.calendar.plan_hours( + -1 * hours_2 - 1, datetime.today()) + self.assertNotEqual(correct_hour, correct_hour_2) + self.assertLess(correct_hour, limit_hour) + self.assertLess(correct_hour_2, limit_hour_2)