[IMP] hr: Remove address_home_id field

Improve usability of employee form. It is confusing for end users
to create another record to encode the employee address.

Move all the private information on the hr.employee record itself.

Remove the M2O address_home_id.

TaskID: 3101400
This commit is contained in:
Yannick Tivisse
2023-07-05 14:21:28 +02:00
parent ef4196281b
commit e8c48f824e
28 changed files with 562 additions and 693 deletions
+1 -1
View File
@@ -34,9 +34,9 @@
'views/hr_work_location_views.xml',
'views/hr_views.xml',
'views/res_config_settings_views.xml',
'views/res_partner_views.xml',
'views/discuss_channel_views.xml',
'views/res_users.xml',
'views/res_partner_views.xml',
'views/hr_templates.xml',
'data/hr_data.xml',
],
+1 -8
View File
@@ -6,19 +6,12 @@
<field name="name">Administration</field>
</record>
<record id="res_partner_admin_private_address" model="res.partner">
<field name="name">Administrator</field>
<field name="company_id" ref="base.main_company"/>
<field name="email">admin@example.com</field>
<field name="type">private</field>
</record>
<record id="employee_admin" model="hr.employee">
<field name="name" eval="obj(ref('base.partner_admin')).name" model="res.partner"/>
<field name="department_id" ref="dep_administration"/>
<field name="user_id" ref="base.user_admin"/>
<field name="address_id" ref="base.main_partner"/>
<field name="address_home_id" ref="res_partner_admin_private_address"/>
<field name="private_email">admin@example.com</field>
<field name="image_1920" eval="obj(ref('base.partner_admin')).image_1920" model="res.partner"/>
</record>
+446 -485
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -17,8 +17,8 @@ from . import mail_alias
from . import discuss_channel
from . import models
from . import res_config_settings
from . import res_partner
from . import res_users
from . import res_company
from . import res_partner
from . import resource
from . import ir_ui_menu
+24 -38
View File
@@ -30,6 +30,10 @@ class HrEmployeePrivate(models.Model):
_inherit = ['hr.employee.base', 'mail.thread.main.attachment', 'mail.activity.mixin', 'resource.mixin', 'avatar.mixin']
_mail_post_access = 'read'
@api.model
def _lang_get(self):
return self.env['res.lang'].get_installed()
# resource and user
# required on the resource, make sure required="True" set in the view
name = fields.Char(string="Employee Name", related='resource_id.name', store=True, readonly=False, tracking=True)
@@ -41,17 +45,19 @@ class HrEmployeePrivate(models.Model):
company_id = fields.Many2one('res.company', required=True)
company_country_id = fields.Many2one('res.country', 'Company Country', related='company_id.country_id', readonly=True)
company_country_code = fields.Char(related='company_country_id.code', depends=['company_country_id'], readonly=True)
# private partner
address_home_id = fields.Many2one(
'res.partner', 'Address', help='Enter here the private address of the employee, not the one linked to your company.',
groups="hr.group_hr_user", tracking=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
is_address_home_a_company = fields.Boolean(
'The employee address has a company linked',
compute='_compute_is_address_home_a_company',
)
private_email = fields.Char(related='address_home_id.email', string="Private Email", groups="hr.group_hr_user")
lang = fields.Selection(related='address_home_id.lang', string="Lang", groups="hr.group_hr_user", readonly=False)
# private info
private_street = fields.Char(string="Private Street", groups="hr.group_hr_user")
private_street2 = fields.Char(string="Private Street2", groups="hr.group_hr_user")
private_city = fields.Char(string="Private City", groups="hr.group_hr_user")
private_state_id = fields.Many2one(
"res.country.state", string="Private State",
domain="[('country_id', '=?', private_country_id)]",
groups="hr.group_hr_user")
private_zip = fields.Char(string="Private Zip", groups="hr.group_hr_user")
private_country_id = fields.Many2one("res.country", string="Private Country", groups="hr.group_hr_user")
private_phone = fields.Char(string="Private Phone", groups="hr.group_hr_user")
private_email = fields.Char(string="Private Email", groups="hr.group_hr_user")
lang = fields.Selection(selection=_lang_get, string="Lang", groups="hr.group_hr_user")
country_id = fields.Many2one(
'res.country', 'Nationality (Country)', groups="hr.group_hr_user", tracking=True)
gender = fields.Selection([
@@ -78,7 +84,7 @@ class HrEmployeePrivate(models.Model):
passport_id = fields.Char('Passport No', groups="hr.group_hr_user", tracking=True)
bank_account_id = fields.Many2one(
'res.partner.bank', 'Bank Account Number',
domain="[('partner_id', '=', address_home_id), '|', ('company_id', '=', False), ('company_id', '=', company_id)]",
domain="[('partner_id', '=', work_contact_id), '|', ('company_id', '=', False), ('company_id', '=', company_id)]",
groups="hr.group_hr_user",
tracking=True,
help='Employee bank account to pay salaries')
@@ -111,7 +117,6 @@ class HrEmployeePrivate(models.Model):
help="The employee type. Although the primary purpose may seem to categorize employees, this field has also an impact in the Contract History. Only Employee type is supposed to be under contract and will have a Contract History.")
job_id = fields.Many2one(tracking=True)
phone = fields.Char(related='address_home_id.phone', related_sudo=False, readonly=False, string="Private Phone", groups="hr.group_hr_user")
# employee in company
child_ids = fields.One2many('hr.employee', 'parent_id', string='Direct subordinates')
category_ids = fields.Many2many(
@@ -372,7 +377,6 @@ class HrEmployeePrivate(models.Model):
onboarding_notes_bodies = {}
hr_root_menu = self.env.ref('hr.menu_hr_root')
for employee in employees:
employee._message_subscribe(employee.address_home_id.ids)
# Launch onboarding plans
url = '/web#%s' % url_encode({
'action': 'hr.plan_wizard_action',
@@ -387,16 +391,16 @@ class HrEmployeePrivate(models.Model):
return employees
def write(self, vals):
if 'address_home_id' in vals:
if 'work_contact_id' in vals:
account_id = vals.get('bank_account_id') or self.bank_account_id.id
if account_id:
bank_account = self.env['res.partner.bank'].browse(account_id)
if vals['address_home_id'] != bank_account.partner_id.id:
if vals['work_contact_id'] != bank_account.partner_id.id:
bank_account.sudo().allow_out_payment = False
bank_account.partner_id = vals['address_home_id']
self.message_unsubscribe(self.address_home_id.ids)
if vals['address_home_id']:
self._message_subscribe([vals['address_home_id']])
bank_account.partner_id = vals['work_contact_id']
self.message_unsubscribe(self.work_contact_id.ids)
if vals['work_contact_id']:
self._message_subscribe([vals['work_contact_id']])
if 'user_id' in vals:
# Update the profile pictures with user, except if provided
vals.update(self._sync_user(self.env['res.users'].browse(vals['user_id']),
@@ -431,8 +435,6 @@ class HrEmployeePrivate(models.Model):
'departure_description': False,
'departure_date': False
})
archived_addresses = unarchived_employees.mapped('address_home_id').filtered(lambda addr: not addr.active)
archived_addresses.toggle_active()
archived_employees = self.filtered(lambda e: not e.active)
if archived_employees:
@@ -474,22 +476,6 @@ class HrEmployeePrivate(models.Model):
for employee in self:
employee.barcode = '041'+"".join(choice(digits) for i in range(9))
@api.depends('address_home_id', 'user_partner_id')
def _compute_related_contacts(self):
super()._compute_related_contacts()
for employee in self:
employee.related_contact_ids |= employee.address_home_id | employee.user_partner_id
@api.depends('address_home_id.parent_id')
def _compute_is_address_home_a_company(self):
"""Checks that chosen address (res.partner) is not linked to a company.
"""
for employee in self:
try:
employee.is_address_home_a_company = employee.address_home_id.parent_id.id is not False
except AccessError:
employee.is_address_home_a_company = False
def _get_tz(self):
# Finds the first valid timezone in his tz, his work hours tz,
# the company calendar tz or UTC and returns it as a string
-22
View File
@@ -31,8 +31,6 @@ class HrEmployeeBase(models.AbstractModel):
mobile_phone = fields.Char('Work Mobile', compute="_compute_work_contact_details", store=True, inverse='_inverse_work_contact_details')
work_email = fields.Char('Work Email', compute="_compute_work_contact_details", store=True, inverse='_inverse_work_contact_details')
work_contact_id = fields.Many2one('res.partner', 'Work Contact', copy=False)
related_contact_ids = fields.Many2many('res.partner', 'Related Contacts', compute='_compute_related_contacts')
related_contacts_count = fields.Integer('Number of related contacts', compute='_compute_related_contacts_count')
work_location_id = fields.Many2one('hr.work.location', 'Work Location', compute="_compute_work_location_id", store=True, readonly=False,
domain="[('address_id', '=', address_id), '|', ('company_id', '=', False), ('company_id', '=', company_id)]")
user_id = fields.Many2one('res.users')
@@ -174,26 +172,6 @@ class HrEmployeeBase(models.AbstractModel):
'mobile': employee.mobile_phone,
})
@api.depends('work_contact_id')
def _compute_related_contacts(self):
for employee in self:
employee.related_contact_ids = employee.work_contact_id
@api.depends('related_contact_ids')
def _compute_related_contacts_count(self):
for employee in self:
employee.related_contacts_count = len(employee.related_contact_ids)
def action_related_contacts(self):
self.ensure_one()
return {
'name': _("Related Contacts"),
'type': 'ir.actions.act_window',
'view_mode': 'kanban,tree,form',
'res_model': 'res.partner',
'domain': [('id', 'in', self.related_contact_ids.ids)]
}
@api.depends('company_id')
def _compute_address_id(self):
for employee in self:
-7
View File
@@ -25,7 +25,6 @@ class HrEmployeePublic(models.Model):
work_phone = fields.Char(readonly=True)
work_email = fields.Char(readonly=True)
work_contact_id = fields.Many2one(readonly=True)
related_contact_ids = fields.Many2many(readonly=True)
work_location_id = fields.Many2one(readonly=True)
user_id = fields.Many2one(readonly=True)
resource_id = fields.Many2one(readonly=True)
@@ -57,12 +56,6 @@ class HrEmployeePublic(models.Model):
for employee in self:
employee.employee_id = self.env['hr.employee'].browse(employee.id)
@api.depends('user_partner_id')
def _compute_related_contacts(self):
super()._compute_related_contacts()
for employee in self:
employee.related_contact_ids |= employee.user_partner_id
@api.model
def _get_fields(self):
return ','.join('emp.%s' % name for name, field in self._fields.items() if field.store and field.type not in ['many2many', 'one2many'])
+1 -15
View File
@@ -2,30 +2,16 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, _
from odoo.exceptions import AccessError
class Partner(models.Model):
_inherit = ['res.partner']
employee_ids = fields.One2many(
'hr.employee', 'address_home_id', string='Employees', groups="hr.group_hr_user",
'hr.employee', 'work_contact_id', string='Employees', groups="hr.group_hr_user",
help="Related employees based on their private address")
employees_count = fields.Integer(compute='_compute_employees_count', groups="hr.group_hr_user")
def _compute_display_name(self):
""" Override to allow an employee to see its private address in his profile.
This avoids to relax access rules on `res.parter` and to add an `ir.rule`.
(advantage in both security and performance).
Use a try/except instead of systematically checking to minimize the impact on performance.
"""
try:
super()._compute_display_name()
except AccessError as e:
if len(self) == 1 and self in self.env.user.employee_ids.mapped('address_home_id'):
super(Partner, self.sudo())._compute_display_name()
raise e
def _compute_employees_count(self):
for partner in self:
partner.employees_count = len(partner.employee_ids)
+13 -15
View File
@@ -12,7 +12,6 @@ HR_READABLE_FIELDS = [
'active',
'child_ids',
'employee_id',
'address_home_id',
'employee_ids',
'employee_parent_id',
'hr_presence_state',
@@ -21,6 +20,7 @@ HR_READABLE_FIELDS = [
'can_edit',
'is_system',
'employee_resource_calendar_id',
'work_contact_id',
]
HR_WRITABLE_FIELDS = [
@@ -31,6 +31,8 @@ HR_WRITABLE_FIELDS = [
'private_state_id',
'private_zip',
'private_country_id',
'private_phone',
'private_email',
'address_id',
'barcode',
'birthday',
@@ -46,9 +48,7 @@ HR_WRITABLE_FIELDS = [
'employee_country_id',
'gender',
'identification_id',
'is_address_home_a_company',
'job_title',
'private_email',
'km_home_work',
'marital',
'mobile_phone',
@@ -56,7 +56,6 @@ HR_WRITABLE_FIELDS = [
'employee_parent_id',
'passport_id',
'permit_no',
'employee_phone',
'pin',
'place_of_birth',
'spouse_birthdate',
@@ -90,26 +89,25 @@ class User(models.Model):
job_title = fields.Char(related='employee_id.job_title', readonly=False, related_sudo=False)
work_phone = fields.Char(related='employee_id.work_phone', readonly=False, related_sudo=False)
mobile_phone = fields.Char(related='employee_id.mobile_phone', readonly=False, related_sudo=False)
employee_phone = fields.Char(related='employee_id.phone', readonly=False, related_sudo=False)
work_email = fields.Char(related='employee_id.work_email', readonly=False, related_sudo=False)
category_ids = fields.Many2many(related='employee_id.category_ids', string="Employee Tags", readonly=False, related_sudo=False)
department_id = fields.Many2one(related='employee_id.department_id', readonly=False, related_sudo=False)
address_id = fields.Many2one(related='employee_id.address_id', readonly=False, related_sudo=False)
work_contact_id = fields.Many2one(related='employee_id.work_contact_id', readonly=False, related_sudo=False)
work_location_id = fields.Many2one(related='employee_id.work_location_id', readonly=False, related_sudo=False)
employee_parent_id = fields.Many2one(related='employee_id.parent_id', readonly=False, related_sudo=False)
coach_id = fields.Many2one(related='employee_id.coach_id', readonly=False, related_sudo=False)
address_home_id = fields.Many2one(related='employee_id.address_home_id', readonly=False, related_sudo=False)
private_street = fields.Char(related='address_home_id.street', string="Private Street", readonly=False, related_sudo=False)
private_street2 = fields.Char(related='address_home_id.street2', string="Private Street2", readonly=False, related_sudo=False)
private_city = fields.Char(related='address_home_id.city', string="Private City", readonly=False, related_sudo=False)
private_street = fields.Char(related='employee_id.private_street', string="Private Street", readonly=False, related_sudo=False)
private_street2 = fields.Char(related='employee_id.private_street2', string="Private Street2", readonly=False, related_sudo=False)
private_city = fields.Char(related='employee_id.private_city', string="Private City", readonly=False, related_sudo=False)
private_state_id = fields.Many2one(
related='address_home_id.state_id', string="Private State", readonly=False, related_sudo=False,
related='employee_id.private_state_id', string="Private State", readonly=False, related_sudo=False,
domain="[('country_id', '=?', private_country_id)]")
private_zip = fields.Char(related='address_home_id.zip', readonly=False, string="Private Zip", related_sudo=False)
private_country_id = fields.Many2one(related='address_home_id.country_id', string="Private Country", readonly=False, related_sudo=False)
is_address_home_a_company = fields.Boolean(related='employee_id.is_address_home_a_company', readonly=False, related_sudo=False)
private_email = fields.Char(related='address_home_id.email', string="Private Email", readonly=False)
private_lang = fields.Selection(related='address_home_id.lang', string="Employee Lang", readonly=False)
private_zip = fields.Char(related='employee_id.private_zip', readonly=False, string="Private Zip", related_sudo=False)
private_country_id = fields.Many2one(related='employee_id.private_country_id', string="Private Country", readonly=False, related_sudo=False)
private_phone = fields.Char(related='employee_id.private_phone', readonly=False, related_sudo=False)
private_email = fields.Char(related='employee_id.private_email', string="Private Email", readonly=False)
private_lang = fields.Selection(related='employee_id.lang', string="Employee Lang", readonly=False)
km_home_work = fields.Integer(related='employee_id.km_home_work', readonly=False, related_sudo=False)
# res.users already have a field bank_account_id and country_id from the res.partner inheritance: don't redefine them
employee_bank_account_id = fields.Many2one(related='employee_id.bank_account_id', string="Employee's Bank Account Number", related_sudo=False, readonly=False)
+1 -2
View File
@@ -112,13 +112,12 @@ class TestSelfAccessRights(TestHrCommon):
cls.richard_emp = cls.env['hr.employee'].create({
'name': 'Richard',
'user_id': cls.richard.id,
'address_home_id': cls.env['res.partner'].create({'name': 'Richard', 'phone': '21454', 'type': 'private'}).id,
'private_phone': '21454',
})
cls.hubert = new_test_user(cls.env, login='hub', groups='base.group_user', name='Simple employee', email='hub@example.com')
cls.hubert_emp = cls.env['hr.employee'].create({
'name': 'Hubert',
'user_id': cls.hubert.id,
'address_home_id': cls.env['res.partner'].create({'name': 'Hubert', 'type': 'private'}).id,
})
cls.protected_fields_emp = OrderedDict([(k, v) for k, v in cls.env['hr.employee']._fields.items() if v.groups == 'hr.group_hr_user'])
+12 -32
View File
@@ -54,6 +54,7 @@
<field name="company_id" invisible="1"/>
<field name="last_activity_time" invisible="1"/>
<field name="last_activity" invisible="1"/>
<field name="work_contact_id" invisible="1"/>
<header>
<button name="%(plan_wizard_action)d" string="Launch Plan" type="action" groups="hr.group_hr_user" attrs="{'invisible': [('active', '=', False)]}"/>
</header>
@@ -135,15 +136,18 @@
<page name="personal_information" string="Private Information" groups="hr.group_hr_user">
<group>
<group string="Private Contact">
<field name="address_home_id"
context="{
'show_address': 1,
'default_type': 'private',
'form_view_ref': 'base.res_partner_view_form_private'}"
options='{"always_reload": True, "highlight_first_line": True}'/>
<label for="private_street" string="Private Address"/>
<div class="o_address_format">
<field name="private_street" placeholder="Street..." class="o_address_street"/>
<field name="private_street2" placeholder="Street 2..." class="o_address_street"/>
<field name="private_city" placeholder="City" class="o_address_city"/>
<field name="private_state_id" class="o_address_state" placeholder="State" options="{'no_open': True, 'no_quick_create': True}" context="{'default_country_id': private_country_id}"/>
<field name="private_zip" placeholder="ZIP" class="o_address_zip"/>
<field name="private_country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'/>
</div>
<field name="private_email" string="Email"/>
<field name="phone" string="Phone" readonly="True"/>
<field name="bank_account_id" context="{'default_partner_id': address_home_id}"/>
<field name="private_phone" string="Phone"/>
<field name="bank_account_id" context="{'default_partner_id': work_contact_id}" options="{'no_quick_create': True}" attrs="{'readonly': [('id', '=', False)]}"/>
<field name="lang" string="Language"/>
<label for="km_home_work"/>
<div class="o_row" name="div_km_home_work">
@@ -215,30 +219,6 @@
</field>
</record>
<!-- This part of the view_employee_form is defined separately so that the
smartbutton can have lower priority and therefore be last in the list. -->
<record id="view_employee_form_smartbutton" model="ir.ui.view">
<field name="name">view.employee.form.smartbutton</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="view_employee_form"/>
<field name="priority" eval="1000"/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<field name="related_contacts_count" invisible="1"/>
<button name="action_related_contacts"
class="oe_stat_button"
icon="fa-address-card-o"
type="object"
attrs="{'invisible': ['|', ('id', '=', False), ('related_contacts_count', '=', 0)]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="related_contacts_count"/></span>
<span class="o_stat_text">Contacts</span>
</div>
</button>
</div>
</field>
</record>
<record id="hr_employee_view_graph" model="ir.ui.view">
<field name="name">hr.employee.view.graph</field>
<field name="model">hr.employee</field>
+12 -11
View File
@@ -20,6 +20,9 @@
<xpath expr="//field[@name='company_id']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<field name="share" position="after">
<field name="work_contact_id" invisible="1"/>
</field>
</field>
</record>
@@ -134,20 +137,18 @@
<group>
<group string="Contact Information">
<field name="employee_ids" invisible="1"/>
<field name="address_home_id" invisible="1"/>
<label for="private_street" string="Private Address"/>
<div class="o_address_format">
<field name="private_street" placeholder="Street..." class="o_address_street"/>
<field name="private_street2" placeholder="Street 2..." class="o_address_street"/>
<field name="private_city" placeholder="City" class="o_address_city"/>
<field name="private_state_id" class="o_address_state" placeholder="State" options="{'no_open': True, 'no_quick_create': True}" context="{'default_country_id': private_country_id}"/>
<field name="private_zip" placeholder="ZIP" class="o_address_zip"/>
<field name="private_country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'/>
<field name="private_street" placeholder="Street..." class="o_address_street" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_street2" placeholder="Street 2..." class="o_address_street" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_city" placeholder="City" class="o_address_city" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_state_id" class="o_address_state" placeholder="State" options="{'no_open': True, 'no_quick_create': True}" context="{'default_country_id': private_country_id}" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_zip" placeholder="ZIP" class="o_address_zip" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}' attrs="{'readonly': [('can_edit', '=', False)]}"/>
</div>
<field name="private_email" string="Email" attrs="{'readonly': [('can_edit', '=', False)], 'invisible': [('address_home_id', '=', False)]}"/>
<field name="employee_phone" string="Phone" class="o_force_ltr" attrs="{'readonly': [('can_edit', '=', False)], 'invisible': [('address_home_id', '=', False)]}"/>
<field name="private_lang" string="Language" attrs="{'readonly': [('can_edit', '=', False)], 'invisible': [('address_home_id', '=', False)]}"/>
<field name="private_email" string="Email" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_phone" string="Phone" class="o_force_ltr" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="private_lang" string="Language" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="employee_bank_account_id" attrs="{'readonly': [('can_edit', '=', False)]}"/>
<field name="km_home_work" attrs="{'readonly': [('can_edit', '=', False)]}"/>
</group>
-7
View File
@@ -21,7 +21,6 @@ class HrDepartureWizard(models.TransientModel):
'hr.employee', string='Employee', required=True,
default=lambda self: self.env.context.get('active_id', None),
)
archive_private_address = fields.Boolean('Archive Private Address', default=True)
def action_register_departure(self):
employee = self.employee_id
@@ -30,9 +29,3 @@ class HrDepartureWizard(models.TransientModel):
employee.departure_reason_id = self.departure_reason_id
employee.departure_description = self.departure_description
employee.departure_date = self.departure_date
if self.archive_private_address:
# ignore contact links to internal users
private_address = employee.address_home_id
if private_address and private_address.active and not self.env['res.users'].search([('partner_id', '=', private_address.id)]):
private_address.sudo().toggle_active()
@@ -24,9 +24,7 @@
<div class="o_td_label" id="label_info">
<span class="o_form_label o_hr_form_label cursor-default">HR Info</span>
</div>
<div class="column" id="info">
<div><field name="archive_private_address"/><label for="archive_private_address"/></div>
</div>
<div class="column" id="info"/>
</group>
</group>
<group>
@@ -14,9 +14,6 @@
<xpath expr="//page[@name='hr_settings']//field[@name='employee_type']" position="after">
<field name="first_contract_date" invisible="1"/>
</xpath>
<xpath expr="//field[@name='bank_account_id']" position="replace">
<field name="bank_account_id" context="{'display_partner':True}" attrs="{'invisible' : [('address_home_id', '=', False)]}"/>
</xpath>
<xpath expr="//field[@name='resource_calendar_id']" position="replace">
<field name="calendar_mismatch" invisible="1"/>
<label for="resource_calendar_id"/>
+10 -13
View File
@@ -3,7 +3,8 @@
<data noupdate="1">
<record id="hr.employee_mit" model="hr.employee">
<field name="address_home_id" ref="base.res_partner_address_3"/>
<field name="private_email">douglas.fletcher51@example.com</field>
<field name="private_phone">(132)-553-7242</field>
</record>
<record id="hr_expense_account_journal" model="account.journal">
@@ -15,22 +16,18 @@
<field name="alias_name">purchase_expense</field>
</record>
<record id="res_partner_address_fp" model="res.partner">
<field name="name">Pieter Parter's Farm</field>
<field name="parent_id" ref="base.partner_root"/>
<field name="street">Chaussée de Namur, 40</field>
<field name="zip">1367</field>
<field name="city">Grand-Rosière-Hottomont</field>
<field name="country_id" ref="base.be"/>
<field name="type">private</field>
</record>
<record id="hr.employee_fme" model="hr.employee">
<field name="address_home_id" ref="res_partner_address_fp"/>
<field name="private_street">Chaussée de Namur, 40</field>
<field name="private_zip">1367</field>
<field name="private_city">Grand-Rosière-Hottomont</field>
<field name="private_country_id" ref="base.be"/>
</record>
<record id="hr.employee_al" model="hr.employee">
<field name="address_home_id" ref="res_partner_address_fp"/>
<field name="private_street">Chaussée de Namur, 40</field>
<field name="private_zip">1367</field>
<field name="private_city">Grand-Rosière-Hottomont</field>
<field name="private_country_id" ref="base.be"/>
</record>
<!-- ++++++++++++++ Expense sheet for Admin ++++++++++++++-->
+6 -6
View File
@@ -614,7 +614,7 @@ class HrExpense(models.Model):
'product_uom_id': self.product_uom_id.id,
'analytic_distribution': self.analytic_distribution,
'expense_id': self.id,
'partner_id': False if self.payment_mode == 'company_account' else self.employee_id.sudo().address_home_id.commercial_partner_id.id,
'partner_id': False if self.payment_mode == 'company_account' else self.employee_id.sudo().work_contact_id.id,
'tax_ids': [Command.set(self.tax_ids.ids)],
}
@@ -1023,7 +1023,7 @@ class HrExpenseSheet(models.Model):
@api.depends('employee_id', 'employee_id.department_id')
def _compute_from_employee_id(self):
for sheet in self:
sheet.address_id = sheet.employee_id.sudo().address_home_id
sheet.address_id = sheet.employee_id.sudo().work_contact_id
sheet.department_id = sheet.employee_id.department_id
sheet.user_id = sheet.employee_id.expense_manager_id or sheet.employee_id.parent_id.user_id
@@ -1399,7 +1399,7 @@ class HrExpenseSheet(models.Model):
# to set it to '' which cause no number to be given to the account.move when posted.
'journal_id': self.journal_id.id,
'move_type': 'in_invoice',
'partner_id': self.employee_id.sudo().address_home_id.commercial_partner_id.id,
'partner_id': self.employee_id.sudo().work_contact_id.id,
'currency_id': self.currency_id.id,
'line_ids':[Command.create(expense._prepare_move_line_vals()) for expense in self.expense_line_ids],
}
@@ -1441,8 +1441,8 @@ class HrExpenseSheet(models.Model):
or journal.company_id.account_journal_payment_credit_account_id
)
else:
if not self.employee_id.sudo().address_home_id:
raise UserError(_("No Home Address found for the employee %s, please configure one.") % (self.employee_id.name))
partner = self.employee_id.sudo().address_home_id.with_company(self.company_id)
if not self.employee_id.sudo().work_contact_id:
raise UserError(_("No work contact found for the employee %s, please configure one.") % (self.employee_id.name))
partner = self.employee_id.sudo().work_contact_id.with_company(self.company_id)
account_dest = partner.property_account_payable_id or partner.parent_id.property_account_payable_id
return account_dest.id
+1 -1
View File
@@ -37,7 +37,7 @@ class TestExpenseCommon(AccountTestInvoicingCommon):
cls.expense_employee = cls.env['hr.employee'].create({
'name': 'expense_employee',
'user_id': cls.expense_user_employee.id,
'address_home_id': cls.expense_user_employee.partner_id.id,
'work_contact_id': cls.expense_user_employee.partner_id.id,
'address_id': cls.expense_user_employee.partner_id.id,
})
+1 -1
View File
@@ -117,7 +117,7 @@ class TestExpenses(TestExpenseCommon):
'journal_id': self.company_data['default_journal_bank'].id,
'payment_ref': 'pay_ref',
'amount': -350.0,
'partner_id': self.expense_employee.address_home_id.id,
'partner_id': self.expense_employee.work_contact_id.id,
})
# Reconcile without the bank reconciliation widget since the widget is in enterprise.
@@ -14,7 +14,7 @@ class TestExpensesAccessRights(TestExpenseCommon):
expense_employee_2 = self.env['hr.employee'].create({
'name': 'expense_employee_2',
'user_id': self.env.user.id,
'address_home_id': self.env.user.partner_id.id,
'work_contact_id': self.env.user.partner_id.id,
'address_id': self.env.user.partner_id.id,
})
+1 -1
View File
@@ -16,7 +16,7 @@
<field name="driver_employee_id" ref="hr.employee_jep"/>
</record>
<!-- recompute driver_employee_id as we assigned a proper address_home_id to some employees -->
<!-- recompute driver_employee_id as we assigned a proper work_contact_id to some employees -->
<function model="fleet.vehicle.assignation.log" name="_compute_driver_employee_id">
<value model="fleet.vehicle.assignation.log" eval="obj().search([('driver_employee_id', '=', False), ('driver_id', '!=', False)]).ids"/>
</function>
+7 -7
View File
@@ -48,9 +48,9 @@ class Employee(models.Model):
for employee in self:
employee.employee_cars_count = cars_count.get(employee.id, 0)
@api.constrains('address_home_id')
def _check_address_home_id(self):
no_address = self.filtered(lambda r: not r.address_home_id)
@api.constrains('work_contact_id')
def _check_work_contact_id(self):
no_address = self.filtered(lambda r: not r.work_contact_id)
car_ids = self.env['fleet.vehicle'].sudo().search([
('driver_employee_id', 'in', no_address.ids),
])
@@ -62,16 +62,16 @@ class Employee(models.Model):
def write(self, vals):
res = super().write(vals)
#Update car partner when it is changed on the employee
if 'address_home_id' in vals:
if 'work_contact_id' in vals:
car_ids = self.env['fleet.vehicle'].sudo().search([
('driver_employee_id', 'in', self.ids),
('driver_id', 'in', self.mapped('address_home_id').ids),
('driver_id', 'in', self.mapped('work_contact_id').ids),
])
if car_ids:
car_ids.write({'driver_id': vals['address_home_id']})
car_ids.write({'driver_id': vals['work_contact_id']})
if 'mobility_card' in vals:
#NOTE: keeping it as a search on driver_id but we might be able to use driver_employee_id in the future
vehicles = self.env['fleet.vehicle'].search([('driver_id', 'in', (self.user_id.partner_id | self.sudo().address_home_id).ids)])
vehicles = self.env['fleet.vehicle'].search([('driver_id', 'in', (self.user_id.partner_id | self.sudo().work_contact_id).ids)])
vehicles._compute_mobility_card()
return res
+7 -7
View File
@@ -27,7 +27,7 @@ class FleetVehicle(models.Model):
for vehicle in self:
if vehicle.driver_id:
vehicle.driver_employee_id = self.env['hr.employee'].search([
('address_home_id', '=', vehicle.driver_id.id),
('work_contact_id', '=', vehicle.driver_id.id),
], limit=1)
else:
vehicle.driver_employee_id = False
@@ -37,7 +37,7 @@ class FleetVehicle(models.Model):
for vehicle in self:
if vehicle.future_driver_id:
vehicle.future_driver_employee_id = self.env['hr.employee'].search([
('address_home_id', '=', vehicle.future_driver_id.id),
('work_contact_id', '=', vehicle.future_driver_id.id),
], limit=1)
else:
vehicle.future_driver_employee_id = False
@@ -47,7 +47,7 @@ class FleetVehicle(models.Model):
for vehicle in self:
employee = self.env['hr.employee']
if vehicle.driver_id:
employee = employee.search([('address_home_id', '=', vehicle.driver_id.id)], limit=1)
employee = employee.search([('work_contact_id', '=', vehicle.driver_id.id)], limit=1)
if not employee:
employee = employee.search([('user_id.partner_id', '=', vehicle.driver_id.id)], limit=1)
vehicle.mobility_card = employee.mobility_card
@@ -57,7 +57,7 @@ class FleetVehicle(models.Model):
partner = False
if vals['driver_employee_id']:
employee = self.env['hr.employee'].sudo().browse(vals['driver_employee_id'])
partner = employee.address_home_id.id
partner = employee.work_contact_id.id
vals['driver_id'] = partner
elif 'driver_id' in vals:
# Reverse the process if we can find a single employee
@@ -65,7 +65,7 @@ class FleetVehicle(models.Model):
if vals['driver_id']:
# Limit to 2, we only care about the first one if he is the only one
employee_ids = self.env['hr.employee'].sudo().search([
('address_home_id', '=', vals['driver_id'])
('work_contact_id', '=', vals['driver_id'])
], limit=2)
if len(employee_ids) == 1:
employee = employee_ids[0].id
@@ -76,7 +76,7 @@ class FleetVehicle(models.Model):
partner = False
if vals['future_driver_employee_id']:
employee = self.env['hr.employee'].sudo().browse(vals['future_driver_employee_id'])
partner = employee.address_home_id.id
partner = employee.work_contact_id.id
vals['future_driver_id'] = partner
elif 'future_driver_id' in vals:
# Reverse the process if we can find a single employee
@@ -84,7 +84,7 @@ class FleetVehicle(models.Model):
if vals['future_driver_id']:
# Limit to 2, we only care about the first one if he is the only one
employee_ids = self.env['hr.employee'].sudo().search([
('address_home_id', '=', vals['future_driver_id'])
('work_contact_id', '=', vals['future_driver_id'])
], limit=2)
if len(employee_ids) == 1:
employee = employee_ids[0].id
@@ -12,10 +12,10 @@ class FleetVehicleAssignationLog(models.Model):
@api.depends('driver_id')
def _compute_driver_employee_id(self):
employees = self.env['hr.employee'].search([('address_home_id', 'in', self.driver_id.ids)])
employees = self.env['hr.employee'].search([('work_contact_id', 'in', self.driver_id.ids)])
for log in self:
employee = employees.filtered(lambda e: e.address_home_id.id == log.driver_id.id)
employee = employees.filtered(lambda e: e.work_contact_id.id == log.driver_id.id)
log.driver_employee_id = employee and employee[0] or False
def _compute_attachment_number(self):
@@ -16,7 +16,7 @@ class FleetVehicleLogServices(models.Model):
internals = self.filtered(lambda r: r.purchaser_employee_id)
super(FleetVehicleLogServices, (self - internals))._compute_purchaser_id()
for service in internals:
service.purchaser_id = service.purchaser_employee_id.address_home_id
service.purchaser_id = service.purchaser_employee_id.work_contact_id
@api.depends('vehicle_id')
def _compute_purchaser_employee_id(self):
@@ -18,7 +18,7 @@ class HrDepartureWizard(models.TransientModel):
"""Find all fleet.vehichle.assignation.log records that link to the employee, if there is no
end date or end date > departure date, update the date. Also check fleet.vehicle to see if
there is any record with its dirver_id to be the employee, set them to False."""
drivers = self.employee_id.user_id.partner_id | self.employee_id.sudo().address_home_id
drivers = self.employee_id.user_id.partner_id | self.employee_id.sudo().work_contact_id
assignations = self.env['fleet.vehicle.assignation.log'].search([('driver_id', 'in', drivers.ids)])
for assignation in assignations:
if self.departure_date and (not assignation.date_end or assignation.date_end > self.departure_date):
+1 -1
View File
@@ -1587,7 +1587,7 @@ class HolidaysRequest(models.Model):
if leave.user_id:
recipient = leave.user_id.partner_id.id
elif leave.employee_id:
recipient = leave.employee_id.address_home_id.id
recipient = leave.employee_id.work_contact_id.id
if recipient:
self.env['mail.thread'].sudo().message_notify(
+10 -1
View File
@@ -586,11 +586,20 @@ class Applicant(models.Model):
def _get_employee_create_vals(self):
self.ensure_one()
address_id = self.partner_id.address_get(['contact'])['contact']
address_sudo = self.env['res.partner'].sudo().browse(address_id)
return {
'name': self.partner_name or self.partner_id.display_name,
'job_id': self.job_id.id,
'job_title': self.job_id.name,
'address_home_id': address_id,
'private_street': address_sudo.street,
'private_street2': address_sudo.street2,
'private_city': address_sudo.city,
'private_state_id': address_sudo.state_id,
'private_zip': address_sudo.zip,
'private_country_id': address_sudo.country_id,
'private_phone': address_sudo.phone,
'private_email': address_sudo.email,
'lang': address_sudo.lang,
'department_id': self.department_id.id,
'address_id': self.company_id.partner_id.id,
'work_email': self.department_id.company_id.email or self.email_from, # To have a valid email address by default