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