144 lines
6.6 KiB
Python
144 lines
6.6 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, models, fields, Command, _
|
|
from odoo.addons.resource.models.utils import filter_domain_leaf
|
|
from odoo.fields import Domain
|
|
|
|
|
|
class PlanningSend(models.TransientModel):
|
|
_name = 'planning.send'
|
|
_inherit = ['hr.mixin']
|
|
_description = "Send Planning"
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
res = super().default_get(fields)
|
|
if 'slot_ids' in res and 'employee_ids' in fields:
|
|
employees = self.env['planning.slot'].browse(res['slot_ids'][0][2]).employee_id
|
|
res['employee_ids'] = [Command.set(employees.sudo().sorted('name').ids)]
|
|
return res
|
|
|
|
start_datetime = fields.Datetime("Period", required=True)
|
|
end_datetime = fields.Datetime("Stop Date", required=True)
|
|
include_unassigned = fields.Boolean("Include Open Shifts", default=True)
|
|
note = fields.Text("Extra Message", help="Additional message displayed in the email sent to employees")
|
|
employee_ids = fields.Many2many('hr.employee', string="Employees", compute_sudo=True,
|
|
compute='_compute_slots_data', inverse='_inverse_employee_ids', store=True)
|
|
slot_ids = fields.Many2many('planning.slot', compute='_compute_slots_data', store=True, export_string_translation=False)
|
|
employees_no_email = fields.Many2many('hr.employee', string="Employees without email", compute_sudo=True,
|
|
compute="_compute_employees_no_email", inverse="_inverse_employees_no_email", export_string_translation=False)
|
|
|
|
def _get_slot_domain(self):
|
|
self.ensure_one()
|
|
|
|
# Our datetime conditions (these will override any from active_domain)
|
|
datetime_domain = [
|
|
('start_datetime', '>=', self.start_datetime),
|
|
('end_datetime', '<=', self.end_datetime)
|
|
]
|
|
|
|
active_domain = self.env.context.get('active_domain', [])
|
|
|
|
if not active_domain:
|
|
return datetime_domain
|
|
|
|
# Remove conditions with 'start_datetime' or 'end_datetime' where the value is not False,
|
|
cleaned_active_domain = filter_domain_leaf(active_domain, lambda field: field not in ['start_datetime', 'end_datetime'])
|
|
|
|
# Combine domains: cleaned active_domain + our datetime
|
|
return Domain.AND([cleaned_active_domain, datetime_domain])
|
|
|
|
@api.depends('start_datetime', 'end_datetime')
|
|
def _compute_slots_data(self):
|
|
for wiz in self:
|
|
wiz.slot_ids = self.env['planning.slot'].with_user(self.env.user).search(self._get_slot_domain(), order='employee_id')
|
|
wiz.employee_ids = wiz.slot_ids.filtered(lambda s: s.resource_type == 'user').mapped('employee_id')
|
|
|
|
def _inverse_employee_ids(self):
|
|
for wiz in self:
|
|
wiz.slot_ids = self.env['planning.slot'].with_user(self.env.user).search(self._get_slot_domain())
|
|
|
|
@api.depends('employee_ids')
|
|
def _compute_employees_no_email(self):
|
|
for planning in self:
|
|
planning.employees_no_email = planning.employee_ids.filtered(lambda employee: not employee.work_email)
|
|
|
|
def _inverse_employees_no_email(self):
|
|
for planning in self:
|
|
planning.employee_ids = planning.employees_no_email + planning.employee_ids.filtered('work_email')
|
|
|
|
def get_employees_without_work_email(self):
|
|
self.ensure_one()
|
|
if not self.employee_ids.has_access('write'):
|
|
return None
|
|
employee_ids_without_work_email = self.employee_ids.filtered(lambda employee: not employee.work_email).ids
|
|
if not employee_ids_without_work_email:
|
|
return None
|
|
context = dict(self.env.context, force_email=True, form_view_ref='planning.hr_employee_view_form_simplified')
|
|
return {
|
|
'relation': 'hr.employee',
|
|
'res_ids': employee_ids_without_work_email,
|
|
'context': context,
|
|
}
|
|
|
|
def action_check_emails(self):
|
|
if self.employees_no_email and self.employee_ids.has_access('write'):
|
|
return {
|
|
'name': _('No Email Address for Some Employees'),
|
|
'view_mode': 'form',
|
|
'res_model': 'planning.send',
|
|
'views': [(self.env.ref('planning.employee_no_email_list_wizard').id, 'form')],
|
|
'type': 'ir.actions.act_window',
|
|
'res_id': self.id,
|
|
'target': 'new',
|
|
}
|
|
return self.action_send()
|
|
|
|
def action_send(self):
|
|
employees = self.employee_ids - self.employees_no_email
|
|
if self.include_unassigned:
|
|
slot_to_send = self.slot_ids.filtered(lambda s: not s.employee_id or s.employee_id in employees)
|
|
else:
|
|
slot_to_send = self.slot_ids.filtered(lambda s: s.employee_id in employees)
|
|
if not slot_to_send:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'type': 'danger',
|
|
'message': _("The shifts have already been published, or there are no shifts to publish."),
|
|
}
|
|
}
|
|
|
|
planning = self.env['planning.planning']._get_preview_planning(self.start_datetime, self.end_datetime, self.include_unassigned)
|
|
planning.is_planning_preview = False
|
|
planning.include_unassigned = self.include_unassigned
|
|
|
|
slot_employees = slot_to_send.mapped('employee_id')
|
|
open_slots = slot_to_send.filtered(lambda s: not s.employee_id and not s.is_past)
|
|
employees_to_send = self.env['hr.employee']
|
|
for employee in employees:
|
|
if employee in slot_employees:
|
|
employees_to_send |= employee
|
|
else:
|
|
for slot in open_slots:
|
|
if not employee.planning_role_ids or not slot.role_id or slot.role_id in employee.planning_role_ids:
|
|
employees_to_send |= employee
|
|
res = planning._send_planning(slots=slot_to_send, message=self.note, employees=employees_to_send)
|
|
if res:
|
|
if self.employees_no_email:
|
|
message = _("Shifts published — employees without a work email were skipped")
|
|
notification_type = "info"
|
|
else:
|
|
message = _("Schedule sent to your employees")
|
|
notification_type = "success"
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'type': notification_type,
|
|
'message': message,
|
|
'next': {'type': 'ir.actions.act_window_close'},
|
|
}
|
|
}
|