diff --git a/polln_shift/README.rst b/polln_shift/README.rst new file mode 100644 index 000000000..431469665 --- /dev/null +++ b/polln_shift/README.rst @@ -0,0 +1,53 @@ +==================== +Polln Shift Module +==================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-beescoop%2Fobeesdoo-lightgray.png?logo=github + :target: https://github.com/beescoop/obeesdoo/tree/12.0/polln_shift + :alt: beescoop/obeesdoo + +|badge1| |badge2| |badge3| + +Module with basic customizations for the Macavrac cooperative. + +**Table of contents** + +.. contents:: + :local: + +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 +~~~~~~~ + +* Patricia Daloze +* Thibault Francois + +Maintainers +~~~~~~~~~~~ + +This module is part of the `beescoop/obeesdoo `_ project on GitHub. + +You are welcome to contribute. diff --git a/polln_shift/__init__.py b/polln_shift/__init__.py new file mode 100644 index 000000000..38718f084 --- /dev/null +++ b/polln_shift/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import controllers \ No newline at end of file diff --git a/polln_shift/__manifest__.py b/polln_shift/__manifest__.py new file mode 100644 index 000000000..9fb0dee5f --- /dev/null +++ b/polln_shift/__manifest__.py @@ -0,0 +1,18 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Polln Shift Module", + "summary": """ + Module with basic customizations for the Polln cooperative. + """, + "author": "Patricia Daloze, Thibault Francois", + "category": "Sales", + "version": "12.0.1.0.0", + "depends": ["beesdoo_shift", "beesdoo_website_shift", "contacts", "beesdoo_easy_my_coop"], + "data": [ + # "data/mail_template.xml", + "views/res_partner.xml", + "views/shift.xml", + ], + "installable": True, + "license": "AGPL-3", +} diff --git a/polln_shift/models/__init__.py b/polln_shift/models/__init__.py new file mode 100644 index 000000000..8d6b8ab43 --- /dev/null +++ b/polln_shift/models/__init__.py @@ -0,0 +1,2 @@ +from . import res_partner +from . import planning \ No newline at end of file diff --git a/polln_shift/models/planning.py b/polln_shift/models/planning.py new file mode 100644 index 000000000..d61c6f25f --- /dev/null +++ b/polln_shift/models/planning.py @@ -0,0 +1,348 @@ +import re + +from datetime import datetime, timedelta + +from odoo import api, models, fields, _ +from odoo.addons.beesdoo_shift.models.cooperative_status import add_days_delta +from odoo.addons.beesdoo_shift.models.planning import float_to_time +from odoo.osv import expression + + +def time_to_float(t): + hour, minute = t.split(':') + return float(hour) + float(minute) / 60 + +class WizardSubscribe(models.TransientModel): + _inherit = 'beesdoo.shift.subscribe' + + def _get_mode(self): + partner = self.env["res.partner"].browse(self._context.get("active_id")) + return partner.working_mode or 'irregular' + + working_mode = fields.Selection(selection=[ + ("irregular", "worker"), + ("exempt", "Exempted"), + ], default=_get_mode) + + def subscribe(self): + res = super().subscribe() + if self.shift_id: + # Write the shift whatever the mode is + self.cooperator_id.sudo().write( + {"subscribed_shift_ids": [(6, 0, [self.shift_id.id])]} + ) + + # Should work with module beesdoo_easy_my_coop but don't + self.cooperator_id.eater = 'worker_eater' + return res + + +class Task(models.Model): + _inherit = 'beesdoo.shift.shift' + + _period = 28 + + def _get_selection_status(self): + return [ + ("open", _("Confirmed")), + ("done", _("Attended (+1)")), + ("absent", _("Absent (-1)")), + ("excused", _("Excused (+0)")), + ("cancel", _("Cancelled")), + ] + + def _get_counter_date_state_change(self, new_state): + """ + Return the cooperator_status of the cooperator that need to be + change and data that need to be change. It does not perform the + change directly. The cooperator_status will be changed by the + _change_counter function. + + Check has been done to ensure that worker is legitimate. + """ + data = {} + status = self.worker_id.cooperative_status_ids[0] + if new_state == "done": + data['sr'] = 1.0 + if new_state == "absent": + data['sr'] = -1.0 + return data, status + + def _get_final_state(self): + """ Disable constrains on shift that cannot be changed + """ + return [] + + +class CooperativeStatus(models.Model): + _inherit = 'cooperative.status' + + def _get_status(self): + return [ + ("ok", _("Up to Date")), + ("alert", _("Alerte")), + ("suspended", _("Suspended")), + ("unsubscribed", _("Unsubscribed")), + ("holiday", _("shift_status_holidays")), + ("exempted", _("Exempted")), + ("resigning", _("Resigning")), + ] + + sr = fields.Float(compute='_get_sr', inverse="_set_sr") + # alter table cooperative_status add column sr_store double precision; + # update cooperative_status set sr_store = sr; + sr_store = fields.Float() + future_alert_date = fields.Date(compute="_compute_future_alert_date") + next_countdown_date = fields.Date(compute="_compute_next_countdown_date") + + def _get_sr(self): + for record in self: + record.sr = record.sr_store + + def _set_sr(self): + for record in self: + record.sr_store = record.sr + + ######################################################## + # Method to override # + # To define the behavior of the status # + # # + # By default: everyone is always up to date # + ######################################################## + + ############################## + # Computed field section # + ############################## + def _next_countdown_date(self, irregular_start_date, today): + """ + Return the next countdown date given irregular_start_date and + today dates. + This does not take holiday and other status into account. + """ + + delta = (today - irregular_start_date).days + if not delta % self._period: + return today + return add_days_delta(today, self._period - (delta % self._period)) + + @api.depends( + "today", + "irregular_start_date", + "sr", + "holiday_start_time", + "holiday_end_time", + "temporary_exempt_start_date", + "temporary_exempt_end_date", + ) + def _compute_future_alert_date(self): + """Compute date before which the worker is up to date""" + for rec in self: + # Only for subscribed irregular worker + if ( + rec.working_mode != "irregular" + or not rec.irregular_start_date + ): + rec.future_alert_date = False + # Alert start time already set + elif rec.alert_start_time: + rec.future_alert_date = False + # Holidays are not set properly + elif bool(rec.holiday_start_time) != bool(rec.holiday_end_time): + rec.future_alert_date = False + # Exemption have not a start and end time + elif bool(rec.temporary_exempt_start_date) != bool( + rec.temporary_exempt_end_date + ): + rec.future_alert_date = False + else: + date = rec.today + counter = rec.sr + # Simulate the countdown + while counter > -2: + date = self._next_countdown_date( + rec.irregular_start_date, date + ) + # Check holidays + if ( + rec.holiday_start_time + and rec.holiday_end_time + and date >= rec.holiday_start_time + and date <= rec.holiday_end_time + ): + date = add_days_delta(date, 1) + continue + # Check temporary exemption + if ( + rec.temporary_exempt_start_date + and rec.temporary_exempt_end_date + and date >= rec.temporary_exempt_start_date + and date <= rec.temporary_exempt_end_date + ): + date = add_days_delta(date, 1) + continue + # Otherwise + date = add_days_delta(date, 1) + counter -= 1 + rec.future_alert_date = self._next_countdown_date( + rec.irregular_start_date, date + ) + + @api.depends( + "today", + "irregular_start_date", + "holiday_start_time", + "holiday_end_time", + "temporary_exempt_start_date", + "temporary_exempt_end_date", + ) + def _compute_next_countdown_date(self): + """ + Compute the following countdown date. This date is the date when + the worker will see his counter changed du to the cron. This + date is like the birthday date of the worker that occurred each + PERIOD. + """ + for rec in self: + # Only for irregular worker + if ( + rec.working_mode != "irregular" + and not rec.irregular_start_date + ): + rec.next_countdown_date = False + # Holidays are not set properly + elif bool(rec.holiday_start_time) != bool(rec.holiday_end_time): + rec.next_countdown_date = False + # Exemption have not a start and end time + elif bool(rec.temporary_exempt_start_date) != bool( + rec.temporary_exempt_end_date + ): + rec.next_countdown_date = False + else: + date = rec.today + next_countdown_date = False + while not next_countdown_date: + date = self._next_countdown_date( + rec.irregular_start_date, date + ) + # Check holidays + if ( + rec.holiday_start_time + and rec.holiday_end_time + and date >= rec.holiday_start_time + and date <= rec.holiday_end_time + ): + date = add_days_delta(date, 1) + continue + # Check temporary exemption + if ( + rec.temporary_exempt_start_date + and rec.temporary_exempt_end_date + and date >= rec.temporary_exempt_start_date + and date <= rec.temporary_exempt_end_date + ): + date = add_days_delta(date, 1) + continue + # Otherwise + next_countdown_date = date + rec.next_countdown_date = next_countdown_date + + ##################################### + # Status Change implementation # + ##################################### + + def _get_regular_status(self): + """ + Return the value of the status + for the regular worker + """ + # TODO change + ICP = self.env["ir.config_parameter"].sudo() + alert_count = int(ICP.get_param("alert_count", -2)) + unsubscribed_count = int(ICP.get_param("unsubscribed_count", -4)) + default_alert_time = int(ICP.get_param("alert_time", 28)) + if (self.temporary_exempt_start_date and self.temporary_exempt_end_date and + self.today >= self.temporary_exempt_start_date and self.today <= self.temporary_exempt_end_date): + return 'exempted' + if (self.holiday_start_time and self.holiday_end_time and + self.today >= self.holiday_start_time and self.today <= self.holiday_end_time): + return 'holiday' + if self.sr >= 0: + return 'ok' + + if self.sr <= unsubscribed_count: + return 'unsubscribed' + if self.sr <= alert_count and not self.alert_start_time: + return 'alert' + if self.alert_start_time: + if self.today < self.alert_start_time + timedelta(days=default_alert_time+self.time_extension): + return 'alert' + else: + return 'suspended' + return 'ok' + + def _get_irregular_status(self): + """ + Return the value of the status + for the irregular worker + """ + return self._get_regular_status() + + def _state_change(self, new_state): + """ + Hook to watch change in the state + """ + self.ensure_one() + if new_state == "unsubscribed" or new_state == "resigning": + # Remove worker from task_templates + self.cooperator_id.sudo().write( + {"subscribed_shift_ids": [(5, 0, 0)]} + ) + # Remove worker from supercoop in task_templates + task_tpls = self.env["beesdoo.shift.template"].search( + [("super_coop_id", "in", self.cooperator_id.user_ids.ids)] + ) + task_tpls.write({"super_coop_id": False}) + # Remove worker for future tasks (remove also supercoop) + self.env["beesdoo.shift.shift"].sudo().unsubscribe_from_today( + self.cooperator_id, now=fields.Datetime.now() + ) + if new_state == "alert": + self.write({"alert_start_time": self.today}) + + def _change_counter(self, data): + """ + Call when a shift state is changed + use data generated by _get_counter_date_state_change + """ + self.sr += data.get("sr", 0) + + + ############################################### + ###### Irregular Cron implementation ########## + ############################################### + + def _get_irregular_worker_domain(self, today): + """ + return the domain the give the list + of valid irregular worker that should + get their counter changed by the cron + """ + return [ + ("status", "not in", ["unsubscribed", "exempted", "resigning"]), + ("irregular_start_date", "!=", False), + ("working_mode", "!=", "exempt"), + ] + + def _change_irregular_counter(self): + """ + Define how the counter will change + for the irregular worker + where today - start_date is a multiple of the period + by default 28 days + """ + self.sr -= 1 + + + + +# TODO: nombre de coop nécessaire pour remplir le planning \ No newline at end of file diff --git a/polln_shift/models/res_partner.py b/polln_shift/models/res_partner.py new file mode 100644 index 000000000..3b0ec5e4f --- /dev/null +++ b/polln_shift/models/res_partner.py @@ -0,0 +1,25 @@ +from odoo import api, fields, models, _ + + +class Partner(models.Model): + _inherit = "res.partner" + + cooperator_type = fields.Selection(string="Cooperator Type", store=True, + selection="_get_share_type", + compute="_compute_cooperator_type", + ) + + @api.depends( + "share_ids", + "share_ids.share_product_id", + "share_ids.share_product_id.default_code", + "share_ids.share_number", + ) + def _compute_cooperator_type(self): + for partner in self: + share_type = "" + for line in partner.share_ids: + if line.share_number > 0: + share_type = line.share_product_id.default_code + break + partner.cooperator_type = share_type \ No newline at end of file diff --git a/polln_shift/readme/CONTRIBUTORS.rst b/polln_shift/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..bd002d0a7 --- /dev/null +++ b/polln_shift/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Thibault Francois +* Patricia Daloze \ No newline at end of file diff --git a/polln_shift/readme/DESCRIPTION.rst b/polln_shift/readme/DESCRIPTION.rst new file mode 100644 index 000000000..78589fbcb --- /dev/null +++ b/polln_shift/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Module with basic customizations for the Macavrac cooperative. diff --git a/polln_shift/views/res_partner.xml b/polln_shift/views/res_partner.xml new file mode 100644 index 000000000..bcbe14ac9 --- /dev/null +++ b/polln_shift/views/res_partner.xml @@ -0,0 +1,13 @@ + + + Polln Coop Form View + res.partner + + 99 + + + + + + + diff --git a/polln_shift/views/shift.xml b/polln_shift/views/shift.xml new file mode 100644 index 000000000..fab9bdaab --- /dev/null +++ b/polln_shift/views/shift.xml @@ -0,0 +1,126 @@ + + + Task Template Form Inherit + beesdoo.shift.template + + + + [("is_worker", "=", True)] + + + + + + Task Form inherit + + beesdoo.shift.shift + + + 1 + + + 1 + + + 1 + + + [('cooperative_status_ids.status', 'not in', ('unsubscribed', 'resigning')), ('cooperative_status_ids.working_mode', '=', 'irregular')] + + + + + + Coop Status Form View Inherit + + cooperative.status + + + + Start date + + + 1 + + + 1 + + + 1 + + + + + + Coop Status Tree View Inherit + + cooperative.status + + + 1 + + + + + + + + + Shift Kanban + beesdoo.shift.shift + + + + +

+
+
+
+ + + + Subscribe Cooperator + beesdoo.shift.subscribe + + + + 1 + + + 1 + + + + + + + + + + + + + Subscribe Cooperator + beesdoo.shift.subscribe + + + + + + + +
\ No newline at end of file