[IMP] hr_contract: Improve general UX (back2basics)
- hr_contract: Add kanban view on contract history action - hr_contract: Display the avg wage, not the sum on aggregates - hr_contract: Improve contract history list view - hr_contract: Improve contract history tree view - hr_contract: Improve contract history search view - hr_contract: Display wage on contract kanban view - hr_contract: Improve contract tree view - hr_contract: Improve contract search view - hr_contract: Make hr_responsible required It is useful in multiple HR flows. - hr_contract: Improve contract history form view - hr_contract: Improve contract form view - hr_work_entry: Improve work entry computed name - hr_work_entry: Improve work entries tree view - hr_work_entry: Improve work entry type form view closes odoo/odoo#72402 Taskid: 2558171 Related: odoo/enterprise#19124 Related: odoo/upgrade#2583 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
@@ -33,6 +33,7 @@ class Contract(models.Model):
|
||||
default=lambda self: self.env.company.resource_calendar_id.id, copy=False, index=True,
|
||||
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
|
||||
wage = fields.Monetary('Wage', required=True, tracking=True, help="Employee's monthly gross wage.")
|
||||
contract_wage = fields.Monetary('Contract Wage', compute='_compute_contract_wage')
|
||||
notes = fields.Html('Notes')
|
||||
state = fields.Selection([
|
||||
('draft', 'New'),
|
||||
@@ -193,6 +194,11 @@ class Contract(models.Model):
|
||||
for contract in self:
|
||||
contract.employee_id.sudo().write({'contract_id': contract.id})
|
||||
|
||||
@api.depends('wage')
|
||||
def _compute_contract_wage(self):
|
||||
for contract in self:
|
||||
contract.contract_wage = contract._get_contract_wage()
|
||||
|
||||
def _get_contract_wage(self):
|
||||
self.ensure_one()
|
||||
return self[self._get_contract_wage_field()]
|
||||
|
||||
@@ -9,6 +9,7 @@ class ContractHistory(models.Model):
|
||||
_name = 'hr.contract.history'
|
||||
_description = 'Contract history'
|
||||
_auto = False
|
||||
_order = 'is_under_contract'
|
||||
|
||||
# Even though it would have been obvious to use the reference contract's id as the id of the
|
||||
# hr.contract.history model, it turned out it was a bad idea as this id could change (for instance if a
|
||||
@@ -22,6 +23,7 @@ class ContractHistory(models.Model):
|
||||
date_start = fields.Date('Start Date', readonly=True)
|
||||
date_end = fields.Date('End Date', readonly=True)
|
||||
employee_id = fields.Many2one('hr.employee', string='Employee', readonly=True)
|
||||
active_employee = fields.Boolean('Active Employee', readonly=True)
|
||||
is_under_contract = fields.Boolean('Is Currently Under Contract', readonly=True)
|
||||
department_id = fields.Many2one('hr.department', string='Department', readonly=True)
|
||||
structure_type_id = fields.Many2one('hr.payroll.structure.type', string='Salary Structure Type', readonly=True)
|
||||
@@ -33,35 +35,42 @@ class ContractHistory(models.Model):
|
||||
('close', 'Expired'),
|
||||
('cancel', 'Cancelled')
|
||||
], string='Status', readonly=True)
|
||||
resource_calendar_id = fields.Many2one('resource.calendar', readonly=True)
|
||||
wage = fields.Monetary('Wage', help="Employee's monthly gross wage.", readonly=True)
|
||||
resource_calendar_id = fields.Many2one('resource.calendar', string="Working Schedule", readonly=True)
|
||||
wage = fields.Monetary('Wage', help="Employee's monthly gross wage.", readonly=True, group_operator="avg")
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True)
|
||||
company_country_id = fields.Many2one('res.country', string="Company country", related='company_id.country_id', readonly=True)
|
||||
country_code = fields.Char(related='company_country_id.code', readonly=True)
|
||||
currency_id = fields.Many2one(string='Currency', related='company_id.currency_id', readonly=True)
|
||||
contract_type_id = fields.Many2one('hr.contract.type', 'Contract Type', readonly=True)
|
||||
contract_ids = fields.One2many('hr.contract', string='Contracts', compute='_compute_contract_ids', readonly=True)
|
||||
contract_count = fields.Integer(compute='_compute_contract_count', string="# Contracts")
|
||||
under_contract_state = fields.Selection([
|
||||
('done', 'Under Contract'),
|
||||
('blocked', 'Not Under Contract')
|
||||
], string='Contractual Status', compute='_compute_under_contract_state')
|
||||
activity_state = fields.Selection(related='contract_id.activity_state')
|
||||
|
||||
@api.depends('contract_ids')
|
||||
def _compute_contract_count(self):
|
||||
for history in self:
|
||||
history.contract_count = len(history.contract_ids)
|
||||
|
||||
@api.depends('is_under_contract')
|
||||
def _compute_under_contract_state(self):
|
||||
for contract in self:
|
||||
contract.under_contract_state = 'done' if contract.is_under_contract else 'blocked'
|
||||
for history in self:
|
||||
history.under_contract_state = 'done' if history.is_under_contract else 'blocked'
|
||||
|
||||
@api.depends('employee_id.name')
|
||||
def _compute_display_name(self):
|
||||
for contract in self:
|
||||
contract.display_name = _("%s's Contracts History", contract.employee_id.name)
|
||||
for history in self:
|
||||
history.display_name = _("%s's Contracts History", history.employee_id.name)
|
||||
|
||||
@api.model
|
||||
def _get_fields(self):
|
||||
return ','.join('contract.%s' % name for name, field in self._fields.items()
|
||||
if field.store
|
||||
and field.type not in ['many2many', 'one2many', 'related']
|
||||
and field.name not in ['id', 'contract_id', 'employee_id', 'date_hired', 'is_under_contract'])
|
||||
and field.name not in ['id', 'contract_id', 'employee_id', 'date_hired', 'is_under_contract', 'active_employee'])
|
||||
|
||||
def init(self):
|
||||
tools.drop_view_if_exists(self.env.cr, self._table)
|
||||
@@ -92,6 +101,7 @@ class ContractHistory(models.Model):
|
||||
)
|
||||
SELECT employee.id AS id,
|
||||
employee.id AS employee_id,
|
||||
employee.active AS active_employee,
|
||||
contract.id AS contract_id,
|
||||
contract_information.is_under_contract::bool AS is_under_contract,
|
||||
employee.first_contract_date AS date_hired,
|
||||
@@ -102,7 +112,6 @@ class ContractHistory(models.Model):
|
||||
ON contract_information.employee_id = employee.id
|
||||
AND contract.company_id = employee.company_id
|
||||
WHERE employee.employee_type IN ('employee', 'student')
|
||||
AND (employee.active = true OR contract.state='draft')
|
||||
)""" % (self._table, self._get_fields()))
|
||||
|
||||
@api.depends('employee_id.contract_ids')
|
||||
|
||||
@@ -12,11 +12,16 @@
|
||||
<field name="resource_calendar_id"/>
|
||||
<field name="state"/>
|
||||
<field name="is_under_contract"/>
|
||||
<filter string="Running Contracts" name="open_contracts" domain="[('state', '=', 'open')]"/>
|
||||
<filter string="Contracts to Review" name="contract_to_review" domain="['|', ('state', 'in', ['draft', 'close', 'cancel']), ('is_under_contract', '!=', True)]"/>
|
||||
<filter string="No Contracts" name="no_contracts" domain="[('contract_id', '=', False)]"/>
|
||||
<filter string="Currently Under Contract" name="currently_under_contract" domain="[('is_under_contract', '=', True)]"/>
|
||||
<filter string="Contracts to Review" name="to_review" domain="['|', ('state', '=', 'draft'), ('is_under_contract', '!=', True)]"/>
|
||||
<filter string="Active Employees" name="active_employees" domain="[('active_employee', '=', True)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Job Position" name="job" domain="[]" context="{'group_by': 'job_id'}"/>
|
||||
<filter string="Status" name='group_by_state' domain="[]" context="{'group_by': 'state'}"/>
|
||||
<filter string="Reference Working Time" name="group_by_resource_calendar_id" domain="[]" context="{'group_by': 'resource_calendar_id'}"/>
|
||||
<filter string="Salary Structure Type" name="group_by_structure_type_id" domain="[]" context="{'group_by': 'structure_type_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -25,6 +30,7 @@
|
||||
<field name="name">Contracts</field>
|
||||
<field name="res_model">hr.contract.history</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="context">{'search_default_active_employees': 1}</field>
|
||||
</record>
|
||||
<record id="hr_contract_history_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.contract.history.form</field>
|
||||
@@ -37,7 +43,7 @@
|
||||
duplicate="false"
|
||||
import="false">
|
||||
<header>
|
||||
<button name="hr_contract_view_form_new_action" string="Create" type="object" groups="hr_contract.group_hr_contract_manager"/>
|
||||
<button name="hr_contract_view_form_new_action" string="Create" type="object" groups="hr_contract.group_hr_contract_manager" class="btn-primary"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box"/>
|
||||
@@ -59,11 +65,10 @@
|
||||
<field name="contract_id" invisible="1"/>
|
||||
<field name="company_country_id" invisible="1"/>
|
||||
<field name="country_code" invisible="1"/>
|
||||
<field name="date_hired"/>
|
||||
<field name="structure_type_id"/>
|
||||
<field string="Reference Working Time" name="resource_calendar_id"/>
|
||||
<field name="resource_calendar_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="wage"/>
|
||||
<field name="wage" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="department_id"/>
|
||||
@@ -87,13 +92,14 @@
|
||||
<field name="date_end"/>
|
||||
<field name="resource_calendar_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="wage"/>
|
||||
<field name="wage" string="Monthly Wage"/>
|
||||
<field name="state" widget="badge" decoration-info="state == 'draft'" decoration-warning="state == 'close'" decoration-success="state == 'open'"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Other Information" name="contract_others">
|
||||
<page string="Employee Information" name="contract_others">
|
||||
<group>
|
||||
<field name="date_hired"/>
|
||||
<field name="hr_responsible_id"/>
|
||||
<field name="company_id"/>
|
||||
</group>
|
||||
@@ -104,10 +110,11 @@
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_contract_history_view_list_action" model="ir.actions.act_window">
|
||||
<field name="name">Contracts</field>
|
||||
<field name="name">Employees</field>
|
||||
<field name="res_model">hr.contract.history</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="search_view_id" ref="hr_contract_history_view_search"/>
|
||||
<field name="context">{'search_default_active_employees': 1}</field>
|
||||
</record>
|
||||
<record id="hr_contract_history_to_review_view_list_action" model="ir.actions.act_window">
|
||||
<field name="name">Contracts to Review</field>
|
||||
@@ -117,6 +124,7 @@
|
||||
<field name="context">
|
||||
{
|
||||
'search_default_to_review': 1,
|
||||
'search_default_active_employees': 1
|
||||
}
|
||||
</field>
|
||||
</record>
|
||||
@@ -125,28 +133,65 @@
|
||||
<field name="model">hr.contract.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Contracts"
|
||||
default_order = 'date_start desc'
|
||||
default_order = 'is_under_contract, date_start desc'
|
||||
create="false"
|
||||
edit="false"
|
||||
delete="false"
|
||||
duplicate="false"
|
||||
import="false">
|
||||
<field name="under_contract_state" widget="state_selection"/>
|
||||
<field name="employee_id" widget="many2one_avatar_employee"/>
|
||||
<field name="date_hired"/>
|
||||
<field name="is_under_contract" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="date_start"/>
|
||||
<field string="Reference Working Time" name="resource_calendar_id"/>
|
||||
<field string="Reference Working Time" name="resource_calendar_id" optional="hide"/>
|
||||
<field name="under_contract_state" widget="state_selection" optional="hide"/>
|
||||
<field name="structure_type_id" optional="hide"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="wage"/>
|
||||
<field name="wage" optional="hide"/>
|
||||
<field name="state"
|
||||
widget="badge"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-warning="state == 'close'"
|
||||
decoration-success="state == 'open'"/>
|
||||
<field name="contract_count"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="hr_contract_history_view_kanban" model="ir.ui.view">
|
||||
<field name="name">hr.contract.history.view.kanban</field>
|
||||
<field name="model">hr.contract.history</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_small_column" default_order="date_end" sample="1">
|
||||
<field name="employee_id"/>
|
||||
<field name="activity_state"/>
|
||||
<field name="state"/>
|
||||
<progressbar field="activity_state" colors='{"planned": "success", "today": "warning", "overdue": "danger"}'/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_card oe_kanban_global_click">
|
||||
<div class="oe_kanban_content">
|
||||
<div class="o_hr_contract_state">
|
||||
<strong class="o_kanban_record_title">
|
||||
<field name="display_name"/>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="text-muted o_kanban_record_subtitle o_hr_contract_job_id">
|
||||
<field name="job_id"/>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<span class="float-right">
|
||||
<field name="employee_id" widget="many2one_avatar_employee"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_clear"></div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="hr_menu_contract_history"
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
<field name="department_id" operator="child_of"/>
|
||||
<field name="resource_calendar_id"/>
|
||||
<filter string="Running" name="running" domain="[('state', '=', 'open')]"/>
|
||||
<filter string="Need Action" name="to_renew" domain="['&', ('state', '=', 'open'), ('kanban_state', '=', 'blocked')]"/>
|
||||
<filter string="Not Running" name="not_running" domain="[('state', '!=', 'open')]"/>
|
||||
<separator />
|
||||
<filter string="Employed" name="current_employee" domain="[('employee_id.active', '=', True)]"/>
|
||||
<separator/>
|
||||
@@ -117,9 +117,12 @@
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('my_activity_date_deadline', '>', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Employee" name="employee" domain="[]" context="{'group_by': 'employee_id'}"/>
|
||||
<filter string="Job Position" name="job" domain="[]" context="{'group_by': 'job_id'}"/>
|
||||
<filter string="Status" name='group_by_state' domain="[]" context="{'group_by': 'state'}"/>
|
||||
<filter string="Status" name="group_by_state" domain="[]" context="{'group_by': 'state'}"/>
|
||||
<filter string="Employee" name="group_by_employee" domain="[]" context="{'group_by': 'employee_id'}"/>
|
||||
<filter string="Start Date" name="group_by_date_start" domain="[]" context="{'group_by': 'date_start'}"/>
|
||||
<filter string="Job Position" name="group_by_job" domain="[]" context="{'group_by': 'job_id'}"/>
|
||||
<filter string="Working Schedule" name="group_by_resource_calendar_id" domain="[]" context="{'group_by': 'resource_calendar_id'}"/>
|
||||
<filter string="Salary Structure Type" name="group_by_structure_type_id" domain="[]" context="{'group_by': 'structure_type_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
@@ -149,8 +152,8 @@
|
||||
<group name="top_info_left">
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="date_start" string="Contract Start Date"/>
|
||||
<field name="date_end" string="Contract End Date"/>
|
||||
<field name="company_country_id" invisible="1"/>
|
||||
<field name="country_code" invisible="1"/>
|
||||
<field name="structure_type_id" domain="['|', ('country_id', '=', False), ('country_id', '=', company_country_id)]"/>
|
||||
@@ -169,11 +172,12 @@
|
||||
<group name="top_info_right">
|
||||
<field name="department_id"/>
|
||||
<field name="job_id"/>
|
||||
<field name="hr_responsible_id"/>
|
||||
<field name="hr_responsible_id" required="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Contract Details" name="other">
|
||||
<group><group name="contract_details"/></group>
|
||||
<group name="notes_group" string="Notes">
|
||||
<field name="notes" nolabel="1"/>
|
||||
</group>
|
||||
@@ -205,18 +209,18 @@
|
||||
<field name="name">hr.contract.tree</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Contracts" multi_edit="1" sample="1" default_order = 'date_start desc'>
|
||||
<tree string="Contracts" multi_edit="1" sample="1" default_order='date_start ASC'>
|
||||
<field name="name" readonly="1"/>
|
||||
<field name="employee_id" readonly="1" widget="many2one_avatar_employee"/>
|
||||
<field name="job_id"/>
|
||||
<field name="resource_calendar_id"/>
|
||||
<field name="date_start" readonly="1"/>
|
||||
<field name="date_end" readonly="1"/>
|
||||
<field name="resource_calendar_id" optional="show"/>
|
||||
<field name="structure_type_id" optional="show"/>
|
||||
<field name="state" widget="badge" decoration-info="state == 'draft'" decoration-warning="state == 'close'" decoration-success="state == 'open'"/>
|
||||
<field name="kanban_state" widget="state_selection" readonly="1"/>
|
||||
<field name="wage" invisible="1"/>
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
|
||||
<field name="company_id" groups="base.group_multi_company" readonly="1" optional="show"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -248,7 +252,7 @@
|
||||
<field name="name"/>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="text-muted o_kanban_record_subtitle o_hr_contract_job_id">
|
||||
<div class="text-muted o_kanban_record_subtitle o_hr_contract_job_id" name="div_job_id">
|
||||
<field name="job_id"/>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
|
||||
@@ -5,7 +5,7 @@ from contextlib import contextmanager
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from psycopg2 import OperationalError
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class HrWorkEntry(models.Model):
|
||||
@@ -61,7 +61,10 @@ class HrWorkEntry(models.Model):
|
||||
@api.depends('work_entry_type_id', 'employee_id')
|
||||
def _compute_name(self):
|
||||
for work_entry in self:
|
||||
work_entry.name = "%s: %s" % (work_entry.work_entry_type_id.name, work_entry.employee_id.name)
|
||||
if not work_entry.employee_id:
|
||||
work_entry.name = _('Undefined')
|
||||
else:
|
||||
work_entry.name = "%s: %s" % (work_entry.work_entry_type_id.name or _('Undefined Type'), work_entry.employee_id.name)
|
||||
|
||||
@api.depends('state')
|
||||
def _compute_conflict(self):
|
||||
@@ -213,7 +216,7 @@ class HrWorkEntryType(models.Model):
|
||||
_description = 'HR Work Entry Type'
|
||||
|
||||
name = fields.Char(required=True, translate=True)
|
||||
code = fields.Char(required=True)
|
||||
code = fields.Char(required=True, help="Carefull, the Code is used in many references, changing it could lead to unwanted changes.")
|
||||
color = fields.Integer(default=0)
|
||||
sequence = fields.Integer(default=25)
|
||||
active = fields.Boolean(
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
<field name="duration" readonly="1"/>
|
||||
<field name="state"/>
|
||||
<field name="date_start" string="Beginning" readonly="1"/>
|
||||
<field name="date_stop" string="End" readonly="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -137,7 +138,6 @@
|
||||
<field name="model">hr.work.entry.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="color" widget="color_picker"/>
|
||||
@@ -161,10 +161,13 @@
|
||||
<group name="identification">
|
||||
<field name="code"/>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="sequence"/>
|
||||
<field name="sequence" groups="base.group_no_one"/>
|
||||
<field name="color" widget="color_picker"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="other">
|
||||
<group name="time_off" string="Time Off Options"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -152,4 +152,5 @@ class HrWorkEntryType(models.Model):
|
||||
_inherit = 'hr.work.entry.type'
|
||||
_description = 'HR Work Entry Type'
|
||||
|
||||
is_leave = fields.Boolean(default=False, string="Time Off")
|
||||
is_leave = fields.Boolean(
|
||||
default=False, string="Time Off", help="Allow the work entry type to be linked with time off types.")
|
||||
|
||||
@@ -31,11 +31,9 @@
|
||||
<field name="model">hr.work.entry.type</field>
|
||||
<field name="inherit_id" ref="hr_work_entry.hr_work_entry_type_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='identification']" position="after">
|
||||
<group>
|
||||
<field name="is_leave"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<group name="time_off" position="inside">
|
||||
<field name="is_leave"/>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -89,4 +89,6 @@ class HrWorkEntryType(models.Model):
|
||||
_inherit = 'hr.work.entry.type'
|
||||
_description = 'HR Work Entry Type'
|
||||
|
||||
leave_type_ids = fields.One2many('hr.leave.type', 'work_entry_type_id', string='Time Off Type')
|
||||
leave_type_ids = fields.One2many(
|
||||
'hr.leave.type', 'work_entry_type_id', string='Time Off Type',
|
||||
help="Every new time off type in this list will be reported as select work entry in payslip.")
|
||||
|
||||
Reference in New Issue
Block a user