181 lines
9.3 KiB
Python
181 lines
9.3 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from urllib.parse import quote
|
|
|
|
from odoo import _, api, fields, models, tools
|
|
from odoo.exceptions import AccessError, ValidationError
|
|
|
|
|
|
class HrEmployee(models.Model):
|
|
_name = 'hr.employee'
|
|
_inherit = ['hr.employee', 'documents.mixin']
|
|
|
|
document_count = fields.Integer(compute='_compute_document_count', groups="hr.group_hr_user")
|
|
hr_employee_folder_id = fields.Many2one('documents.document', string="HR Employee Folder", groups="base.group_system,hr.group_hr_user")
|
|
hr_employee_contract_folder_id = fields.Many2one('documents.document', string="HR Employee Contract Folder", groups="base.group_system,hr.group_hr_user")
|
|
|
|
def _get_document_folder(self):
|
|
return self.hr_employee_folder_id if self.company_id.documents_hr_settings else False
|
|
|
|
def _get_document_partner(self):
|
|
return self.work_contact_id
|
|
|
|
def _check_create_documents(self):
|
|
return self.company_id.documents_hr_settings and super()._check_create_documents()
|
|
|
|
def _compute_document_count(self):
|
|
# FIX in 18.3, to remove when documents hr setting won't be optional anymore.
|
|
employees_without_document_settings = self.filtered(
|
|
lambda e: not e.company_id.documents_hr_settings
|
|
)
|
|
if employees_without_document_settings:
|
|
# Search everywhere if no employee folder configured.
|
|
document_count_by_partner = dict(self.env['documents.document']._read_group(
|
|
[('partner_id', 'in', employees_without_document_settings.work_contact_id.ids)],
|
|
groupby=['partner_id'], aggregates=['__count'],
|
|
))
|
|
for employee in employees_without_document_settings:
|
|
employee.document_count = document_count_by_partner.get(employee.work_contact_id, 0)
|
|
|
|
employees_with_document_settings = self - employees_without_document_settings
|
|
if not employees_with_document_settings:
|
|
return
|
|
|
|
document_count_by_folder = dict(self.env['documents.document']._read_group([
|
|
('id', 'child_of', employees_with_document_settings.hr_employee_folder_id.ids),
|
|
('type', '!=', 'folder')
|
|
], groupby=['folder_id'], aggregates=['__count']))
|
|
|
|
document_count_by_employee_folder = {
|
|
folder: sum(
|
|
count for doc_folder, count in document_count_by_folder.items()
|
|
if doc_folder.parent_path.startswith(folder.parent_path)
|
|
) for folder in employees_with_document_settings.hr_employee_folder_id
|
|
}
|
|
for employee in employees_with_document_settings:
|
|
employee.document_count = document_count_by_employee_folder.get(employee.hr_employee_folder_id, 0)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
employees = super().create(vals_list)
|
|
if not self.env.context.get('salary_simulation', False):
|
|
employees._generate_employee_documents_folders()
|
|
return employees
|
|
|
|
def write(self, vals):
|
|
result = super().write(vals)
|
|
if 'name' in vals and len(self) == 1:
|
|
# This makes no sense to rename multiple employees with the same name. This would probably be an error.
|
|
# So we rename the folder only if one employee is renamed at a time.
|
|
self.sudo().hr_employee_folder_id.write({'name': vals['name']})
|
|
return result
|
|
|
|
def action_open_documents(self):
|
|
""" Open and display all the content of the employee subfolder under HR > Employee. """
|
|
self.ensure_one()
|
|
if not self.work_contact_id:
|
|
raise ValidationError(_('You must have a contact linked to the employee in order to use Document\'s features.'))
|
|
action = self.env['ir.actions.actions']._for_xml_id('documents.document_action_preference')
|
|
# If setting not activated, use the old filter -> Search everywhere
|
|
if not self.company_id.documents_hr_settings:
|
|
action['context'] = {
|
|
'default_partner_id': self.work_contact_id.id,
|
|
'searchpanel_default_folder_id': False,
|
|
'default_res_id': self.id,
|
|
'default_res_model': 'hr.employee',
|
|
}
|
|
action['domain'] = [('partner_id', '=', self.work_contact_id.id)]
|
|
return action
|
|
if not self.hr_employee_folder_id:
|
|
raise ValidationError(_('You must configure the HR Employee folder in document settings to use Document\'s features.'))
|
|
if not self.env.user.has_groups('hr.group_hr_user'):
|
|
raise AccessError(_('You cannot access the employee\'s folder.'))
|
|
return {
|
|
'type': 'ir.actions.act_url',
|
|
'target': 'self',
|
|
# do not use the document's access_url, as it uses the web.base.url and could redirect on a domain you are not connected
|
|
# on if web.base.url points to a different domain than the one you are connected on (and web.base.url.freeze is set)
|
|
'url': f"/odoo/documents/{quote(self.hr_employee_folder_id.sudo().access_token, safe='')}",
|
|
}
|
|
|
|
def _generate_employee_documents_folders(self, skip_subfolders=False):
|
|
""" Employee document folder is meant to be used by HR only to store all the documents they need regarding the
|
|
employee (E.g.: ID Card, Drive License, etc..). The employee does not have access to this folder,
|
|
nor the documents inside it (by default at least) """
|
|
employees = self.filtered('company_id.documents_employee_folder_id')
|
|
folders = self.env["documents.document"].sudo().create([{
|
|
'name': employee.name,
|
|
'type': 'folder',
|
|
'folder_id': employee.company_id.documents_employee_folder_id.id,
|
|
'company_id': employee.company_id.id,
|
|
'access_internal': 'none',
|
|
'access_via_link': 'edit',
|
|
'is_access_via_link_hidden': True,
|
|
'owner_id': False,
|
|
} for employee in employees])
|
|
for employee, folder in zip(employees, folders):
|
|
employee.hr_employee_folder_id = folder.id
|
|
# create Contracts Subfolder
|
|
if not employee.hr_employee_contract_folder_id:
|
|
employee.hr_employee_contract_folder_id = self.env["documents.document"].sudo().create([{
|
|
'name': self.env._("Contracts"),
|
|
'type': 'folder',
|
|
'folder_id': employee.hr_employee_folder_id.id,
|
|
'company_id': employee.company_id.id,
|
|
}])
|
|
if not skip_subfolders:
|
|
employees._generate_employee_documents_subfolders()
|
|
|
|
def _generate_employee_documents_subfolders(self):
|
|
""" Generate subfolders under the employee folder, following what is specified on the company
|
|
-> res_config_settings.employee_subfolders
|
|
Removing a folder name from the setting should not delete the folder if it's not empty.
|
|
Existing folders should not be recreated.
|
|
Only newly added folder name should generate a new subfolder.
|
|
"""
|
|
Documents = self.env['documents.document']
|
|
create_subfolders_vals = []
|
|
subfolders_to_delete = Documents
|
|
|
|
subfolders = Documents.search([
|
|
('type', '=', 'folder'),
|
|
('folder_id', 'in', self.hr_employee_folder_id.ids),
|
|
('id', 'not in', self.hr_employee_contract_folder_id.ids)])
|
|
subfolders_by_employee_folder = subfolders.grouped('folder_id')
|
|
for company in self.company_id:
|
|
subfolder_names = [
|
|
name for name in company.employee_subfolders.split(',') if name
|
|
] if company.employee_subfolders else []
|
|
company_employees = self.filtered(lambda e: e.company_id == company and e.hr_employee_folder_id)
|
|
for employee in company_employees:
|
|
# Add new folders added to the list
|
|
existing_subfolders = subfolders_by_employee_folder.get(employee.hr_employee_folder_id, Documents)
|
|
added_subfolder_names = list(set(subfolder_names) - set(existing_subfolders.mapped('name')))
|
|
for subfolder_name in added_subfolder_names:
|
|
create_subfolders_vals.append({
|
|
'name': subfolder_name.strip(),
|
|
'type': 'folder',
|
|
'folder_id': employee.hr_employee_folder_id.id,
|
|
'company_id': company.id,
|
|
})
|
|
|
|
# Delete removed folder from the list.
|
|
# -> Side effect : folders created manually that are still empty will be deleted.
|
|
removed_subfolders_names = list(set(existing_subfolders.mapped('name')) - set(subfolder_names))
|
|
subfolders_to_delete |= existing_subfolders.filtered(
|
|
lambda f: f.name in removed_subfolders_names and not f.children_ids)
|
|
if create_subfolders_vals:
|
|
self.env["documents.document"].sudo().create(create_subfolders_vals)
|
|
subfolders_to_delete.unlink()
|
|
|
|
def _get_employee_documents_token(self):
|
|
self.ensure_one()
|
|
return tools.hmac(
|
|
self.env(su=True),
|
|
"documents-hr-my-files",
|
|
str(self.id),
|
|
)
|
|
|
|
def _get_documents_link_url(self):
|
|
self.ensure_one()
|
|
return f'{self.get_base_url()}/documents_hr/my_files/{self.id}/{self._get_employee_documents_token()}'
|