From 7d7cd57cd9fe400d8a69b0e2736e82fd835c2aec Mon Sep 17 00:00:00 2001 From: tgaullier Date: Wed, 24 Jan 2024 17:28:57 +0100 Subject: [PATCH] [WIP] #6 - Module migration --- .../__init__.py | 0 reminder_summary_timesheet/__manifest__.py | 32 ++ .../data/reminder_cron.xml | 8 +- reminder_summary_timesheet/i18n/fr.po | 382 ++++++++++++++++++ .../models/__init__.py | 0 .../models/hr_employee.py | 12 +- .../models/res_users.py | 8 + .../models/timesheet_reminder.py | 60 +++ .../models/timesheet_summary.py | 60 ++- .../static/description/icon.png | Bin .../views/hr_view.xml | 32 +- .../views/mail.xml | 14 +- timesheet_reminder_mail/__manifest__.py | 22 - timesheet_reminder_mail/i18n/fr.po | 147 ------- timesheet_reminder_mail/models/res_users.py | 9 - .../models/timesheet_reminder.py | 60 --- 16 files changed, 544 insertions(+), 302 deletions(-) rename {timesheet_reminder_mail => reminder_summary_timesheet}/__init__.py (100%) create mode 100644 reminder_summary_timesheet/__manifest__.py rename {timesheet_reminder_mail => reminder_summary_timesheet}/data/reminder_cron.xml (83%) create mode 100644 reminder_summary_timesheet/i18n/fr.po rename {timesheet_reminder_mail => reminder_summary_timesheet}/models/__init__.py (100%) mode change 100755 => 100644 rename {timesheet_reminder_mail => reminder_summary_timesheet}/models/hr_employee.py (85%) create mode 100644 reminder_summary_timesheet/models/res_users.py create mode 100644 reminder_summary_timesheet/models/timesheet_reminder.py rename {timesheet_reminder_mail => reminder_summary_timesheet}/models/timesheet_summary.py (67%) mode change 100755 => 100644 rename {timesheet_reminder_mail => reminder_summary_timesheet}/static/description/icon.png (100%) rename {timesheet_reminder_mail => reminder_summary_timesheet}/views/hr_view.xml (85%) mode change 100755 => 100644 rename {timesheet_reminder_mail => reminder_summary_timesheet}/views/mail.xml (94%) delete mode 100755 timesheet_reminder_mail/__manifest__.py delete mode 100644 timesheet_reminder_mail/i18n/fr.po delete mode 100644 timesheet_reminder_mail/models/res_users.py delete mode 100755 timesheet_reminder_mail/models/timesheet_reminder.py diff --git a/timesheet_reminder_mail/__init__.py b/reminder_summary_timesheet/__init__.py similarity index 100% rename from timesheet_reminder_mail/__init__.py rename to reminder_summary_timesheet/__init__.py diff --git a/reminder_summary_timesheet/__manifest__.py b/reminder_summary_timesheet/__manifest__.py new file mode 100644 index 0000000..9061509 --- /dev/null +++ b/reminder_summary_timesheet/__manifest__.py @@ -0,0 +1,32 @@ +{ + 'name': 'Reminders and summaries for timesheet lines', + 'version': '16.0.0.0', + 'category': 'HR', + 'summary': 'Remind employees to fill their timesheets and summarize them to managers', + 'description': """ +DESCRIPTION +----------- +This module sends an email notification to each user who hasn't provided at least one timesheet line and that is not in vacation. +In addition, it sends a daily summary to the manager. In addition, it sends a daily summary to managers. It is presented in the form of tables: + - timesheet lines of today + - timesheet lines of the past working day + - timesheet lines of current week + - timesheet lines of current month +""", + 'author': 'Nuxly', + 'website': 'https://www.nuxly.com', + 'depends' : [ + 'base', + 'hr_timesheet', + 'hr_holidays_public' + ], + 'data': [ + 'data/reminder_cron.xml', + 'views/mail.xml', + 'views/hr_view.xml', + ], + 'installable': True, + 'auto_install': True, + 'application': True, + "license": "AGPL-3", +} diff --git a/timesheet_reminder_mail/data/reminder_cron.xml b/reminder_summary_timesheet/data/reminder_cron.xml similarity index 83% rename from timesheet_reminder_mail/data/reminder_cron.xml rename to reminder_summary_timesheet/data/reminder_cron.xml index 773a665..49d3430 100644 --- a/timesheet_reminder_mail/data/reminder_cron.xml +++ b/reminder_summary_timesheet/data/reminder_cron.xml @@ -1,19 +1,19 @@ - - Reminder to fill timesheet + + Daily reminder to fill timesheet code - model._cron_reminder() + model._cron_timesheet_reminder() 1 days -1 - + Daily summary of time spent diff --git a/reminder_summary_timesheet/i18n/fr.po b/reminder_summary_timesheet/i18n/fr.po new file mode 100644 index 0000000..bad3de9 --- /dev/null +++ b/reminder_summary_timesheet/i18n/fr.po @@ -0,0 +1,382 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * reminder_summary_timesheet +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-03-20 09:49+0000\n" +"PO-Revision-Date: 2023-03-20 09:49+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: reminder_summary_timesheet +#: model:mail.template,body_html:reminder_summary_timesheet.reminder_timesheet_fill +msgid "" +"\n" +" \n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +" \n" +" \n" +"
\n" +" Your Timesheets
\n" +" \n" +" ${object.name}\n" +" \n" +"
\n" +" \"${object.company_id.name}\"\n" +"
\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +" Hello ${object.name},

\n" +" Currently, you do not have any \"To be validated\" timesheet lines entered for today. Is this an oversight or normal?\n" +"
\n" +" % if ctx.get('action_url'):\n" +" \n" +" % endif\n" +"
Thank you,
\n" +" % if user.signature\n" +" ${user.signature | safe}\n" +" % endif\n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +"
\n" +" ${object.company_id.name}\n" +"
\n" +" ${object.company_id.phone}\n" +" % if object.company_id.email\n" +" | ${object.company_id.email}\n" +" % endif\n" +" % if object.company_id.website\n" +" | \n" +" ${object.company_id.website}\n" +" \n" +" % endif\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +"
\n" +" Powered by Odoo\n" +"
\n" +"
\n" +" " +msgstr "" +"\n" +" \n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +" \n" +" \n" +"
\n" +" Vos lignes de feuilles de temps
\n" +" \n" +" ${object.name}\n" +" \n" +"
\n" +" \"${object.company_id.name}\"\n" +"
\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +" Bonjour ${object.name},

\n" +" À l'heure actuelle, vous n'avez pas de ligne de feuille de temps \"A valider\" saisis pour aujourd'hui. Est-ce un oubli ou normal ?\n" +"
\n" +" % if ctx.get('action_url'):\n" +" \n" +" % endif\n" +"
Merci,
\n" +" % if user.signature\n" +" ${user.signature | safe}\n" +" % endif\n" +"
\n" +"
\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +"
\n" +" ${object.company_id.name}\n" +"
\n" +" ${object.company_id.phone}\n" +" % if object.company_id.email\n" +" | ${object.company_id.email}\n" +" % endif\n" +" % if object.company_id.website\n" +" | \n" +" ${object.company_id.website}\n" +" \n" +" % endif\n" +"
\n" +"
\n" +"
\n" +" \n" +" \n" +"
\n" +" Powered by Odoo\n" +"
\n" +"
\n" +" " + +#. module: reminder_summary_timesheet +#: model:mail.template,body_html:reminder_summary_timesheet.mail_template_timesheet_summary_manager +msgid "" +"
\n" +"

Hello ${object.firstname or 'n/a'},

\n" +"


You will find below several tables summarizing the time spent by your employees.

\n" +" % set sum_analytic_lines = object.get_summarized_analytic_lines(object)\n" +" % for summary in sum_analytic_lines\n" +" % if summary[0]:\n" +" % set intervenants = summary[0][0]\n" +" % set projets = summary[0][1]\n" +" % set titre = summary[1]\n" +"
\n" +"

${titre}

\n" +"
\n" +"
\n" +"
Project | Intervenant
\n" +" % for intervenant in intervenants\n" +"
${intervenant}
\n" +" % endfor\n" +"
\n" +" % for nom_projet in projets\n" +" % if nom_projet == 'Total':\n" +"
\n" +"
${nom_projet}
\n" +" % for intervenant in intervenants\n" +" % if intervenant in projets[nom_projet]:\n" +"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" +" % else\n" +"
\n" +" % endif\n" +" % endfor\n" +"
\n" +" % elif (loop.index % 2) == 0:\n" +"
\n" +"
${nom_projet}
\n" +" % for intervenant in intervenants\n" +" % if intervenant in projets[nom_projet]:\n" +"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" +" % else\n" +"
\n" +" % endif\n" +" % endfor\n" +"
\n" +" % else\n" +"
\n" +"
${nom_projet}
\n" +" % for intervenant in intervenants\n" +" % if intervenant in projets[nom_projet]:\n" +"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" +" % else\n" +"
\n" +" % endif\n" +" % endfor\n" +"
\n" +" % endif\n" +" % endfor\n" +"

\n" +" % endif\n" +" % endfor\n" +"
Thank you,
\n" +" % if user.signature\n" +" ${user.signature | safe}\n" +" % endif\n" +"
\n" +" " +msgstr "" +"
\n" +"

Bonjour ${object.firstname or 'n/a'},

\n" +"


Tu trouveras ci-dessous plusieurs tableaux récapitulant le temps passé sur chaque projet par tes employés.

\n" +" % set sum_analytic_lines = object.get_summarized_analytic_lines(object)\n" +" % for summary in sum_analytic_lines\n" +" % if summary[0]:\n" +" % set intervenants = summary[0][0]\n" +" % set projets = summary[0][1]\n" +" % set titre = summary[1]\n" +"
\n" +"

${titre}

\n" +"
\n" +"
\n" +"
Projet | Intervenant
\n" +" % for intervenant in intervenants\n" +"
${intervenant}
\n" +" % endfor\n" +"
\n" +" % for nom_projet in projets\n" +" % if nom_projet == 'Total':\n" +"
\n" +"
${nom_projet}
\n" +" % for intervenant in intervenants\n" +" % if intervenant in projets[nom_projet]:\n" +"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" +" % else\n" +"
\n" +" % endif\n" +" % endfor\n" +"
\n" +" % elif (loop.index % 2) == 0:\n" +"
\n" +"
${nom_projet}
\n" +" % for intervenant in intervenants\n" +" % if intervenant in projets[nom_projet]:\n" +"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" +" % else\n" +"
\n" +" % endif\n" +" % endfor\n" +"
\n" +" % else\n" +"
\n" +"
${nom_projet}
\n" +" % for intervenant in intervenants\n" +" % if intervenant in projets[nom_projet]:\n" +"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" +" % else\n" +"
\n" +" % endif\n" +" % endfor\n" +"
\n" +" % endif\n" +" % endfor\n" +"

\n" +" % endif\n" +" % endfor\n" +"
Merci,
\n" +" % if user.signature\n" +" ${user.signature | safe}\n" +" % endif\n" +"
\n" +" " + +#. module: reminder_summary_timesheet +#: model:mail.template,subject:reminder_summary_timesheet.reminder_timesheet_fill +msgid "Daily reminder to fill your timesheet" +msgstr "Rappel pour saisir vos temps" + +#. module: reminder_summary_timesheet +#: model:ir.actions.server,name:reminder_summary_timesheet.cron_reminder_timesheet_ir_actions_server +#: model:ir.actions.server,name:reminder_summary_timesheet.cron_timesheet_reminder_ir_actions_server +#: model:ir.cron,cron_name:reminder_summary_timesheet.cron_reminder_timesheet +#: model:ir.cron,cron_name:reminder_summary_timesheet.cron_timesheet_reminder +msgid "Daily reminder to fill timesheet" +msgstr "Rappel quotidien pour remplir les lignes de feuille de temps" + +#. module: reminder_summary_timesheet +#: model:ir.actions.server,name:reminder_summary_timesheet.cron_timesheet_summary_manager_ir_actions_server +#: model:ir.cron,cron_name:reminder_summary_timesheet.cron_timesheet_summary_manager +#: model:mail.template,subject:reminder_summary_timesheet.mail_template_timesheet_summary_manager +msgid "Daily summary of time spent" +msgstr "Résumé quotidien des temps passés" + +#. module: reminder_summary_timesheet +#: model:ir.model.fields,help:reminder_summary_timesheet.field_hr_employee__ignore_timesheet_reminder +msgid "Do not send timesheet mail reminder if checked" +msgstr "Si la case est cochée, aucun rappel n'est envoyé pour la saisie de ligne de feuille de temps" + +#. module: reminder_summary_timesheet +#: model:ir.model.fields,field_description:reminder_summary_timesheet.field_hr_employee__ignore_timesheet_reminder +msgid "Ignore timesheet reminder" +msgstr "Ignorer le rappel sur la saisie de temps" + +#. module: reminder_summary_timesheet +#: model:mail.template,name:reminder_summary_timesheet.reminder_timesheet_fill +msgid "Timesheet: Daily reminder to fill timesheet" +msgstr "Feuille de temps : Rappel quotidien pour saisir des lignes" + +#. module: reminder_summary_timesheet +#: model:mail.template,name:reminder_summary_timesheet.mail_template_timesheet_summary_manager +msgid "Timesheet: Daily summary of time spent" +msgstr "Feuille de temps : Résumé quotidien des temps passés" + +#. module: reminder_summary_timesheet +#. odoo-python +#: code:addons/reminder_summary_timesheet/models/timesheet_summary.py:0 +#, python-format +msgid "Today summary" +msgstr "Temps du jour" + +#. module: reminder_summary_timesheet +#. odoo-python +#: code:addons/reminder_summary_timesheet/models/timesheet_summary.py:0 +#, python-format +msgid "Previous working day summary" +msgstr "Temps de la veille" + +#. module: reminder_summary_timesheet +#. odoo-python +#: code:addons/reminder_summary_timesheet/models/timesheet_summary.py:0 +#, python-format +msgid "Total of the week" +msgstr "Cumule de la semaine" + +#. module: reminder_summary_timesheet +#. odoo-python +#: code:addons/reminder_summary_timesheet/models/timesheet_summary.py:0 +#, python-format +msgid "Total of the month" +msgstr "Cumule du mois" diff --git a/timesheet_reminder_mail/models/__init__.py b/reminder_summary_timesheet/models/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from timesheet_reminder_mail/models/__init__.py rename to reminder_summary_timesheet/models/__init__.py diff --git a/timesheet_reminder_mail/models/hr_employee.py b/reminder_summary_timesheet/models/hr_employee.py similarity index 85% rename from timesheet_reminder_mail/models/hr_employee.py rename to reminder_summary_timesheet/models/hr_employee.py index c27eb0d..a528969 100644 --- a/timesheet_reminder_mail/models/hr_employee.py +++ b/reminder_summary_timesheet/models/hr_employee.py @@ -1,7 +1,7 @@ -from odoo import api, fields, models, _ - -class HrEmployeePrivate(models.Model): - _inherit = 'hr.employee' - - ignore_timesheet_reminder = fields.Boolean(string='Ignore timesheet reminder', store=True, +from odoo import fields, models, _ + +class HrEmployeePrivate(models.Model): + _inherit = 'hr.employee' + + ignore_timesheet_reminder = fields.Boolean(string='Ignore timesheet reminder', store=True, help="Do not send timesheet mail reminder if checked", groups="hr.group_hr_user", tracking=True) \ No newline at end of file diff --git a/reminder_summary_timesheet/models/res_users.py b/reminder_summary_timesheet/models/res_users.py new file mode 100644 index 0000000..c6e3559 --- /dev/null +++ b/reminder_summary_timesheet/models/res_users.py @@ -0,0 +1,8 @@ +from odoo import models, _ + +class HrEmployeePrivate(models.Model): + _inherit = "res.users" + + # Return the different time tables of a given manager + def get_summarized_analytic_lines(self, manager): + return self.env['timesheet.summary'].get_summarized_analytic_lines(manager) \ No newline at end of file diff --git a/reminder_summary_timesheet/models/timesheet_reminder.py b/reminder_summary_timesheet/models/timesheet_reminder.py new file mode 100644 index 0000000..7f293de --- /dev/null +++ b/reminder_summary_timesheet/models/timesheet_reminder.py @@ -0,0 +1,60 @@ +from datetime import datetime, date +import logging +from odoo import models + +_logger = logging.getLogger(__name__) + + +class TimesheetReminder(models.TransientModel): + _name = 'timesheet.reminder' + _description = "Alert each user who hasn't provided at least one timesheet line and that is not in vacation" + + # Checks that their timesheet lines have been correctly entered by the users + def _cron_timesheet_reminder(self): + holidays_public_line = self.env['hr.holidays.public.line'] + leave = self.env['hr.leave'] + timesheet_line = self.env['account.analytic.line'] + hr_employee = self.env['hr.employee'] + + today = date.today() + today_time = datetime.now() + + _logger.info("Starting the check of employee timesheet lines...") + # Checks if the day is a working day + if today.weekday() in [0, 1, 2, 3, 4]: + # Checks if the day is not a public holiday + holiday = holidays_public_line.search([]).filtered(lambda x: x.date == today) + if not holiday: + # Retrieves only employees who must write at least one timesheet line + employees = hr_employee.search([('ignore_timesheet_reminder', '=', False)]) + for employee in employees: + _logger.info("Check for '%s'.", employee.name) + # Checks if the employee is not on leave + on_leave = leave.search([('employee_id', '=', employee.id)]).filtered( + lambda x: x.number_of_days == 1.0 and x.date_from == today_time or x.date_from <= today_time <= x.date_to) + if not on_leave: + # Checks if the employee has written at least one timesheet line + lines = timesheet_line.search([('date', '=', today), ('employee_id', '=', employee.id)]) + if not lines: + # Preparing the reminder email + self._send_timesheet_reminder( + employee, + 'reminder_summary_timesheet.reminder_timesheet_fill', + 'hr_timesheet.act_hr_timesheet_line' + ) + _logger.info("End of check.") + + # Send an email timesheet line entry reminder to specified users + def _send_timesheet_reminder(self, employees, template_xmlid, action_xmlid, additionnal_values=None): + action_url = '%s/web#menu_id=%s&action=%s' % ( + self.env['ir.config_parameter'].sudo().get_param('web.base.url'), + self.env.ref('hr_timesheet.timesheet_menu_root').id, + self.env.ref(action_xmlid).id, + ) + template = self.env.ref(template_xmlid) + template_ctx = {'action_url': action_url} + if additionnal_values: + template_ctx.update(additionnal_values) + for employee in employees: + template.with_context(**template_ctx).send_mail(employee.id) + _logger.info("A timesheet line entry reminder has been sent to '%s'.", employee.name) diff --git a/timesheet_reminder_mail/models/timesheet_summary.py b/reminder_summary_timesheet/models/timesheet_summary.py old mode 100755 new mode 100644 similarity index 67% rename from timesheet_reminder_mail/models/timesheet_summary.py rename to reminder_summary_timesheet/models/timesheet_summary.py index 67d2abe..e2bf9ef --- a/timesheet_reminder_mail/models/timesheet_summary.py +++ b/reminder_summary_timesheet/models/timesheet_summary.py @@ -1,14 +1,16 @@ from datetime import datetime, date, timedelta from dateutil.relativedelta import relativedelta import logging -from odoo import models +from odoo import models, _ logger = logging.getLogger(__name__) -class Summary(models.TransientModel): +class TimesheetSummary(models.TransientModel): _name = 'timesheet.summary' + _description = "Daily summary of time spent for each manager" + # Send a summary of timesheet lines entered by employees def _cron_timesheet_summary_manager(self): managers = self.get_managers() action_url = '%s/web#menu_id=%s&action=%s' % ( @@ -16,59 +18,55 @@ def _cron_timesheet_summary_manager(self): self.env.ref('hr_timesheet.timesheet_menu_root').id, self.env.ref('hr_timesheet.act_hr_timesheet_line').id, ) - # send mail template to users having email address - template = self.env.ref('timesheet_reminder_mail.mail_template_timesheet_summary_manager') + template = self.env.ref('reminder_summary_timesheet.mail_template_timesheet_summary_manager') template_ctx = {'action_url': action_url} for manager in managers: template.with_context(template_ctx).send_mail(manager.id) logger.info("Summaries of time spent to send to the manager '%s'.", manager.name) - # Retoune les managers responsablent d'approuver les feuilles de temps + # Return managers responsible for approving timesheet lines def get_managers(self): - hr_employee = self.env['hr.employee'] managers = [] + logger.warning("====> ICI : %s", self.env['hr.employee']) - # Récupération des employés aillant un manager - employees = hr_employee.search([('timesheet_manager_id', "!=", False)]) - - # Parcours ces employés en sortant les managers - for employee in employees: + # Retrieves managers distinct from all employees + for employee in self.env['hr.employee'].search([('timesheet_manager_id', '!=', False)]): if employee['timesheet_manager_id'] not in managers: managers += employee['timesheet_manager_id'] return managers - # Retoune les différents tableaux de temps d'un manager + # Return the different summary tables of timesheet lines for a given manager def get_summarized_analytic_lines(self, manager): date_today = date.today() - # Récupération des temps du jour + # Retrieves the summary of today timesheet lines today = date_today.strftime("%Y-%m-%d") daily_times = self.get_analytic_lines(today, today, manager) - daily_times_summarized = self.summarize_analytic_lines(daily_times), "Temps du jour" + daily_times_summarized = self.summarize_analytic_lines(daily_times), _('Today summary') - # Récupération des temps de la veille ouvrée + # Retrieves the summary of the previous working day timesheet lines date_yesterday = date_today + relativedelta(days=-3) if date_today.weekday() == 0 else date_today + relativedelta(days=-1) yesterday = date_yesterday.strftime("%Y-%m-%d") yesterday_times = self.get_analytic_lines(yesterday, yesterday, manager) - yesterday_times_summarized = self.summarize_analytic_lines(yesterday_times), "Temps de la veille" + yesterday_times_summarized = self.summarize_analytic_lines(yesterday_times), _('Previous working day summary') - # Récupération des temps de la semaine + # Retrieves the summary of the current week timesheet lines date_monday = (date_today - timedelta(days=date_today.weekday()+2)).strftime("%Y-%m-%d") weekly_times = self.get_analytic_lines(date_monday, date_today, manager) - weekly_times_summarized = self.summarize_analytic_lines(weekly_times), "Cumule de la semaine" + weekly_times_summarized = self.summarize_analytic_lines(weekly_times), _('Total of the week') - # Récupération des temps du mois + # Retrieves the summary of the current month timesheet lines date_start_month = date(date_today.year, date_today.month, 1).strftime("%Y-%m-%d") monthly_times = self.get_analytic_lines(date_start_month, date_today, manager) - monthly_times_summarized = self.summarize_analytic_lines(monthly_times), "Cumule du mois" + monthly_times_summarized = self.summarize_analytic_lines(monthly_times), _('Total of the month') res = daily_times_summarized, yesterday_times_summarized, weekly_times_summarized, monthly_times_summarized return res - # Retourne les temps passés des employées en fonction d'un manager et d'un plage données + # Returns the timesheet lines of a given manager's employees based on a given range def get_analytic_lines(self, date_start, date_end, manager): employees = self.env['hr.employee'].search([ ("timesheet_manager_id", "=", manager.id @@ -83,15 +81,15 @@ def get_analytic_lines(self, date_start, date_end, manager): return aal - # Retourne une synthèse des temps passés + # Returns a summary of given timesheet lines def summarize_analytic_lines(self, aal): if aal: - # tpi : total par intervenant - # tpp : total par projet + # tpi : Total Per Intervener + # tpp : Total Per Project tpi = tpp = "Total" intervenants = [] projets = {} - # temps total des intervenants pour tous projets confondus + # Total time of all projects combined for each interveners tpi_dict = {tpi: {tpp: 0}} for line in aal: @@ -99,20 +97,20 @@ def summarize_analytic_lines(self, aal): intervenant = line.employee_id.user_partner_id.firstname temps = line.unit_amount - # Ajout de l'intervenant dans le tableau "intervenants" s'il n'existe pas déjà + # Adding the intervener to the "interveners" table if it is not already entered if intervenant not in intervenants: intervenants.append(intervenant) - # Ajout de le projet dans le tableau "projets" s'il n'existe pas déjà + # Adding the project to the "projects" table if it is not already entered if projet not in projets: projets[projet] = {} projets[projet][tpp] = 0 - # Si l’intervenant est déjà sur le projet du tableau "projets" alors on additionne le temps passé au temps déjà renseigné + # If the intervener is already on the project in the "projects" table then we add the time spent to the time already entered if intervenant in projets[projet]: projets[projet][intervenant] += temps tpi_dict[tpi][intervenant] += temps projets[projet][tpp] += temps tpi_dict[tpi][tpp] += temps - # Sinon l’intervenant est ajouté sur le projet du tableau "projets" et son temps passé est renseigné + # Otherwise the intervener is added to the project in the “projects” table and their time spent is entered else: projets[projet][intervenant] = temps if intervenant not in tpi_dict[tpi]: @@ -122,9 +120,9 @@ def summarize_analytic_lines(self, aal): projets[projet][tpp] += temps tpi_dict[tpi][tpp] += temps - # Ajout d'un intervenant "Total" + # Adding a “Total” intervener intervenants.append(tpp) - # Ajout d'une ligne de temps total des intervenants pour tous projets confondus au tableau des projets + # Adding a total time line of participants for all projects combined to the “projects” table projets.update(tpi_dict) res = intervenants, projets return res diff --git a/timesheet_reminder_mail/static/description/icon.png b/reminder_summary_timesheet/static/description/icon.png similarity index 100% rename from timesheet_reminder_mail/static/description/icon.png rename to reminder_summary_timesheet/static/description/icon.png diff --git a/timesheet_reminder_mail/views/hr_view.xml b/reminder_summary_timesheet/views/hr_view.xml old mode 100755 new mode 100644 similarity index 85% rename from timesheet_reminder_mail/views/hr_view.xml rename to reminder_summary_timesheet/views/hr_view.xml index a9a7317..3bfd1f8 --- a/timesheet_reminder_mail/views/hr_view.xml +++ b/reminder_summary_timesheet/views/hr_view.xml @@ -1,17 +1,17 @@ - - - - - - hr.employee.form.timesheet.reminder - hr.employee - - - - - - - - - + + + + + + hr.employee.form.timesheet.reminder + hr.employee + + + + + + + + + \ No newline at end of file diff --git a/timesheet_reminder_mail/views/mail.xml b/reminder_summary_timesheet/views/mail.xml similarity index 94% rename from timesheet_reminder_mail/views/mail.xml rename to reminder_summary_timesheet/views/mail.xml index e3116b6..44ac8a0 100644 --- a/timesheet_reminder_mail/views/mail.xml +++ b/reminder_summary_timesheet/views/mail.xml @@ -3,9 +3,9 @@ - Timesheet: Reminder to fill timesheet + Timesheet: Daily reminder to fill timesheet - Rappel pour saisir vos temps + Daily reminder to fill your timesheet ${(object.user_id.company_id.partner_id.email_formatted or user.email_formatted) | safe} ${object.work_email | safe} @@ -36,16 +36,16 @@
- Bonjour ${object.name},

- À l'heure actuelle, vous n'avez pas de temps "A valider" saisis pour aujourd'hui. Est-ce un oubli ou normal ? + Hello ${object.name},

+ Currently, you do not have any "To be validated" timesheet lines entered for today. Is this an oversight or normal?
% if ctx.get('action_url'):
Remplissez vos feuilles de temps + style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size: 13px;">Fill out your timesheet lines
% endif -
Merci,
+
Thank you,
% if user.signature ${user.signature | safe} % endif @@ -99,7 +99,7 @@ - Timesheet - Daily summary of time spent + Timesheet: Daily summary of time spent ${(object.company_id.partner_id.email_formatted or object.email_formatted) |safe} ${object.work_email | safe} diff --git a/timesheet_reminder_mail/__manifest__.py b/timesheet_reminder_mail/__manifest__.py deleted file mode 100755 index f58f065..0000000 --- a/timesheet_reminder_mail/__manifest__.py +++ /dev/null @@ -1,22 +0,0 @@ -{ - 'name': 'Reminder_timesheet', - 'version': '14.0.0.0', - 'category': 'HR', - 'summary': 'Module to reminder timesheet', - 'description': """ -DESCRIPTION ------------ -This module send a notificaton by mail to each user doesn't wrote at least one of time on timesheet. -""", - 'author': 'Nuxly', - 'website': 'https://www.nuxly.com', - 'depends' : ['base', 'hr_timesheet', 'hr_holidays_public'], - 'data': [ - 'data/reminder_cron.xml', - 'views/mail.xml', - 'views/hr_view.xml', - ], - 'installable': True, - 'auto_install': True, - 'application': True, -} diff --git a/timesheet_reminder_mail/i18n/fr.po b/timesheet_reminder_mail/i18n/fr.po deleted file mode 100644 index 5499b46..0000000 --- a/timesheet_reminder_mail/i18n/fr.po +++ /dev/null @@ -1,147 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * timesheet_reminder_mail -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 14.0+e\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-03-20 09:49+0000\n" -"PO-Revision-Date: 2023-03-20 09:49+0000\n" -"Last-Translator: \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: timesheet_reminder_mail -#: model:mail.template,body_html:timesheet_reminder_mail.mail_template_timesheet_summary_manager -msgid "" -"
\n" -"

Hello ${object.firstname or 'n/a'},

\n" -"


You will find below several tables summarizing the time spent by your employees.

\n" -" % set sum_analytic_lines = object.get_summarized_analytic_lines(object)\n" -" % for summary in sum_analytic_lines\n" -" % if summary[0]:\n" -" % set intervenants = summary[0][0]\n" -" % set projets = summary[0][1]\n" -" % set titre = summary[1]\n" -"
\n" -"

${titre}

\n" -"
\n" -"
\n" -"
Project | Intervenant
\n" -" % for intervenant in intervenants\n" -"
${intervenant}
\n" -" % endfor\n" -"
\n" -" % for nom_projet in projets\n" -" % if nom_projet == 'Total':\n" -"
\n" -"
${nom_projet}
\n" -" % for intervenant in intervenants\n" -" % if intervenant in projets[nom_projet]:\n" -"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" -" % else\n" -"
\n" -" % endif\n" -" % endfor\n" -"
\n" -" % elif (loop.index % 2) == 0:\n" -"
\n" -"
${nom_projet}
\n" -" % for intervenant in intervenants\n" -" % if intervenant in projets[nom_projet]:\n" -"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" -" % else\n" -"
\n" -" % endif\n" -" % endfor\n" -"
\n" -" % else\n" -"
\n" -"
${nom_projet}
\n" -" % for intervenant in intervenants\n" -" % if intervenant in projets[nom_projet]:\n" -"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" -" % else\n" -"
\n" -" % endif\n" -" % endfor\n" -"
\n" -" % endif\n" -" % endfor\n" -"

\n" -" % endif\n" -" % endfor\n" -"
Thank you,
\n" -" % if user.signature\n" -" ${user.signature | safe}\n" -" % endif\n" -"
\n" -" " -msgstr "" -"
\n" -"

Bonjour ${object.firstname or 'n/a'},

\n" -"


Tu trouveras ci-dessous plusieurs tableaux récapitulant le temps passé sur chaque projet par tes employés.

\n" -" % set sum_analytic_lines = object.get_summarized_analytic_lines(object)\n" -" % for summary in sum_analytic_lines\n" -" % if summary[0]:\n" -" % set intervenants = summary[0][0]\n" -" % set projets = summary[0][1]\n" -" % set titre = summary[1]\n" -"
\n" -"

${titre}

\n" -"
\n" -"
\n" -"
Projet | Intervenant
\n" -" % for intervenant in intervenants\n" -"
${intervenant}
\n" -" % endfor\n" -"
\n" -" % for nom_projet in projets\n" -" % if nom_projet == 'Total':\n" -"
\n" -"
${nom_projet}
\n" -" % for intervenant in intervenants\n" -" % if intervenant in projets[nom_projet]:\n" -"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" -" % else\n" -"
\n" -" % endif\n" -" % endfor\n" -"
\n" -" % elif (loop.index % 2) == 0:\n" -"
\n" -"
${nom_projet}
\n" -" % for intervenant in intervenants\n" -" % if intervenant in projets[nom_projet]:\n" -"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" -" % else\n" -"
\n" -" % endif\n" -" % endfor\n" -"
\n" -" % else\n" -"
\n" -"
${nom_projet}
\n" -" % for intervenant in intervenants\n" -" % if intervenant in projets[nom_projet]:\n" -"
${'{:.2f}'.format(projets[nom_projet][intervenant]).replace('.',',')}
\n" -" % else\n" -"
\n" -" % endif\n" -" % endfor\n" -"
\n" -" % endif\n" -" % endfor\n" -"

\n" -" % endif\n" -" % endfor\n" -"
Merci,
\n" -" % if user.signature\n" -" ${user.signature | safe}\n" -" % endif\n" -"
\n" -" " diff --git a/timesheet_reminder_mail/models/res_users.py b/timesheet_reminder_mail/models/res_users.py deleted file mode 100644 index 927b01d..0000000 --- a/timesheet_reminder_mail/models/res_users.py +++ /dev/null @@ -1,9 +0,0 @@ -from odoo import api, fields, models, _ - -class HrEmployeePrivate(models.Model): - _inherit = "res.users" - - # Retoune les différents tableaux de temps d'un manager - def get_summarized_analytic_lines(self, manager): - summary = self.env['timesheet.summary'] - return summary.get_summarized_analytic_lines(manager) \ No newline at end of file diff --git a/timesheet_reminder_mail/models/timesheet_reminder.py b/timesheet_reminder_mail/models/timesheet_reminder.py deleted file mode 100755 index 456a425..0000000 --- a/timesheet_reminder_mail/models/timesheet_reminder.py +++ /dev/null @@ -1,60 +0,0 @@ -from datetime import datetime, date -import logging -from odoo import models - -_logger = logging.getLogger(__name__) - - -class Reminder(models.TransientModel): - _name = 'timesheet.reminder' - _description = "Alert each user that doesn't wrote at least one line of time on timesheet" - - def _cron_reminder(self): - public_holiday = self.env['hr.holidays.public.line'] - leave = self.env['hr.leave'] - timesheet = self.env['account.analytic.line'] - hr_employee = self.env['hr.employee'] - - today = date.today() - today_time = datetime.now() - - # Checking working day (not weekend) - if today.weekday() in [0, 1, 2, 3, 4]: - # checking if day is a holiday - holiday = public_holiday.search([]).filtered(lambda x: x.date == today) - if not holiday: - # Check for all employees, if each one write at least one line of timesheet - employees = hr_employee.search([('ignore_timesheet_reminder', '=', False)]) - for employee in employees: - _logger.info("Check '%s'.", employee.name) - # Check if each one doesn't on leave - on_leave = leave.search([('employee_id', '=', employee.id)]).filtered(lambda - x: x.number_of_days == 1.0 and x.date_from == today_time or x.date_from <= today_time <= x.date_to) - if not on_leave: - # Check if each one write at least one line of timesheet, or do a alert - lines = timesheet.search([('date', '=', today), ('employee_id', '=', employee.id), ('state', '!=', 'draft')]) - if not lines: - self._cron_timesheet_send_reminder( - employee, - 'timesheet_reminder_mail.reminder_timesheet_fill', - 'hr_timesheet.act_hr_timesheet_line' - ) - - def _cron_timesheet_send_reminder(self, employees, template_xmlid, action_xmlid, additionnal_values=None): - """ Send the email reminder to specified users - :param user_ids : list of user identifier to send the reminder - :param template_xmlid : xml id of the reminder mail template - """ - action_url = '%s/web#menu_id=%s&action=%s' % ( - self.env['ir.config_parameter'].sudo().get_param('web.base.url'), - self.env.ref('hr_timesheet.timesheet_menu_root').id, - self.env.ref(action_xmlid).id, - ) - # send mail template to users having email address - template = self.env.ref(template_xmlid) - template_ctx = {'action_url': action_url} - if additionnal_values: - template_ctx.update(additionnal_values) - for employee in employees: - template.with_context(**template_ctx).send_mail(employee.id) - _logger.info("A reminder sent to user '%s'.", employee.name)