Skip to content

Commit

Permalink
[WIP] #6 - Module migration
Browse files Browse the repository at this point in the history
  • Loading branch information
tgaullier committed Jan 24, 2024
1 parent ddcde8d commit 7d7cd57
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 302 deletions.
File renamed without changes.
32 changes: 32 additions & 0 deletions reminder_summary_timesheet/__manifest__.py
Original file line number Diff line number Diff line change
@@ -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",
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<?xml version="1.0" ?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="cron_reminder_timesheet" model="ir.cron">
<field name="name">Reminder to fill timesheet</field>
<record forcecreate="True" id="cron_timesheet_reminder" model="ir.cron">
<field name="name">Daily reminder to fill timesheet</field>
<field name="model_id" ref="model_timesheet_reminder"/>
<field name="state">code</field>
<field name="code">model._cron_reminder()</field>
<field name="code">model._cron_timesheet_reminder()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field name="nextcall" eval="datetime.utcnow()" />
<!-- <field name="active" eval="False"/> -->
</record>

<record forcecreate="True" id="cron_timesheet_summary_manager" model="ir.cron">
<field name="name">Daily summary of time spent</field>
<field name="model_id" ref="model_timesheet_summary"/>
Expand Down
382 changes: 382 additions & 0 deletions reminder_summary_timesheet/i18n/fr.po

Large diffs are not rendered by default.

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -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)
8 changes: 8 additions & 0 deletions reminder_summary_timesheet/models/res_users.py
Original file line number Diff line number Diff line change
@@ -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)
60 changes: 60 additions & 0 deletions reminder_summary_timesheet/models/timesheet_reminder.py
Original file line number Diff line number Diff line change
@@ -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)
60 changes: 29 additions & 31 deletions ...reminder_mail/models/timesheet_summary.py → ...ary_timesheet/models/timesheet_summary.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,74 +1,72 @@
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' % (
self.env['ir.config_parameter'].sudo().get_param('web.base.url'),
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
Expand All @@ -83,36 +81,36 @@ 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:
projet = line.project_id.name
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]:
Expand All @@ -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
32 changes: 16 additions & 16 deletions timesheet_reminder_mail/views/hr_view.xml → reminder_summary_timesheet/views/hr_view.xml
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>

<record id="hr_employee_view_form_inherit_timesheet_reminder" model="ir.ui.view">
<field name="name">hr.employee.form.timesheet.reminder</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='timesheet']" position="inside">
<field name="ignore_timesheet_reminder" />
</xpath>
</field>
</record>

</data>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>

<record id="hr_employee_view_form_inherit_timesheet_reminder" model="ir.ui.view">
<field name="name">hr.employee.form.timesheet.reminder</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='application_group']" position="inside">
<field name="ignore_timesheet_reminder" />
</xpath>
</field>
</record>

</data>
</odoo>
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<data>
<!-- Email template for reminder to fill timesheet -->
<record id="reminder_timesheet_fill" model="mail.template">
<field name="name">Timesheet: Reminder to fill timesheet</field>
<field name="name">Timesheet: Daily reminder to fill timesheet</field>
<field name="model_id" ref="hr.model_hr_employee"/>
<field name="subject">Rappel pour saisir vos temps</field>
<field name="subject">Daily reminder to fill your timesheet</field>
<field name="email_from">${(object.user_id.company_id.partner_id.email_formatted or user.email_formatted) | safe}</field>
<field name="email_to">${object.work_email | safe}</field>
<field name="body_html" type="html">
Expand Down Expand Up @@ -36,16 +36,16 @@
<table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;">
<tr><td valign="top" style="font-size: 13px;">
<div>
Bonjour ${object.name},<br/><br/>
À l'heure actuelle, vous n'avez pas de temps "A valider" saisis pour aujourd'hui. Est-ce un oubli ou normal ?
Hello ${object.name},<br/><br/>
Currently, you do not have any "To be validated" timesheet lines entered for today. Is this an oversight or normal?
<br/>
% if ctx.get('action_url'):
<div style="margin: 16px 0px 16px 0px;">
<a href="${ctx.get('action_url')}"
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size: 13px;">Remplissez vos feuilles de temps</a>
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</a>
</div>
% endif
<br/>Merci,<br/>
<br/>Thank you,<br/>
% if user.signature
${user.signature | safe}
% endif
Expand Down Expand Up @@ -99,7 +99,7 @@

<!-- Ajout d'un template mail pour la synthèse des temps passés journalier à envoyer au manager -->
<record id="mail_template_timesheet_summary_manager" model="mail.template">
<field name="name">Timesheet - Daily summary of time spent</field>
<field name="name">Timesheet: Daily summary of time spent</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="email_from">${(object.company_id.partner_id.email_formatted or object.email_formatted) |safe}</field>
<field name="email_to">${object.work_email | safe}</field>
Expand Down
22 changes: 0 additions & 22 deletions timesheet_reminder_mail/__manifest__.py

This file was deleted.

Loading

0 comments on commit 7d7cd57

Please sign in to comment.