forked from Mapan/odoo17e
191 lines
9.5 KiB
Python
191 lines
9.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import date, datetime, timedelta, time
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models
|
|
import logging
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Company(models.Model):
|
|
_inherit = 'res.company'
|
|
|
|
# reminder for employees
|
|
timesheet_mail_employee_allow = fields.Boolean("Employee Reminder", default=True)
|
|
timesheet_mail_employee_delay = fields.Integer("Employee Reminder Days", default=1)
|
|
timesheet_mail_employee_interval = fields.Selection([
|
|
('weeks', 'after the end of the week'),
|
|
('months', 'after the end of the month')
|
|
], string='Employee Frequency', required=True, default="weeks")
|
|
timesheet_mail_employee_nextdate = fields.Datetime('Next scheduled date for employee reminder', readonly=True)
|
|
|
|
# reminder for approver
|
|
timesheet_mail_allow = fields.Boolean("Approver Reminder", default=True)
|
|
timesheet_mail_delay = fields.Integer("Approver Reminder Days", default=3)
|
|
timesheet_mail_interval = fields.Selection([
|
|
('weeks', 'after the end of the week'),
|
|
('months', 'after the end of the month')
|
|
], string='Approver Reminder Frequency', required=True, default="weeks")
|
|
timesheet_mail_nextdate = fields.Datetime('Next scheduled date for approver reminder', readonly=True)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
companies = super().create(vals_list)
|
|
for company, values in zip(companies, vals_list):
|
|
company.with_context(force_nextdates_calculation=True)._timesheet_postprocess(values)
|
|
return companies
|
|
|
|
def write(self, values):
|
|
result = super(Company, self).write(values)
|
|
self.with_context(force_nextdates_calculation=False)._timesheet_postprocess(values)
|
|
return result
|
|
|
|
def _timesheet_postprocess(self, values):
|
|
if (
|
|
values.get('timesheet_mail_employee_allow') or
|
|
self.env.context.get('force_nextdates_calculation') or
|
|
any(field_name in values for field_name in ['timesheet_mail_employee_delay', 'timesheet_mail_employee_interval'])
|
|
):
|
|
self._calculate_timesheet_mail_employee_nextdate()
|
|
if (
|
|
values.get('timesheet_mail_allow') or
|
|
self.env.context.get('force_nextdates_calculation') or
|
|
any(field_name in values for field_name in ['timesheet_mail_delay', 'timesheet_mail_interval'])
|
|
):
|
|
self._calculate_timesheet_mail_nextdate()
|
|
|
|
def _calculate_next_week_date(self, delay):
|
|
now = fields.Datetime.now()
|
|
nextdate = now + relativedelta(weeks=1, days=-now.weekday() + delay - 1)
|
|
if nextdate < now or nextdate.date() == now.date():
|
|
nextdate = now + relativedelta(weeks=2, days=-now.weekday() + delay - 1)
|
|
return nextdate
|
|
|
|
def _calculate_next_month_date(self, delay):
|
|
now = fields.Datetime.now()
|
|
nextdate = now + relativedelta(day=1, months=1, days=delay - 1)
|
|
if nextdate < now or nextdate.date() == now.date():
|
|
nextdate = now + relativedelta(day=1, months=2, days=delay - 1)
|
|
return nextdate
|
|
|
|
def _calculate_timesheet_mail_employee_nextdate(self):
|
|
for company in self:
|
|
delay = company.timesheet_mail_employee_delay
|
|
if company.timesheet_mail_employee_interval == 'weeks':
|
|
nextdate = self._calculate_next_week_date(delay)
|
|
else:
|
|
nextdate = self._calculate_next_month_date(delay)
|
|
company.timesheet_mail_employee_nextdate = fields.Datetime.to_string(nextdate)
|
|
|
|
def _calculate_timesheet_mail_nextdate(self):
|
|
for company in self:
|
|
delay = company.timesheet_mail_delay
|
|
if company.timesheet_mail_interval == 'weeks':
|
|
nextdate = self._calculate_next_week_date(delay)
|
|
else:
|
|
nextdate = self._calculate_next_month_date(delay)
|
|
company.timesheet_mail_nextdate = fields.Datetime.to_string(nextdate)
|
|
|
|
@api.model
|
|
def _cron_timesheet_reminder_employee(self):
|
|
""" Send an email reminder to the user having at least one timesheet since the last 3 month. From those ones, we exclude
|
|
ones having complete their timesheet (meaning timesheeted the same hours amount than their working calendar).
|
|
"""
|
|
today_max = fields.Datetime.to_string(datetime.combine(date.today(), time.max))
|
|
companies = self.search([('timesheet_mail_employee_allow', '=', True), ('timesheet_mail_employee_nextdate', '<', today_max)])
|
|
for company in companies:
|
|
if company.timesheet_mail_employee_nextdate < fields.Datetime.today():
|
|
_logger.warning('The cron "Timesheet: Employees Email Reminder" should have run on %s' % company.timesheet_mail_employee_nextdate)
|
|
|
|
# get the employee that have at least a timesheet for the last 3 months
|
|
# and that are still active; don't spam retired users
|
|
users = self.env['account.analytic.line'].search([
|
|
('date', '>=', fields.Date.to_string(date.today() - relativedelta(months=3))),
|
|
('date', '<=', fields.Date.today()),
|
|
('is_timesheet', '=', True),
|
|
('company_id', '=', company.id),
|
|
]).mapped('user_id').filtered('active')
|
|
|
|
# calculate the period
|
|
if company.timesheet_mail_employee_interval == 'months':
|
|
date_start = (date.today() - timedelta(days=company.timesheet_mail_employee_delay)) + relativedelta(day=1)
|
|
date_stop = date_start + relativedelta(months=1, days=-1)
|
|
else:
|
|
date_start = date.today() - timedelta(weeks=1, days=company.timesheet_mail_employee_delay - 1)
|
|
date_stop = date_start + timedelta(days=6)
|
|
|
|
date_start = fields.Date.to_string(date_start)
|
|
date_stop = fields.Date.to_string(date_stop)
|
|
|
|
# get the related employees timesheet status for the cron period
|
|
employees = self.env['hr.employee'].search([('company_id', '=', company.id), ('user_id', 'in', users.ids)])
|
|
work_hours_struct = employees.get_timesheet_and_working_hours(date_start, date_stop)
|
|
|
|
for employee in employees:
|
|
if employee.user_id and work_hours_struct[employee.id]['timesheet_hours'] < work_hours_struct[employee.id]['working_hours']:
|
|
self._cron_timesheet_send_reminder(
|
|
employee,
|
|
'timesheet_grid.mail_template_timesheet_reminder_user',
|
|
'hr_timesheet.act_hr_timesheet_line',
|
|
additionnal_values=work_hours_struct[employee.id],
|
|
)
|
|
|
|
# compute the next execution date
|
|
companies._calculate_timesheet_mail_employee_nextdate()
|
|
|
|
@api.model
|
|
def _cron_timesheet_reminder(self):
|
|
""" Send a email reminder to all users having the group 'timesheet approver'. """
|
|
today_max = fields.Datetime.to_string(datetime.combine(date.today(), time.max))
|
|
companies = self.search([('timesheet_mail_allow', '=', True), ('timesheet_mail_nextdate', '<', today_max)])
|
|
for company in companies:
|
|
if company.timesheet_mail_nextdate < fields.Datetime.today():
|
|
_logger.warning('The cron "Timesheet: Approver Email Reminder" should have run on %s', company.timesheet_mail_nextdate)
|
|
# calculate the period
|
|
if company.timesheet_mail_interval == 'months':
|
|
date_start = (date.today() - timedelta(days=company.timesheet_mail_delay)) + relativedelta(day=1)
|
|
date_stop = date_start + relativedelta(months=1, days=-1)
|
|
else:
|
|
date_start = date.today() - timedelta(weeks=1, days=company.timesheet_mail_delay - 1)
|
|
date_stop = date_start + timedelta(days=6)
|
|
|
|
date_start = fields.Date.to_string(date_start)
|
|
date_stop = fields.Date.to_string(date_stop)
|
|
|
|
values = {
|
|
'date_start': date_start,
|
|
'date_stop': date_stop,
|
|
}
|
|
users = self.env['res.users'].search([('groups_id', 'in', [self.env.ref('hr_timesheet.group_hr_timesheet_approver').id])])
|
|
self._cron_timesheet_send_reminder(
|
|
self.env['hr.employee'].search([('company_id', '=', company.id), ('user_id', 'in', users.ids)]),
|
|
'timesheet_grid.mail_template_timesheet_reminder',
|
|
'timesheet_grid.action_timesheet_previous_week',
|
|
additionnal_values=values)
|
|
|
|
# compute the next execution date
|
|
companies._calculate_timesheet_mail_nextdate()
|
|
|
|
@api.model
|
|
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.get_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.filtered('user_id'):
|
|
template.with_context(**template_ctx).send_mail(employee.id)
|