[IMP] hr: Review the UX for 'My Profile' view

1/ web: adapt formatDate to handle NaN values
DashboardRenderer might return NaN for `Date` or `DateTime` fields if
their original value is null or incorrect.

2/ hr: add job_title in demo data

3/ hr_recruitment: link employee in the chatter for new recruits

4/ hr: remove employee documents in the model

5/ hr_attendance: improve resiliency of relative_time

6/ hr: display manager on res_users

7/ hr: change Address Book to Employee Directory

TaskID: 2009111

closes odoo/odoo#34180

Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
Kevin Baptiste
2019-07-18 10:27:50 +00:00
committed by Yannick Tivisse
parent 9f5f2fc502
commit e9d9898276
34 changed files with 258 additions and 101 deletions
+20
View File
@@ -141,6 +141,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="work_email">aiden.hughes71@example.com</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4'), ref('employee_category_3')])]"/>
<field name="job_id" ref="hr.job_ceo"/>
<field name="job_title">Chief Executive Officer</field>
<field name="department_id" ref="dep_management"/>
</record>
@@ -148,6 +149,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Ronnie Hart</field>
<field name="department_id" ref="dep_rd"/>
<field name="job_id" ref="hr.job_cto"/>
<field name="job_title">Chief Technical Officer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4'), ref('employee_category_3')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(376)-310-7863</field>
@@ -160,6 +162,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_rd"/>
<field name="parent_id" ref="employee_al"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(538)-497-4804</field>
@@ -173,6 +176,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_management"/>
<field name="parent_id" ref="employee_al"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(450)-719-4182</field>
@@ -185,6 +189,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_rd"/>
<field name="parent_id" ref="employee_al"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(332)-775-6660</field>
@@ -197,6 +202,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_rd"/>
<field name="parent_id" ref="employee_al"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(157)-363-8229</field>
@@ -210,6 +216,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_rd"/>
<field name="parent_id" ref="employee_admin"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">+3281813700</field>
@@ -222,6 +229,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_rd"/>
<field name="parent_id" ref="employee_al"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(449)-505-5146</field>
@@ -234,6 +242,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_ps"/>
<field name="parent_id" ref="employee_admin"/>
<field name="job_id" ref="hr.job_consultant"/>
<field name="job_title">Consultant</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4'), ref('employee_category_5')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(203)-276-7903</field>
@@ -246,6 +255,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_ps"/>
<field name="parent_id" ref="employee_admin"/>
<field name="job_id" ref="hr.job_consultant"/>
<field name="job_title">Consultant</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4'), ref('employee_category_5')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(663)-707-8451</field>
@@ -258,6 +268,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_sales"/>
<field name="parent_id" ref="employee_admin"/>
<field name="job_id" ref="hr.job_marketing"/>
<field name="job_title">Marketing and Community Manager</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4'), ref('employee_category_2')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(916)-264-7362</field>
@@ -270,6 +281,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="department_id" ref="dep_administration"/>
<field name="parent_id" ref="employee_admin"/>
<field name="job_id" ref="hr.job_hrm"/>
<field name="job_title">Human Resources Manager</field>
<field name="category_ids" eval="[(6, 0, [ref('employee_category_4')])]"/>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(360)-694-7266</field>
@@ -281,6 +293,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Walter Horton</field>
<field name="department_id" ref="dep_rd"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(350)-912-1201</field>
<field name="work_email">walter.horton80@example.com</field>
@@ -291,6 +304,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Paul Williams</field>
<field name="department_id" ref="dep_management"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(114)-262-1607</field>
<field name="work_email">paul.williams59@example.com</field>
@@ -301,6 +315,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Doris Cole</field>
<field name="department_id" ref="dep_ps"/>
<field name="job_id" ref="hr.job_consultant"/>
<field name="job_title">Consultant</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(883)-331-5378</field>
<field name="work_email">doris.cole31@example.com</field>
@@ -311,6 +326,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Rachel Perry</field>
<field name="department_id" ref="dep_sales"/>
<field name="job_id" ref="hr.job_marketing"/>
<field name="job_title">Marketing and Community Manager</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(206)-267-3735</field>
<field name="work_email">jod@odoo.com</field>
@@ -321,6 +337,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Beth Evans</field>
<field name="department_id" ref="dep_rd"/>
<field name="job_id" ref="hr.job_developer"/>
<field name="job_title">Experienced Developer</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(754)-532-3841</field>
<field name="work_email">beth.evans77@example.com</field>
@@ -331,6 +348,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Ernest Reed</field>
<field name="department_id" ref="dep_ps"/>
<field name="job_id" ref="hr.job_consultant"/>
<field name="job_title">Consultant</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(944)-518-8232</field>
<field name="work_email">ernest.reed47@example.com</field>
@@ -341,6 +359,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Eli Lambert</field>
<field name="department_id" ref="dep_sales"/>
<field name="job_id" ref="hr.job_marketing"/>
<field name="job_title">Marketing and Community Manager</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_phone">(644)-169-1352</field>
<field name="work_email">eli.lambert22@example.com</field>
@@ -351,6 +370,7 @@ If you have development competencies, we can propose you specific traineeships</
<field name="name">Abigail Peterson</field>
<field name="department_id" ref="dep_ps"/>
<field name="job_id" ref="hr.job_consultant"/>
<field name="job_title">Consultant</field>
<field name="work_location">Building 1, Second Floor</field>
<field name="work_email">abigail.peterson39@example.com</field>
<field name="work_phone">(482)-233-3393</field>
+3 -3
View File
@@ -41,12 +41,13 @@ class HrEmployeePrivate(models.Model):
active = fields.Boolean('Active', related='resource_id.active', default=True, store=True, readonly=False)
# private partner
address_home_id = fields.Many2one(
'res.partner', 'Private Address', help='Enter here the private address of the employee, not the one linked to your company.',
'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)
is_address_home_a_company = fields.Boolean(
'The employee adress has a company linked',
compute='_compute_is_address_home_a_company',
)
private_email = fields.Char(related='address_home_id.email', string="Private Email", readonly=False, groups="hr.group_hr_user")
country_id = fields.Many2one(
'res.country', 'Nationality (Country)', groups="hr.group_hr_user", tracking=True)
gender = fields.Selection([
@@ -90,8 +91,7 @@ class HrEmployeePrivate(models.Model):
study_school = fields.Char("School", groups="hr.group_hr_user", tracking=True)
emergency_contact = fields.Char("Emergency Contact", groups="hr.group_hr_user", tracking=True)
emergency_phone = fields.Char("Emergency Phone", groups="hr.group_hr_user", tracking=True)
km_home_work = fields.Integer(string="Km home-work", groups="hr.group_hr_user", tracking=True)
google_drive_link = fields.Char(string="Employee Documents", groups="hr.group_hr_user", tracking=True)
km_home_work = fields.Integer(string="Km Home-Work", groups="hr.group_hr_user", tracking=True)
# image: all image fields are base64 encoded and PIL-supported
image = fields.Binary(
+1
View File
@@ -28,6 +28,7 @@ class HrEmployeePublic(models.Model):
user_id = fields.Many2one(readonly=True)
resource_id = fields.Many2one(readonly=True)
resource_calendar_id = fields.Many2one(readonly=True)
color = fields.Integer(readonly=True)
# hr.employee.public specific fields
child_ids = fields.One2many('hr.employee.public', 'parent_id', string='Direct subordinates', readonly=True)
+6 -6
View File
@@ -9,13 +9,13 @@ class ResConfigSettings(models.TransientModel):
resource_calendar_id = fields.Many2one(
'resource.calendar', 'Company Working Hours',
related='company_id.resource_calendar_id', readonly=False)
module_hr_org_chart = fields.Boolean(string="Show Organizational Chart")
module_hr_presence = fields.Boolean(string="Advanced control presence of employees")
module_hr_org_chart = fields.Boolean(string="Organizational Chart")
module_hr_presence = fields.Boolean(string="Advanced Employees Presence Check Based on")
module_hr_skills = fields.Boolean(string="Employee Skills and Resumé")
hr_presence_control_login = fields.Boolean(string="According to the system login (User status on chat)", config_parameter='hr.hr_presence_control_login')
hr_presence_control_email = fields.Boolean(string="According to the amount of sent emails", config_parameter='hr_presence.hr_presence_control_email')
hr_presence_control_ip = fields.Boolean(string="According to the IP address", config_parameter='hr_presence.hr_presence_control_ip')
module_hr_attendance = fields.Boolean(string="According to the Attendance module.")
hr_presence_control_login = fields.Boolean(string="The system login (User status on chat)", config_parameter='hr.hr_presence_control_login')
hr_presence_control_email = fields.Boolean(string="The amount of sent emails", config_parameter='hr_presence.hr_presence_control_email')
hr_presence_control_ip = fields.Boolean(string="The IP address", config_parameter='hr_presence.hr_presence_control_ip')
module_hr_attendance = fields.Boolean(string="The Attendance module.")
hr_presence_control_email_amount = fields.Integer(related="company_id.hr_presence_control_email_amount", readonly=False)
hr_presence_control_ip_list = fields.Char(related="company_id.hr_presence_control_ip_list", readonly=False)
hr_employee_self_edit = fields.Boolean(string="Employee Edition", config_parameter='hr.hr_employee_self_edit')
+1 -2
View File
@@ -25,6 +25,7 @@ class User(models.Model):
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)
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)
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)
@@ -44,7 +45,6 @@ class User(models.Model):
visa_no = fields.Char(related='employee_id.visa_no', readonly=False, related_sudo=False)
permit_no = fields.Char(related='employee_id.permit_no', readonly=False, related_sudo=False)
visa_expire = fields.Date(related='employee_id.visa_expire', readonly=False, related_sudo=False)
google_drive_link = fields.Char(related='employee_id.google_drive_link', readonly=False, related_sudo=False)
additional_note = fields.Text(related='employee_id.additional_note', readonly=False, related_sudo=False)
barcode = fields.Char(related='employee_id.barcode', readonly=False, related_sudo=False)
pin = fields.Char(related='employee_id.pin', readonly=False, related_sudo=False)
@@ -92,7 +92,6 @@ class User(models.Model):
'employee_bank_account_id',
'employee_country_id',
'gender',
'google_drive_link',
'identification_id',
'is_address_home_a_company',
'job_title',
+14 -5
View File
@@ -8,6 +8,14 @@
<field name="arch" type="xml">
<search string="Employees">
<field name="name" string="Employees" filter_domain="['|',('work_email','ilike',self),('name','ilike',self)]"/>
<field name="job_title" string="Job Title"/>
<field name="department_id" string="Department"/>
<field name="company_id" string="Company"/>
<group expand="0" string="Group By">
<filter name="group_manager" string="Manager" domain="[]" context="{'group_by':'parent_id'}"/>
<filter name="group_department" string="Department" domain="[]" context="{'group_by':'department_id'}"/>
<filter name="group_company" string="Company" domain="[]" context="{'group_by':'company_id'}"/>
</group>
</search>
</field>
</record>
@@ -43,6 +51,7 @@
<group>
<field name="department_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="parent_id"/>
</group>
</group>
<notebook>
@@ -55,8 +64,7 @@
options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="work_location"/>
</group>
<group name="managers" string="Managers">
<field name="parent_id"/>
<group name="managers" string="Responsibles">
<field name="coach_id"/>
</group>
<group string="Schedule" groups="base.group_no_one">
@@ -81,7 +89,7 @@
<field name="work_email"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="department_id"/>
<field name="job_id"/>
<field name="job_title"/>
<field name="parent_id"/>
<field name="coach_id" invisible="1"/>
</tree>
@@ -119,12 +127,13 @@
</div>
<field name="name"/>
</strong>
<span t-if="record.job_id.raw_value" class="o_kanban_record_subtitle"><field name="job_id"/></span>
<span t-if="record.job_title.raw_value" class="o_kanban_record_subtitle"><field name="job_title"/></span>
</div>
</div>
<ul>
<li id="last_login"/>
<li t-if="record.work_location.raw_value"><field name="work_location"/></li>
<li t-if="record.work_email.raw_value"><field name="work_email" /></li>
<li t-if="record.work_phone.raw_value"><field name="work_phone" /></li>
</ul>
</div>
</div>
+13 -21
View File
@@ -10,7 +10,7 @@
<field name="name" string="Employees" filter_domain="['|',('work_email','ilike',self),('name','ilike',self)]"/>
<field name="department_id" operator="child_of"/>
<field name="category_ids" groups="hr.group_hr_user"/>
<field name="job_id" />
<field name="job_title" />
<field name="company_id" groups="base.group_multi_company"/>
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
<separator/>
@@ -77,7 +77,7 @@
<field name="name" placeholder="Employee's Name" required="True"/>
</h1>
<h2>
<field name="job_title" placeholder="Job Title" />
<field name="job_title" placeholder="Job Position" />
</h2>
<field name="category_ids" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}" placeholder="Tags" groups="hr.group_hr_manager"/>
</div>
@@ -90,7 +90,8 @@
<group>
<field name="department_id"/>
<field name="job_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="parent_id"/>
<field name="company_id" invisible="1"/>
</group>
</group>
<notebook>
@@ -103,8 +104,7 @@
options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="work_location"/>
</group>
<group name="managers" string="Managers">
<field name="parent_id"/>
<group name="managers" string="Responsibles">
<field name="coach_id"/>
</group>
<group name="departure" string="Departure" attrs="{'invisible': [('active', '=', True)]}">
@@ -120,23 +120,17 @@
</page>
<page name="personal_information" string="Private Information" groups="hr.group_hr_user">
<group>
<group string="Contact Information">
<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}'/>
<field name="is_address_home_a_company" invisible="1"/>
<div class="text-warning" groups="hr.group_hr_user"
attrs="{'invisible': [('is_address_home_a_company','=', False)]}">
Use here the home address of the employee.
This private address is used in the expense report reimbursement document.
It should be different from the work address.
</div>
<field name="km_home_work" groups="hr.group_hr_user"/>
<field name="phone" groups="hr.group_hr_user"/>
<field name="private_email" string="Email"/>
<field name="phone" groups="hr.group_hr_user" string="Phone"/>
<field name="bank_account_id" context="{'default_partner_id': address_home_id}"/>
<field name="km_home_work" groups="hr.group_hr_user"/>
</group>
<group string="Citizenship">
<field name="country_id" options='{"no_open": True, "no_create": True}'/>
@@ -175,16 +169,13 @@
<field name="study_school"/>
</group>
</group>
<group>
<field name="google_drive_link" widget="url"/>
</group>
</page>
<page name="hr_settings" string="HR Settings" groups="hr.group_hr_user">
<group>
<group string='Status' name="active_group">
<field name="user_id" string="Related User"/>
</group>
<group string="Login Information" name="identification_group">
<group string="Attendance" name="identification_group">
<field name="pin"/>
<field name="barcode"/>
<button string="Generate Badge ID" class="btn btn-primary" type="object" name="generate_random_barcode" attrs="{'invisible': [('barcode', '!=', False)]}"/>
@@ -251,13 +242,14 @@
</div>
<field name="name" placeholder="Employee's Name"/>
</strong>
<span t-if="record.job_id.raw_value" class="o_kanban_record_subtitle"><field name="job_id"/></span>
<span t-if="record.job_title.raw_value" class="o_kanban_record_subtitle"><field name="job_title"/></span>
</div>
</div>
<field name="category_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<ul>
<li id="last_login"/>
<li t-if="record.work_location.raw_value"><field name="work_location"/></li>
<li t-if="record.work_email.raw_value"><field name="work_email" /></li>
<li t-if="record.work_phone.raw_value"><field name="work_phone" /></li>
</ul>
</div>
</div>
+1 -1
View File
@@ -31,7 +31,7 @@
<menuitem
id="menu_hr_employee"
name="Company Address"
name="Employee Directory"
action="hr_employee_public_action"
parent="menu_hr_root"
sequence="4"/>
@@ -16,7 +16,7 @@
<div class="o_setting_right_pane">
<label for="module_hr_skills"/>
<div class="text-muted">
Manage skills, knowledge and resumé of your employees
Enrich Employees Profiles with Resume, Skills and knowledge
</div>
</div>
</div>
@@ -73,7 +73,7 @@
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." role="img" aria-label="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="row">
<div class="text-muted col-lg-8">
Set default calendar used to compute time allocation for leaves, timesheets, ...
Set default company schedule to manage your employees working time
</div>
</div>
<div class="content-group">
+25 -22
View File
@@ -15,6 +15,19 @@
<footer position="replace"/>
<h1 position="replace"/>
<xpath expr="//field[@name='image']" position="replace"/>
<xpath expr="//field[@name='email']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//group[@name='preferences']" position="before">
<separator string="Email &amp; Notifications" />
</xpath>
<field name="signature" position="replace" />
<xpath expr="//field[@name='company_id']" position="after">
<field name="signature" />
</xpath>
<xpath expr="//field[@name='company_id']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
</field>
</record>
@@ -71,7 +84,7 @@
<field name="category_ids" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}" placeholder="Tags" groups="hr.group_hr_user"/>
</div>
<h2>
<field name="job_title"/>
<field name="job_title" placeholder="Job Position"/>
</h2>
<group>
<group>
@@ -81,10 +94,14 @@
</group>
<group>
<field name="department_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="company_id" invisible="1"/>
<field name="employee_parent_id"/>
</group>
</group>
<notebook>
<page name="preferences" string="Preferences">
<div>$0</div>
</page>
<page name="public" string="Work Information">
<div id="o_work_employee_container"> <!-- These two div are used to position org_chart -->
<div id="o_work_employee_main">
@@ -94,8 +111,7 @@
options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="work_location"/>
</group>
<group name="managers" string="Managers">
<field name="employee_parent_id"/>
<group name="managers" string="Responsibles">
<field name="coach_id"/>
</group>
</div>
@@ -112,16 +128,10 @@
'default_type': 'private',
'form_view_ref': 'base.res_partner_view_form_private'}"
options='{"always_reload": True, "highlight_first_line": True}'/>
<field name="is_address_home_a_company" invisible="1"/>
<div class="text-warning"
attrs="{'invisible': [('is_address_home_a_company','=', False)]}">
Use here the home address of the employee.
This private address is used in the expense report reimbursement document.
It should be different from the work address.
</div>
<field name="km_home_work"/>
<field name="employee_phone"/>
<field name="private_email" string="Email"/>
<field name="employee_phone" string="Phone"/>
<field name="employee_bank_account_id"/>
<field name="km_home_work"/>
</group>
<group string="Citizenship">
<field name="employee_country_id" options='{"no_open": True, "no_create": True}'/>
@@ -155,18 +165,11 @@
<field name="visa_expire"/>
</group>
</group>
<group>
<field name="google_drive_link" widget="url"/>
</group>
</page>
<page name="preferences" string="Preferences">
<div>$0</div>
</page>
<page name="hr_settings" string="HR Settings">
<group>
<group string='Status' name="active_group">
</group>
<group string="Login Information" name="identification_group">
<group string='Status' name="active_group" invisible="1"></group>
<group string="Attendance" name="identification_group">
<field name="pin"/>
<field name="barcode"/>
</group>
@@ -13,9 +13,12 @@ class HrEmployeeBase(models.AbstractModel):
attendance_ids = fields.One2many('hr.attendance', 'employee_id', help='list of attendances for the employee')
last_attendance_id = fields.Many2one('hr.attendance', compute='_compute_last_attendance_id', store=True)
last_check_in = fields.Datetime(related='last_attendance_id.check_in', store=True)
last_check_out = fields.Datetime(related='last_attendance_id.check_out', store=True)
attendance_state = fields.Selection(string="Attendance Status", compute='_compute_attendance_state', selection=[('checked_out', "Checked out"), ('checked_in', "Checked in")])
hours_last_month = fields.Float(compute='_compute_hours_last_month')
hours_today = fields.Float(compute='_compute_hours_today')
hours_last_month_display = fields.Char(compute='_compute_hours_last_month')
def _compute_presence_state(self):
"""
@@ -42,6 +45,7 @@ class HrEmployeeBase(models.AbstractModel):
('check_out', '<=', end),
])
employee.hours_last_month = sum(attendances.mapped('worked_hours'))
employee.hours_last_month_display = "%g" % employee.hours_last_month
def _compute_hours_today(self):
now = fields.Datetime.now()
+4
View File
@@ -8,7 +8,10 @@ class User(models.Model):
_inherit = ['res.users']
hours_last_month = fields.Float(related='employee_id.hours_last_month')
hours_last_month_display = fields.Char(related='employee_id.hours_last_month_display')
attendance_state = fields.Selection(related='employee_id.attendance_state')
last_check_in = fields.Datetime(related='employee_id.last_attendance_id.check_in')
last_check_out = fields.Datetime(related='employee_id.last_attendance_id.check_out')
def __init__(self, pool, cr):
""" Override of __init__ to add access rights.
@@ -17,6 +20,7 @@ class User(models.Model):
"""
attendance_readable_fields = [
'hours_last_month',
'hours_last_month_display',
'attendance_state',
]
super(User, self).__init__(pool, cr)
@@ -0,0 +1,19 @@
odoo.define('hr_attendance.widget', function (require) {
"use strict";
var basic_fields = require('web.basic_fields');
var field_registry = require('web.field_registry');
var RelativeTime = basic_fields.FieldDateTime.extend({
_formatValue: function (val) {
if (!(val && val._isAMomentObject)) {
return;
}
return val.fromNow(true);
},
});
field_registry.add('relative_time', RelativeTime);
return RelativeTime;
});
@@ -24,7 +24,15 @@
('attendance_state', '=', 'checked_out')]}">
<div role="img" id="oe_hr_attendance_status" class="fa fa-fw fa-circle o_button_icon oe_hr_attendance_status_green" attrs="{'invisible': [('attendance_state', '=', 'checked_out')]}" aria-label="Available" title="Available"/>
<div role="img" id="oe_hr_attendance_status" class="fa fa-fw fa-circle o_button_icon oe_hr_attendance_status_red" attrs="{'invisible': [('attendance_state', '=', 'checked_in')]}" aria-label="Not available" title="Not available"/>
<div class="o_stat_info">
<div class="o_stat_info" attrs="{'invisible': ['|', ('last_check_in', '=', False), ('last_check_out', '!=', False)]}">
<span class="o_stat_text">Present Since</span>
<span class="o_stat_value"><field name="last_check_in" widget="relative_time"/></span>
</div>
<div class="o_stat_info" attrs="{'invisible': [('last_check_out', '=', False)]}">
<span class="o_stat_text">Absent Since</span>
<span class="o_stat_value"><field name="last_check_out" widget="relative_time"/></span>
</div>
<div class="o_stat_info" attrs="{'invisible': [('last_check_in', '!=', False)]}">
<span class="o_stat_text">Attendance</span>
</div>
</button>
@@ -37,7 +45,7 @@
help="Worked hours last month">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="hours_last_month"/> Hours
<field name="hours_last_month_display"/> Hours
</span>
<span class="o_stat_text">
Last Month
@@ -66,7 +74,15 @@
type="action" attrs="{'invisible': ['|', ('attendance_state', '=', False), '&amp;', ('hr_presence_state', '=', 'present'), ('attendance_state', '=', 'checked_out')]}">
<div role="img" id="oe_hr_attendance_status" class="fa fa-fw fa-circle o_button_icon oe_hr_attendance_status_green" attrs="{'invisible': [('attendance_state', '=', 'checked_out')]}" aria-label="Available" title="Available"/>
<div role="img" id="oe_hr_attendance_status" class="fa fa-fw fa-circle o_button_icon oe_hr_attendance_status_red" attrs="{'invisible': [('attendance_state', '=', 'checked_in')]}" aria-label="Not available" title="Not available"/>
<div class="o_stat_info">
<div class="o_stat_info" attrs="{'invisible': ['|', ('last_check_in', '=', False), ('last_check_out', '!=', False)]}">
<span class="o_stat_text">Present Since</span>
<span class="o_stat_value"><field name="last_check_in" widget="relative_time"/></span>
</div>
<div class="o_stat_info" attrs="{'invisible': [('last_check_out', '=', False)]}">
<span class="o_stat_text">Absent Since</span>
<span class="o_stat_value"><field name="last_check_out" widget="relative_time"/></span>
</div>
<div class="o_stat_info" attrs="{'invisible': [('last_check_in', '!=', False)]}">
<span class="o_stat_text">Attendance</span>
</div>
</button>
@@ -82,7 +98,7 @@
help="Worked hours last month">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="hours_last_month"/> Hours
<field name="hours_last_month_display"/> Hours
</span>
<span class="o_stat_text">
Last Month
@@ -7,6 +7,7 @@
<script type="text/javascript" src="/hr_attendance/static/src/js/kiosk_mode.js"></script>
<script type="text/javascript" src="/hr_attendance/static/src/js/kiosk_confirm.js"></script>
<script type="text/javascript" src="/hr_attendance/static/src/js/my_attendances.js"></script>
<script type="text/javascript" src="/hr_attendance/static/src/js/time_widget.js"></script>
<link rel="stylesheet" type="text/scss" href="/hr_attendance/static/src/scss/hr_attendance.scss"/>
</xpath>
</template>
@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
hr_presence_control_attendance = fields.Boolean(string="the Attendance module", config_parameter='hr_presence.hr_presence_control_attendance')
@@ -27,12 +27,6 @@
<field name="contracts_count" widget="statinfo" string="Contracts"/>
</button>
</div>
<xpath expr="//group[@name='active_group']" position="after">
<group string="Current Contract">
<field name="medic_exam" string="Medical Exam"/>
<field name="vehicle"/>
</group>
</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>
@@ -89,12 +83,6 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="hr.res_users_view_form_profile"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='active_group']" position="after">
<group string="Current Contract">
<field name="medic_exam" string="Medical Exam"/>
<field name="vehicle"/>
</group>
</xpath>
<xpath expr="//field[@name='employee_bank_account_id']" position="replace">
<field name="employee_bank_account_id" context="{'display_partner':True}"/>
</xpath>
+1 -1
View File
@@ -18,7 +18,7 @@ class Employee(models.Model):
return [('groups_id', 'in', group.ids)] if group else []
expense_manager_id = fields.Many2one(
'res.users', string='Expense Responsible',
'res.users', string='Expense',
domain=_group_hr_expense_user_domain,
help="User responsible of expense approval. Should be Expense approver.")
+5 -1
View File
@@ -15,7 +15,7 @@ class HrEmployeeBase(models.AbstractModel):
return [('groups_id', 'in', group.ids)] if group else []
leave_manager_id = fields.Many2one(
'res.users', string='Time Off Responsible',
'res.users', string='Time Off',
domain=_group_hr_user_domain,
help="User responsible of leaves approval. Should be Team Leader or Department Manager.")
remaining_leaves = fields.Float(
@@ -39,6 +39,8 @@ class HrEmployeeBase(models.AbstractModel):
allocation_used_count = fields.Float('Total number of days off used', compute='_compute_total_allocation_used')
show_leaves = fields.Boolean('Able to see Remaining Time Off', compute='_compute_show_leaves')
is_absent = fields.Boolean('Absent Today', compute='_compute_leave_status', search='_search_absent_employee')
allocation_display = fields.Char(compute='_compute_allocation_count')
allocation_used_display = fields.Char(compute='_compute_total_allocation_used')
def _get_date_start_work(self):
return self.create_date
@@ -86,10 +88,12 @@ class HrEmployeeBase(models.AbstractModel):
('date_to', '>=', datetime.date.today()),
])
employee.allocation_count = sum(allocations.mapped('number_of_days'))
employee.allocation_display = "%g" % employee.allocation_count
def _compute_total_allocation_used(self):
for employee in self:
employee.allocation_used_count = employee.allocation_count - employee.remaining_leaves
employee.allocation_used_display = "%g" % employee.allocation_used_count
def _compute_presence_state(self):
super()._compute_presence_state()
+4
View File
@@ -13,6 +13,8 @@ class User(models.Model):
allocation_count = fields.Float(related='employee_id.allocation_count')
leave_date_to = fields.Date(related='employee_id.leave_date_to')
is_absent = fields.Boolean(related='employee_id.is_absent')
allocation_used_display = fields.Char(related='employee_id.allocation_used_display')
allocation_display = fields.Char(related='employee_id.allocation_display')
def __init__(self, pool, cr):
""" Override of __init__ to add access rights.
@@ -27,6 +29,8 @@ class User(models.Model):
'allocation_count',
'leave_date_to',
'is_absent',
'allocation_used_display',
'allocation_display',
]
init_res = super(User, self).__init__(pool, cr)
# duplicate list to avoid modifying the original reference
+5 -5
View File
@@ -171,8 +171,8 @@
help="Remaining leaves">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="allocation_used_count" digits="[42,1]"/>/
<field name="allocation_count" digits="[42,1]"/>
<field name="allocation_used_display"/>/
<field name="allocation_display"/>
</span>
<span class="o_stat_text">
Time Off
@@ -198,7 +198,7 @@
<button disabled="1"
class="oe_stat_button"
attrs="{'invisible': [('is_absent', '=', False)]}">
<div role="img" class="fa fa-fw fa-plane o_button_icon text-danger" aria-label="Absent Until" title="Absent Until"/>
<div role="img" class="fa fa-fw fa-plane o_button_icon text-warning" aria-label="Absent Until" title="Absent Until"/>
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="leave_date_to"/>
@@ -258,8 +258,8 @@
help="Remaining leaves">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="allocation_used_count" digits="[42,1]"/>/
<field name="allocation_count" digits="[42,1]"/>
<field name="allocation_used_display"/>/
<field name="allocation_display"/>
</span>
<span class="o_stat_text">
Time Off
@@ -38,6 +38,7 @@ class HrOrgChartController(http.Controller):
link='/mail/view?model=%s&res_id=%s' % ('hr.employee.public', employee.id,),
job_id=job.id,
job_name=job.name or '',
job_title=employee.job_title or '',
direct_sub_count=len(employee.child_ids),
indirect_sub_count=employee.child_all_count,
)
@@ -36,12 +36,12 @@
<t t-if="!is_self">
<a t-att-href="employee.link" class="o_employee_redirect" t-att-data-employee-id="employee.id">
<h5 class="o_media_heading"><b><t t-esc="employee.name"/></b></h5>
<strong><t t-esc="employee.job_name"/></strong>
<strong><t t-esc="employee.job_title"/></strong>
</a>
</t>
<t t-if="is_self">
<h5 class="o_media_heading"><b><t t-esc="employee.name"/></b></h5>
<strong><t t-esc="employee.job_name"/></strong>
<strong><t t-esc="employee.job_title"/></strong>
</t>
</div>
</div>
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
hr_presence_control_login = fields.Boolean(string="the system login (User status on chat)", config_parameter='hr_presence.hr_presence_control_login')
hr_presence_control_email = fields.Boolean(string="the amount of sent emails", config_parameter='hr_presence.hr_presence_control_email')
hr_presence_control_ip = fields.Boolean(string="the IP address", config_parameter='hr_presence.hr_presence_control_ip')
hr_presence_control_email_amount = fields.Integer(related="company_id.hr_presence_control_email_amount", readonly=False)
hr_presence_control_ip_list = fields.Char(related="company_id.hr_presence_control_ip_list", readonly=False)
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.hr</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="70"/>
<field name="inherit_id" ref="hr.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@data-key='hr']//div[hasclass('o_settings_container')]" position="inside">
<div class="col-12 col-lg-6 o_setting_box" title="Employees Presence Check Based on">
<div class="o_setting_left_pane">
<field name="module_hr_presence"/>
</div>
<div class="o_setting_right_pane">
<label for="module_hr_presence"/>
<div class="content-group" name="hr_presence_options">
<div class="row">
<field name="hr_presence_control_login" class="col-lg-1 ml16"/>
<label for="hr_presence_control_login" class="col-lg-10 o_light_label"/>
</div>
<div class="row">
<field name="hr_presence_control_email" class="col-lg-1 ml16"/>
<label for="hr_presence_control_email" class="col-lg-10 o_light_label"/>
</div>
<div attrs="{'invisible': [('hr_presence_control_email', '=', False)]}">
<span>At least </span>
<field name="hr_presence_control_email_amount"/>
<span> sent emails</span>
</div>
<div class="row">
<field name="hr_presence_control_ip" class="col-lg-1 ml16"/>
<label for="hr_presence_control_ip" class="col-lg-10 o_light_label"/>
</div>
<div attrs="{'invisible': [('hr_presence_control_ip', '=', False)]}">
<span>IP List (comma separated):</span>
<field name="hr_presence_control_ip_list"/>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>
+1
View File
@@ -23,6 +23,7 @@
'security/ir.model.access.csv',
'data/hr_recruitment_data.xml',
'data/digest_data.xml',
'data/hr_recruitment_templates.xml',
'views/hr_recruitment_views.xml',
'views/res_config_settings_views.xml',
'views/hr_recruitment_templates.xml',
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="applicant_hired_template">
Applicant hired<br/>
<ul>
<li>Employee: <a href="#" t-att-data-oe-id="applicant.emp_id.id" data-oe-model="hr.employee"><t t-esc="applicant.emp_id.name"/></a></li>
</ul>
</template>
</data>
</odoo>
@@ -156,10 +156,10 @@ class Applicant(models.Model):
day_close = fields.Float(compute='_compute_day', string="Days to Close")
delay_close = fields.Float(compute="_compute_day", string='Delay to Close', readonly=True, group_operator="avg", help="Number of days to close", store=True)
color = fields.Integer("Color Index", default=0)
emp_id = fields.Many2one('hr.employee', string="Employee", tracking=True, help="Employee linked to the applicant.")
emp_id = fields.Many2one('hr.employee', string="Employee", help="Employee linked to the applicant.")
user_email = fields.Char(related='user_id.email', type="char", string="User Email", readonly=True)
attachment_number = fields.Integer(compute='_get_attachment_number', string="Number of Attachments")
employee_name = fields.Char(related='emp_id.name', string="Employee Name", readonly=False)
employee_name = fields.Char(related='emp_id.name', string="Employee Name", readonly=False, tracking=False)
attachment_ids = fields.One2many('ir.attachment', 'res_id', domain=[('res_model', '=', 'hr.applicant')], string='Attachments')
kanban_state = fields.Selection([
('normal', 'Grey'),
@@ -461,6 +461,7 @@ class Applicant(models.Model):
employee = self.env['hr.employee'].create({
'name': applicant.partner_name or contact_name,
'job_id': applicant.job_id.id or False,
'job_title': applicant.job_id.name,
'address_home_id': address_id,
'department_id': applicant.department_id.id or False,
'address_id': applicant.company_id and applicant.company_id.partner_id
@@ -475,6 +476,10 @@ class Applicant(models.Model):
applicant.job_id.message_post(
body=_('New Employee %s Hired') % applicant.partner_name if applicant.partner_name else applicant.name,
subtype="hr_recruitment.mt_job_applicant_hired")
applicant.message_post_with_view(
'hr_recruitment.applicant_hired_template',
values={'applicant': applicant},
subtype_id=self.env.ref("hr_recruitment.mt_applicant_hired").id)
employee_action = self.env.ref('hr.open_view_employee_list')
dict_act_window = employee_action.read([])[0]
+13 -1
View File
@@ -16,7 +16,19 @@
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_filter"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='job_id']" position="after">
<xpath expr="//field[@name='job_title']" position="after">
<field name="employee_skill_ids"/>
<field name="resume_line_ids" string="Resumé" filter_domain="['|', ('resume_line_ids.name', 'ilike', self), ('resume_line_ids.description', 'ilike', self)]"/>
</xpath>
</field>
</record>
<record id="hr_employee_public_view_search" model="ir.ui.view">
<field name="name">hr.employee.public.skill.search</field>
<field name="model">hr.employee.public</field>
<field name="inherit_id" ref="hr.hr_employee_public_view_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='company_id']" position="after">
<field name="employee_skill_ids"/>
<field name="resume_line_ids" string="Resumé" filter_domain="['|', ('resume_line_ids.name', 'ilike', self), ('resume_line_ids.description', 'ilike', self)]"/>
</xpath>
+3 -7
View File
@@ -8,12 +8,8 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
<field name="arch" type="xml">
<xpath expr="//footer" position="before">
<group name="livechat" string="Livechat Preferences">
<group>
<field name="livechat_username" readonly="0"/>
</group>
</group>
<xpath expr="//group[@name='out of office']" position="inside">
<field name="livechat_username" string="Online Chat Username" readonly="0"/>
</xpath>
</field>
</record>
@@ -24,7 +20,7 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='messaging']" position="after">
<xpath expr="//group[@name='messaging']" position="inside">
<group name="livechat" string="Livechat">
<field name="livechat_username"/>
</group>
+1 -1
View File
@@ -28,7 +28,7 @@ class Users(models.Model):
notification_type = fields.Selection([
('email', 'Handle by Emails'),
('inbox', 'Handle in Odoo')],
'Notification Management', required=True, default='email',
'Notification', required=True, default='email',
help="Policy on how to handle Chatter notifications:\n"
"- Handle by Emails: notifications are sent to your email address\n"
"- Handle in Odoo: notifications appear in your Odoo Inbox")
+1 -1
View File
@@ -13,7 +13,7 @@
<field name="notification_type" widget="radio" readonly="0"/>
</field>
<xpath expr="//group[@name='signature']" position="after">
<group string="Chat Status" name="out of office">
<group string="Chat" name="out of office">
<field name="out_of_office_message" readonly="0"/>
</group>
</xpath>
@@ -106,7 +106,7 @@ function formatChar(value, field, options) {
* @returns {string}
*/
function formatDate(value, field, options) {
if (value === false) {
if (value === false || isNaN(value)) {
return "";
}
if (field && field.type === 'datetime') {
+1 -1
View File
@@ -240,7 +240,7 @@ class Users(models.Model):
# Special behavior for this field: res.company.search() will only return the companies
# available to the current user (should be the user's companies?), when the user_preference
# context is set.
company_id = fields.Many2one('res.company', string='Default Company', required=True, default=lambda self: self.env.company.id,
company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company.id,
help='The default company for this user.', context={'user_preference': True})
company_ids = fields.Many2many('res.company', 'res_company_users_rel', 'user_id', 'cid',
string='Companies', default=lambda self: self.env.company.ids)