1# -*- coding: utf-8 -*- 2# Part of Odoo. See LICENSE file for full copyright and licensing details. 3 4from odoo import api, models, fields, _, SUPERUSER_ID 5from odoo.exceptions import AccessError 6 7 8class User(models.Model): 9 _inherit = ['res.users'] 10 11 # note: a user can only be linked to one employee per company (see sql constraint in ´hr.employee´) 12 employee_ids = fields.One2many('hr.employee', 'user_id', string='Related employee') 13 employee_id = fields.Many2one('hr.employee', string="Company employee", 14 compute='_compute_company_employee', search='_search_company_employee', store=False) 15 16 job_title = fields.Char(related='employee_id.job_title', readonly=False, related_sudo=False) 17 work_phone = fields.Char(related='employee_id.work_phone', readonly=False, related_sudo=False) 18 mobile_phone = fields.Char(related='employee_id.mobile_phone', readonly=False, related_sudo=False) 19 employee_phone = fields.Char(related='employee_id.phone', readonly=False, related_sudo=False) 20 work_email = fields.Char(related='employee_id.work_email', readonly=False, related_sudo=False) 21 category_ids = fields.Many2many(related='employee_id.category_ids', string="Employee Tags", readonly=False, related_sudo=False) 22 department_id = fields.Many2one(related='employee_id.department_id', readonly=False, related_sudo=False) 23 address_id = fields.Many2one(related='employee_id.address_id', readonly=False, related_sudo=False) 24 work_location = fields.Char(related='employee_id.work_location', readonly=False, related_sudo=False) 25 employee_parent_id = fields.Many2one(related='employee_id.parent_id', readonly=False, related_sudo=False) 26 coach_id = fields.Many2one(related='employee_id.coach_id', readonly=False, related_sudo=False) 27 address_home_id = fields.Many2one(related='employee_id.address_home_id', readonly=False, related_sudo=False) 28 is_address_home_a_company = fields.Boolean(related='employee_id.is_address_home_a_company', readonly=False, related_sudo=False) 29 private_email = fields.Char(related='address_home_id.email', string="Private Email", readonly=False) 30 km_home_work = fields.Integer(related='employee_id.km_home_work', readonly=False, related_sudo=False) 31 # res.users already have a field bank_account_id and country_id from the res.partner inheritance: don't redefine them 32 employee_bank_account_id = fields.Many2one(related='employee_id.bank_account_id', string="Employee's Bank Account Number", related_sudo=False, readonly=False) 33 employee_country_id = fields.Many2one(related='employee_id.country_id', string="Employee's Country", readonly=False, related_sudo=False) 34 identification_id = fields.Char(related='employee_id.identification_id', readonly=False, related_sudo=False) 35 passport_id = fields.Char(related='employee_id.passport_id', readonly=False, related_sudo=False) 36 gender = fields.Selection(related='employee_id.gender', readonly=False, related_sudo=False) 37 birthday = fields.Date(related='employee_id.birthday', readonly=False, related_sudo=False) 38 place_of_birth = fields.Char(related='employee_id.place_of_birth', readonly=False, related_sudo=False) 39 country_of_birth = fields.Many2one(related='employee_id.country_of_birth', readonly=False, related_sudo=False) 40 marital = fields.Selection(related='employee_id.marital', readonly=False, related_sudo=False) 41 spouse_complete_name = fields.Char(related='employee_id.spouse_complete_name', readonly=False, related_sudo=False) 42 spouse_birthdate = fields.Date(related='employee_id.spouse_birthdate', readonly=False, related_sudo=False) 43 children = fields.Integer(related='employee_id.children', readonly=False, related_sudo=False) 44 emergency_contact = fields.Char(related='employee_id.emergency_contact', readonly=False, related_sudo=False) 45 emergency_phone = fields.Char(related='employee_id.emergency_phone', readonly=False, related_sudo=False) 46 visa_no = fields.Char(related='employee_id.visa_no', readonly=False, related_sudo=False) 47 permit_no = fields.Char(related='employee_id.permit_no', readonly=False, related_sudo=False) 48 visa_expire = fields.Date(related='employee_id.visa_expire', readonly=False, related_sudo=False) 49 additional_note = fields.Text(related='employee_id.additional_note', readonly=False, related_sudo=False) 50 barcode = fields.Char(related='employee_id.barcode', readonly=False, related_sudo=False) 51 pin = fields.Char(related='employee_id.pin', readonly=False, related_sudo=False) 52 certificate = fields.Selection(related='employee_id.certificate', readonly=False, related_sudo=False) 53 study_field = fields.Char(related='employee_id.study_field', readonly=False, related_sudo=False) 54 study_school = fields.Char(related='employee_id.study_school', readonly=False, related_sudo=False) 55 employee_count = fields.Integer(compute='_compute_employee_count') 56 hr_presence_state = fields.Selection(related='employee_id.hr_presence_state') 57 last_activity = fields.Date(related='employee_id.last_activity') 58 last_activity_time = fields.Char(related='employee_id.last_activity_time') 59 60 can_edit = fields.Boolean(compute='_compute_can_edit') 61 62 def _compute_can_edit(self): 63 can_edit = self.env['ir.config_parameter'].sudo().get_param('hr.hr_employee_self_edit') or self.env.user.has_group('hr.group_hr_user') 64 for user in self: 65 user.can_edit = can_edit 66 67 @api.depends('employee_ids') 68 def _compute_employee_count(self): 69 for user in self.with_context(active_test=False): 70 user.employee_count = len(user.employee_ids) 71 72 def __init__(self, pool, cr): 73 """ Override of __init__ to add access rights. 74 Access rights are disabled by default, but allowed 75 on some specific fields defined in self.SELF_{READ/WRITE}ABLE_FIELDS. 76 """ 77 hr_readable_fields = [ 78 'active', 79 'child_ids', 80 'employee_id', 81 'employee_ids', 82 'employee_parent_id', 83 'hr_presence_state', 84 'last_activity', 85 'last_activity_time', 86 'can_edit', 87 ] 88 89 hr_writable_fields = [ 90 'additional_note', 91 'address_home_id', 92 'address_id', 93 'barcode', 94 'birthday', 95 'category_ids', 96 'children', 97 'coach_id', 98 'country_of_birth', 99 'department_id', 100 'display_name', 101 'emergency_contact', 102 'emergency_phone', 103 'employee_bank_account_id', 104 'employee_country_id', 105 'gender', 106 'identification_id', 107 'is_address_home_a_company', 108 'job_title', 109 'private_email', 110 'km_home_work', 111 'marital', 112 'mobile_phone', 113 'notes', 114 'employee_parent_id', 115 'passport_id', 116 'permit_no', 117 'employee_phone', 118 'pin', 119 'place_of_birth', 120 'spouse_birthdate', 121 'spouse_complete_name', 122 'visa_expire', 123 'visa_no', 124 'work_email', 125 'work_location', 126 'work_phone', 127 'certificate', 128 'study_field', 129 'study_school', 130 ] 131 132 init_res = super(User, self).__init__(pool, cr) 133 # duplicate list to avoid modifying the original reference 134 type(self).SELF_READABLE_FIELDS = type(self).SELF_READABLE_FIELDS + hr_readable_fields + hr_writable_fields 135 type(self).SELF_WRITEABLE_FIELDS = type(self).SELF_WRITEABLE_FIELDS + hr_writable_fields 136 return init_res 137 138 @api.model 139 def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): 140 # When the front-end loads the views it gets the list of available fields 141 # for the user (according to its access rights). Later, when the front-end wants to 142 # populate the view with data, it only asks to read those available fields. 143 # However, in this case, we want the user to be able to read/write its own data, 144 # even if they are protected by groups. 145 # We make the front-end aware of those fields by sending all field definitions. 146 # Note: limit the `sudo` to the only action of "editing own profile" action in order to 147 # avoid breaking `groups` mecanism on res.users form view. 148 profile_view = self.env.ref("hr.res_users_view_form_profile") 149 if profile_view and view_id == profile_view.id: 150 self = self.with_user(SUPERUSER_ID) 151 return super(User, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) 152 153 def write(self, vals): 154 """ 155 Synchronize user and its related employee 156 and check access rights if employees are not allowed to update 157 their own data (otherwise sudo is applied for self data). 158 """ 159 hr_fields = { 160 field 161 for field_name, field in self._fields.items() 162 if field.related_field and field.related_field.model_name == 'hr.employee' and field_name in vals 163 } 164 can_edit_self = self.env['ir.config_parameter'].sudo().get_param('hr.hr_employee_self_edit') or self.env.user.has_group('hr.group_hr_user') 165 if hr_fields and not can_edit_self: 166 # Raise meaningful error message 167 raise AccessError(_("You are only allowed to update your preferences. Please contact a HR officer to update other information.")) 168 169 result = super(User, self).write(vals) 170 171 employee_values = {} 172 for fname in [f for f in ['name', 'email', 'image_1920', 'tz'] if f in vals]: 173 employee_values[fname] = vals[fname] 174 if employee_values: 175 if 'email' in employee_values: 176 employee_values['work_email'] = employee_values.pop('email') 177 if 'image_1920' in vals: 178 without_image = self.env['hr.employee'].sudo().search([('user_id', 'in', self.ids), ('image_1920', '=', False)]) 179 with_image = self.env['hr.employee'].sudo().search([('user_id', 'in', self.ids), ('image_1920', '!=', False)]) 180 without_image.write(employee_values) 181 if not can_edit_self: 182 employee_values.pop('image_1920') 183 with_image.write(employee_values) 184 else: 185 self.env['hr.employee'].sudo().search([('user_id', 'in', self.ids)]).write(employee_values) 186 return result 187 188 @api.model 189 def action_get(self): 190 if self.env.user.employee_id: 191 return self.env['ir.actions.act_window']._for_xml_id('hr.res_users_action_my') 192 return super(User, self).action_get() 193 194 @api.depends('employee_ids') 195 @api.depends_context('company') 196 def _compute_company_employee(self): 197 for user in self: 198 user.employee_id = self.env['hr.employee'].search([('id', 'in', user.employee_ids.ids), ('company_id', '=', self.env.company.id)], limit=1) 199 200 def _search_company_employee(self, operator, value): 201 return [('employee_ids', operator, value)] 202 203 def action_create_employee(self): 204 self.ensure_one() 205 self.env['hr.employee'].create(dict( 206 name=self.name, 207 company_id=self.env.company.id, 208 **self.env['hr.employee']._sync_user(self) 209 )) 210