[IMP] hr_contract: Replace some contract states by kanban status
Purpose ======= Having some usability check to be sure to make the pay for everyone, and don't loose contracts. We would like to remove incoming and to renew columns in hr.contract and replace them by a kanban state. In employee, we would like to add warning on the kanban to warn the hr responsible there is no contract or a contract is about to expire Specification ============= hr.contract kanban: - add a kanabn state - it becomes red automatically when the contract is in running and expire in 7 days - add aquick search in filter to filter on expered soon - the kanban state can be changed manaually (in case of there is several rh) - sort by contract end date set first (more close of today first) Employee kanban - add an red icon if there is no contract in Runnig status OR there is a kanban state red in their contract (whatever the status). - add a quick seaach on employee with warning - State pending + green = incoming - State confirmed + red = about to expire / to renew closes odoo/odoo#33489 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
5548029fb7
commit
f17655dd77
@@ -23,7 +23,7 @@ You can assign several contracts per employee.
|
||||
'security/ir.model.access.csv',
|
||||
'data/hr_contract_data.xml',
|
||||
'views/hr_contract_views.xml',
|
||||
'views/hr_contract_templates.xml',
|
||||
'views/assets.xml',
|
||||
],
|
||||
'demo': ['data/hr_contract_demo.xml'],
|
||||
'installable': True,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
eval="obj().env.ref('hr.employee_admin').department_id.id"/>
|
||||
<field eval="7540.0" name="wage"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_al" model="hr.contract">
|
||||
@@ -23,6 +24,7 @@
|
||||
eval="obj().env.ref('hr.employee_al').department_id.id"/>
|
||||
<field eval="4000.0" name="wage"/>
|
||||
<field name="state">open</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_mit" model="hr.contract">
|
||||
@@ -35,6 +37,7 @@
|
||||
eval="obj().env.ref('hr.employee_mit').department_id.id"/>
|
||||
<field eval="4500.0" name="wage"/>
|
||||
<field name="state">open</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_stw" model="hr.contract">
|
||||
@@ -48,6 +51,7 @@
|
||||
eval="obj().env.ref('hr.employee_stw').department_id.id"/>
|
||||
<field eval="4500.0" name="wage"/>
|
||||
<field name="state">open</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_qdp" model="hr.contract">
|
||||
@@ -60,6 +64,7 @@
|
||||
eval="obj().env.ref('hr.employee_qdp').department_id.id"/>
|
||||
<field eval="3750.0" name="wage"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_han" model="hr.contract">
|
||||
@@ -72,6 +77,7 @@
|
||||
eval="obj().env.ref('hr.employee_han').department_id.id"/>
|
||||
<field eval="4600.0" name="wage"/>
|
||||
<field name="state">open</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_niv" model="hr.contract">
|
||||
@@ -85,6 +91,7 @@
|
||||
eval="obj().env.ref('hr.employee_niv').department_id.id"/>
|
||||
<field eval="4000.0" name="wage"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_jth" model="hr.contract">
|
||||
@@ -98,6 +105,7 @@
|
||||
eval="obj().env.ref('hr.employee_jth').department_id.id"/>
|
||||
<field eval="4200.0" name="wage"/>
|
||||
<field name="state">draft</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_chs" model="hr.contract">
|
||||
@@ -110,6 +118,7 @@
|
||||
eval="obj().env.ref('hr.employee_chs').department_id.id"/>
|
||||
<field eval="3750.0" name="wage"/>
|
||||
<field name="state">cancel</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_jve" model="hr.contract">
|
||||
@@ -122,6 +131,7 @@
|
||||
eval="obj().env.ref('hr.employee_jve').department_id.id"/>
|
||||
<field eval="3950.0" name="wage"/>
|
||||
<field name="state">cancel</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_fme" model="hr.contract">
|
||||
@@ -134,7 +144,8 @@
|
||||
<field name="department_id" model="hr.department"
|
||||
eval="obj().env.ref('hr.employee_fme').department_id.id"/>
|
||||
<field eval="3650.0" name="wage"/>
|
||||
<field name="state">pending</field>
|
||||
<field name="state">open</field>
|
||||
<field name="kanban_state">blocked</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_fpi" model="hr.contract">
|
||||
@@ -148,6 +159,7 @@
|
||||
eval="obj().env.ref('hr.employee_fpi').department_id.id"/>
|
||||
<field eval="3750.0" name="wage"/>
|
||||
<field name="state">close</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_contract_vad" model="hr.contract">
|
||||
@@ -161,6 +173,7 @@
|
||||
eval="obj().env.ref('hr.employee_vad').department_id.id"/>
|
||||
<field eval="3750.0" name="wage"/>
|
||||
<field name="state">close</field>
|
||||
<field name="kanban_state">normal</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -6,8 +6,8 @@ from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.osv import expression
|
||||
|
||||
from odoo.osv import expression
|
||||
|
||||
class Contract(models.Model):
|
||||
_name = 'hr.contract'
|
||||
@@ -33,14 +33,23 @@ class Contract(models.Model):
|
||||
notes = fields.Text('Notes')
|
||||
state = fields.Selection([
|
||||
('draft', 'New'),
|
||||
('incoming', 'Incoming'),
|
||||
('open', 'Running'),
|
||||
('pending', 'To Renew'),
|
||||
('close', 'Expired'),
|
||||
('cancel', 'Cancelled')
|
||||
], string='Status', group_expand='_expand_states',
|
||||
tracking=True, help='Status of the contract', default='draft')
|
||||
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
|
||||
"""
|
||||
kanban_state:
|
||||
* draft + green = "Incoming" state (will be set as Open once the contract has started)
|
||||
* open + red = "Pending" state (will be set as Closed once the contract has ended)
|
||||
* red = Shows a warning on the employees kanban view
|
||||
"""
|
||||
kanban_state = fields.Selection([
|
||||
('normal', 'Grey'),
|
||||
('done', 'Green'),
|
||||
('blocked', 'Red')
|
||||
], string='Kanban State', default='normal', tracking=True)
|
||||
currency_id = fields.Many2one(string="Currency", related='company_id.currency_id', readonly=True)
|
||||
permit_no = fields.Char('Work Permit No', related="employee_id.permit_no", readonly=False)
|
||||
visa_no = fields.Char('Visa No', related="employee_id.visa_no", readonly=False)
|
||||
@@ -57,6 +66,7 @@ class Contract(models.Model):
|
||||
def _expand_states(self, states, domain, order):
|
||||
return [key for key, val in type(self).state.selection]
|
||||
|
||||
|
||||
@api.onchange('employee_id')
|
||||
def _onchange_employee_id(self):
|
||||
if self.employee_id:
|
||||
@@ -64,14 +74,18 @@ class Contract(models.Model):
|
||||
self.department_id = self.employee_id.department_id
|
||||
self.resource_calendar_id = self.employee_id.resource_calendar_id
|
||||
|
||||
@api.constrains('employee_id', 'state', 'date_start', 'date_end')
|
||||
@api.constrains('employee_id', 'state', 'kanban_state', 'date_start', 'date_end')
|
||||
def _check_current_contract(self):
|
||||
""" Two contracts in state [incoming | pending | open] cannot overlap """
|
||||
for contract in self.filtered(lambda c: c.state not in ['draft', 'cancel', 'close']):
|
||||
""" Two contracts in state [incoming | open | close] cannot overlap """
|
||||
for contract in self.filtered(lambda c: c.state not in ['draft', 'cancel'] or c.state == 'draft' and c.kanban_state == 'done'):
|
||||
domain = [
|
||||
('id', '!=', contract.id),
|
||||
('employee_id', '=', contract.employee_id.id),
|
||||
('state', 'in', ['incoming', 'pending', 'open']),
|
||||
'|',
|
||||
('state', 'in', ['open', 'close']),
|
||||
'&',
|
||||
('state', '=', 'draft'),
|
||||
('kanban_state', '=', 'done') # replaces incoming
|
||||
]
|
||||
|
||||
if not contract.date_end:
|
||||
@@ -102,11 +116,11 @@ class Contract(models.Model):
|
||||
('visa_expire', '<=', fields.Date.to_string(date.today() + relativedelta(days=60))),
|
||||
('visa_expire', '>=', fields.Date.to_string(date.today() + relativedelta(days=1))),
|
||||
]).write({
|
||||
'state': 'pending'
|
||||
'kanban_state': 'blocked'
|
||||
})
|
||||
|
||||
self.search([
|
||||
('state', 'in', ('open', 'pending')),
|
||||
('state', '=', 'open'),
|
||||
'|',
|
||||
('date_end', '<=', fields.Date.to_string(date.today() + relativedelta(days=1))),
|
||||
('visa_expire', '<=', fields.Date.to_string(date.today() + relativedelta(days=1))),
|
||||
@@ -114,38 +128,45 @@ class Contract(models.Model):
|
||||
'state': 'close'
|
||||
})
|
||||
|
||||
self.search([('state', '=', 'incoming'), ('date_start', '<=', fields.Date.to_string(date.today())),]).write({
|
||||
self.search([('state', '=', 'draft'), ('kanban_state', '=', 'done'), ('date_start', '<=', fields.Date.to_string(date.today())),]).write({
|
||||
'state': 'open'
|
||||
})
|
||||
return True
|
||||
|
||||
def _assign_open_contract(self):
|
||||
for contract in self:
|
||||
contract.employee_id.sudo().write({'contract_id': contract.id})
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
res = super(Contract, self).write(vals)
|
||||
if vals.get('state') == 'open':
|
||||
self._assign_open_contract()
|
||||
|
||||
calendar = vals.get('resource_calendar_id')
|
||||
if calendar and (self.state == 'open' or (self.state == 'draft' and self.kanban_state == 'done')):
|
||||
self.mapped('employee_id').write({'resource_calendar_id': calendar})
|
||||
|
||||
if 'state' in vals and 'kanban_state' not in vals:
|
||||
self.write({'kanban_state': 'normal'})
|
||||
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def create(self, vals_list):
|
||||
contracts = super().create(vals_list)
|
||||
open_contracts = contracts.filtered(lambda c: c.state in ['open', 'pending'])
|
||||
# sync contract -> employee
|
||||
for contract in open_contracts:
|
||||
contract.employee_id.contract_id = contract
|
||||
def create(self, vals):
|
||||
contracts = super(Contract, self).create(vals)
|
||||
if vals.get('state') == 'open':
|
||||
contracts._assign_open_contract()
|
||||
open_contracts = contracts.filtered(lambda c: c.state == 'open' or c.state == 'draft' and c.kanban_state == 'done')
|
||||
# sync contract calendar -> calendar employee
|
||||
for contract in open_contracts.filtered(lambda c: c.resource_calendar_id):
|
||||
contract.employee_id.resource_calendar_id = contract.resource_calendar_id
|
||||
return contracts
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
res = super(Contract, self).write(vals)
|
||||
if vals.get('state') == 'open':
|
||||
for contract in self:
|
||||
contract.employee_id.sudo().write({'contract_id': contract.id})
|
||||
calendar = vals.get('resource_calendar_id')
|
||||
if calendar and self.state in ['open', 'pending']:
|
||||
self.mapped('employee_id').write({'resource_calendar_id': calendar})
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _track_subtype(self, init_values):
|
||||
self.ensure_one()
|
||||
if 'state' in init_values and self.state == 'pending':
|
||||
if 'state' in init_values and self.state == 'open' and 'kanban_state' in init_values and self.kanban_state == 'blocked':
|
||||
return self.env.ref('hr_contract.mt_contract_pending')
|
||||
elif 'state' in init_values and self.state == 'close':
|
||||
return self.env.ref('hr_contract.mt_contract_close')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class Employee(models.Model):
|
||||
@@ -14,6 +15,12 @@ class Employee(models.Model):
|
||||
groups="hr.group_hr_user", help='Current contract of the employee')
|
||||
calendar_mismatch = fields.Boolean(related='contract_id.calendar_mismatch')
|
||||
contracts_count = fields.Integer(compute='_compute_contracts_count', string='Contract Count')
|
||||
contract_warning = fields.Boolean(string='Contract Warning', store=True, compute='_compute_contract_warning', groups="hr.group_hr_user")
|
||||
|
||||
@api.depends('contract_id', 'contract_id.state', 'contract_id.kanban_state')
|
||||
def _compute_contract_warning(self):
|
||||
for employee in self:
|
||||
employee.contract_warning = not employee.contract_id or employee.contract_id.kanban_state == 'blocked' or employee.contract_id.state != 'open'
|
||||
|
||||
def _compute_contracts_count(self):
|
||||
# read_group as sudo, since contract count is displayed on form view
|
||||
@@ -22,20 +29,27 @@ class Employee(models.Model):
|
||||
for employee in self:
|
||||
employee.contracts_count = result.get(employee.id, 0)
|
||||
|
||||
def _get_contracts(self, date_from, date_to, states=['open', 'pending']):
|
||||
def _get_contracts(self, date_from, date_to, states=['open'], kanban_state=False):
|
||||
"""
|
||||
Returns the contracts of the employee between date_from and date_to
|
||||
"""
|
||||
return self.env['hr.contract'].search([
|
||||
'&', '&', '&',
|
||||
('employee_id', 'in', self.ids),
|
||||
('state', 'in', states),
|
||||
('date_start', '<=', date_to),
|
||||
'|', ('date_end', '=', False), ('date_end', '>=', date_from)
|
||||
])
|
||||
state_domain = [('state', 'in', states)]
|
||||
if kanban_state:
|
||||
state_domain = expression.AND([state_domain, [('kanban_state', 'in', kanban_state)]])
|
||||
|
||||
return self.env['hr.contract'].search(
|
||||
expression.AND([[('employee_id', 'in', self.ids)],
|
||||
state_domain,
|
||||
[('date_start', '<=', date_to),
|
||||
'|',
|
||||
('date_end', '=', False),
|
||||
('date_end', '>=', date_from)]]))
|
||||
|
||||
def _get_incoming_contracts(self, date_from, date_to):
|
||||
return self._get_contracts(date_from, date_to, states=['draft'], kanban_state=['done'])
|
||||
|
||||
@api.model
|
||||
def _get_all_contracts(self, date_from, date_to, states=['open', 'pending']):
|
||||
def _get_all_contracts(self, date_from, date_to, states=['open']):
|
||||
"""
|
||||
Returns the contracts of all employees between date_from and date_to
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.o_hr_contract_state {
|
||||
.o_status {
|
||||
vertical-align: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
.o_hr_contract_job_id {
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
@@ -12,6 +12,9 @@ class TestHrContracts(TestContractBase):
|
||||
self.contracts = self.env['hr.contract'].with_context(tracking_disable=True)
|
||||
self.test_contract = dict(name='Test', wage=1, employee_id=self.employee.id, state='open')
|
||||
|
||||
def test_employee_contractwarning(self):
|
||||
self.assertEquals(self.employee.contract_warning, True)
|
||||
|
||||
def apply_cron(self):
|
||||
self.env.ref('hr_contract.ir_cron_data_contract_update_state').method_direct_trigger()
|
||||
|
||||
@@ -20,16 +23,20 @@ class TestHrContracts(TestContractBase):
|
||||
self.contract = self.contracts.create(self.test_contract)
|
||||
self.apply_cron()
|
||||
self.assertEquals(self.contract.state, 'open')
|
||||
self.assertEquals(self.contract.kanban_state, 'normal')
|
||||
self.assertEquals(self.employee.contract_warning, False)
|
||||
|
||||
self.test_contract.update(dict(date_end=datetime.now() + relativedelta(days=5)))
|
||||
self.contract.write(self.test_contract)
|
||||
self.apply_cron()
|
||||
self.assertEquals(self.contract.state, 'pending')
|
||||
self.assertEquals(self.contract.state, 'open')
|
||||
self.assertEquals(self.contract.kanban_state, 'blocked')
|
||||
|
||||
self.test_contract.update({
|
||||
'date_start': datetime.now() + relativedelta(days=-50),
|
||||
'date_end': datetime.now() + relativedelta(days=-1),
|
||||
'state': 'pending',
|
||||
'state': 'open',
|
||||
'kanban_state': 'blocked',
|
||||
})
|
||||
self.contract.write(self.test_contract)
|
||||
self.apply_cron()
|
||||
@@ -40,19 +47,21 @@ class TestHrContracts(TestContractBase):
|
||||
self.test_contract.update(dict(date_end=False))
|
||||
self.contract = self.contracts.create(self.test_contract)
|
||||
self.apply_cron()
|
||||
self.assertEquals(self.contract.state, 'pending')
|
||||
self.assertEquals(self.contract.state, 'open')
|
||||
self.assertEquals(self.contract.kanban_state, 'blocked')
|
||||
|
||||
self.employee.visa_expire = date.today() + relativedelta(days=-5)
|
||||
self.test_contract.update({
|
||||
'date_start': datetime.now() + relativedelta(days=-50),
|
||||
'state': 'pending',
|
||||
'state': 'open',
|
||||
'kanban_state': 'blocked',
|
||||
})
|
||||
self.contract.write(self.test_contract)
|
||||
self.apply_cron()
|
||||
self.assertEquals(self.contract.state, 'close')
|
||||
|
||||
def test_contract_start_date(self):
|
||||
self.test_contract.update(dict(date_start=datetime.now(), state='incoming'))
|
||||
self.test_contract.update(dict(date_start=datetime.now(), state='draft', kanban_state='done'))
|
||||
self.contract = self.contracts.create(self.test_contract)
|
||||
self.apply_cron()
|
||||
self.assertEquals(self.contract.state, 'open')
|
||||
|
||||
@@ -12,11 +12,12 @@ class TestHrContracts(TestContractBase):
|
||||
super(TestHrContracts, self).setUp()
|
||||
self.contracts = self.env['hr.contract'].with_context(tracking_disable=True)
|
||||
|
||||
def create_contract(self, state, start, end=None):
|
||||
def create_contract(self, state, kanban_state, start, end=None):
|
||||
return self.env['hr.contract'].create({
|
||||
'name': 'Contract',
|
||||
'employee_id': self.employee.id,
|
||||
'state': state,
|
||||
'kanban_state': kanban_state,
|
||||
'wage': 1,
|
||||
'date_start': start,
|
||||
'date_end': end,
|
||||
@@ -25,65 +26,65 @@ class TestHrContracts(TestContractBase):
|
||||
def test_incoming_overlapping_contract(self):
|
||||
start = datetime.strptime('2015-11-01', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-11-30', '%Y-%m-%d').date()
|
||||
self.create_contract('open', start, end)
|
||||
self.create_contract('open', 'normal', start, end)
|
||||
|
||||
# Incoming contract
|
||||
with self.assertRaises(ValidationError, msg="It should not create two contract in state open or incoming"):
|
||||
start = datetime.strptime('2015-11-15', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-12-30', '%Y-%m-%d').date()
|
||||
self.create_contract('incoming', start, end)
|
||||
self.create_contract('draft', 'done', start, end)
|
||||
|
||||
def test_pending_overlapping_contract(self):
|
||||
start = datetime.strptime('2015-11-01', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-11-30', '%Y-%m-%d').date()
|
||||
self.create_contract('open', start, end)
|
||||
self.create_contract('open', 'normal', start, end)
|
||||
|
||||
# Pending contract
|
||||
with self.assertRaises(ValidationError, msg="It should not create two contract in state open or pending"):
|
||||
start = datetime.strptime('2015-11-15', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-12-30', '%Y-%m-%d').date()
|
||||
self.create_contract('pending', start, end)
|
||||
self.create_contract('open', 'blocked', start, end)
|
||||
|
||||
# Draft contract -> should not raise
|
||||
start = datetime.strptime('2015-11-15', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-12-30', '%Y-%m-%d').date()
|
||||
self.create_contract('draft', start, end)
|
||||
self.create_contract('draft', 'normal', start, end)
|
||||
|
||||
def test_draft_overlapping_contract(self):
|
||||
start = datetime.strptime('2015-11-01', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-11-30', '%Y-%m-%d').date()
|
||||
self.create_contract('open', start, end)
|
||||
self.create_contract('open', 'normal', start, end)
|
||||
|
||||
# Draft contract -> should not raise even if overlapping
|
||||
start = datetime.strptime('2015-11-15', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-12-30', '%Y-%m-%d').date()
|
||||
self.create_contract('draft', start, end)
|
||||
self.create_contract('draft', 'normal', start, end)
|
||||
|
||||
def test_overlapping_contract_no_end(self):
|
||||
|
||||
# No end date
|
||||
self.create_contract('open', datetime.strptime('2015-11-01', '%Y-%m-%d').date())
|
||||
self.create_contract('open', 'normal', datetime.strptime('2015-11-01', '%Y-%m-%d').date())
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
start = datetime.strptime('2015-11-15', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-12-30', '%Y-%m-%d').date()
|
||||
self.create_contract('incoming', start, end)
|
||||
self.create_contract('draft', 'done', start, end)
|
||||
|
||||
def test_overlapping_contract_no_end2(self):
|
||||
|
||||
start = datetime.strptime('2015-11-01', '%Y-%m-%d').date()
|
||||
end = datetime.strptime('2015-12-30', '%Y-%m-%d').date()
|
||||
self.create_contract('open', start, end)
|
||||
self.create_contract('open', 'normal', start, end)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
# No end
|
||||
self.create_contract('incoming', datetime.strptime('2015-01-01', '%Y-%m-%d').date())
|
||||
self.create_contract('draft', 'done', datetime.strptime('2015-01-01', '%Y-%m-%d').date())
|
||||
|
||||
def test_set_employee_contract_create(self):
|
||||
contract = self.create_contract('open', date(2018, 1, 1), date(2018, 1, 2))
|
||||
contract = self.create_contract('open', 'normal', date(2018, 1, 1), date(2018, 1, 2))
|
||||
self.assertEqual(self.employee.contract_id, contract)
|
||||
|
||||
def test_set_employee_contract_write(self):
|
||||
contract = self.create_contract('draft', date(2018, 1, 1), date(2018, 1, 2))
|
||||
contract = self.create_contract('draft', 'normal', date(2018, 1, 1), date(2018, 1, 2))
|
||||
contract.state = 'open'
|
||||
self.assertEqual(self.employee.contract_id, contract)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<odoo>
|
||||
<template id="assets_backend" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/css" href="/hr_contract/static/src/scss/state_selection.scss"/>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -53,6 +53,37 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_employee_view_kanban" model="ir.ui.view">
|
||||
<field name="name">hr.employee.view.kanban</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.hr_kanban_view_employees"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//div[hasclass('o_kanban_record_headings')]" position="after">
|
||||
<field name="contract_warning" invisible="1"/>
|
||||
<t t-if="record.contract_warning.raw_value">
|
||||
<i class="fa fa-exclamation-triangle text-danger" role="img"
|
||||
title="There is something wrong with the contract. Either there is no running contract for this employee or employee's contract is about to expire."
|
||||
aria-label="There is something wrong with the contract. Either there is no running contract for this employee or employee's contract is about to expire."></i>
|
||||
</t>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_employee_view_search" model="ir.ui.view">
|
||||
<field name="name">hr.employee.view.search</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.view_employee_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="." position="inside">
|
||||
<filter string="Contract Warning" name="with_contract_warning" domain="[('contract_warning', '=', True)]"/>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_user_view_form" model="ir.ui.view">
|
||||
<field name="name">hr.user.preferences.view.form.contract.inherit</field>
|
||||
<field name="model">res.users</field>
|
||||
@@ -83,7 +114,7 @@
|
||||
<field name="department_id" operator="child_of"/>
|
||||
<field name="state"/>
|
||||
<filter string="Running" name="running" domain="[('state', '=', 'open')]"/>
|
||||
<filter string="To Renew" name="to_renew" domain="[('state', '=', 'pending')]"/>
|
||||
<filter string="To Renew / Expires Soon" name="to_renew" domain="['&', ('state', '=', 'open'), ('kanban_state', '=', 'blocked')]"/>
|
||||
<separator />
|
||||
<filter string="Current Employee" name="current_employee" domain="[('employee_id.active','=',True)]"/>
|
||||
<separator/>
|
||||
@@ -114,8 +145,16 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title" name="title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<div class="oe_edit_only">
|
||||
<div class="float-left">
|
||||
<field name="kanban_state" widget="state_selection" class="mr-2"/>
|
||||
</div>
|
||||
<label for="name"/>
|
||||
</div>
|
||||
<h1>
|
||||
<div class="float-left oe_read_only">
|
||||
<field name="kanban_state" widget="state_selection" class="mr-2"/>
|
||||
</div>
|
||||
<field name="name" placeholder="Contract Reference"/>
|
||||
</h1>
|
||||
</div>
|
||||
@@ -143,7 +182,7 @@
|
||||
<label for="resource_calendar_id"/>
|
||||
<div>
|
||||
<field name="resource_calendar_id" required="1" nolabel="1"/>
|
||||
<span attrs="{'invisible': ['|', ('calendar_mismatch', '=', False), ('state', 'not in', ['open', 'pending'])]}"
|
||||
<span attrs="{'invisible': ['|', ('calendar_mismatch', '=', False), ('state', '!=', 'open')]}"
|
||||
class="fa fa-exclamation-triangle text-danger o_calendar_warning pl-3">
|
||||
</span>
|
||||
<span class="o_calendar_warning_tooltip text-danger">
|
||||
@@ -196,6 +235,7 @@
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="state"/>
|
||||
<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"/>
|
||||
@@ -207,9 +247,10 @@
|
||||
<field name="name">hr.contract.kanban</field>
|
||||
<field name="model">hr.contract</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_small_column">
|
||||
<kanban class="o_kanban_small_column" default_order="date_end">
|
||||
<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">
|
||||
@@ -224,10 +265,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_kanban_content">
|
||||
<div>
|
||||
<strong class="o_kanban_record_title"><field name="name"/></strong>
|
||||
<div class="o_hr_contract_state">
|
||||
<field name="kanban_state" widget="state_selection" />
|
||||
<strong class="o_kanban_record_title">
|
||||
<field name="name"/>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="text-muted o_kanban_record_subtitle">
|
||||
<div class="text-muted o_kanban_record_subtitle o_hr_contract_job_id">
|
||||
<field name="job_id"/>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
|
||||
Reference in New Issue
Block a user