Skip to content

Commit

Permalink
[FIX] hr_work_entry: Closed contract
Browse files Browse the repository at this point in the history
When a contract is closed, set the date of the day.
In the contract cron, ensure that all closed contracts have
an end date if it is followed by a new contract.
Add multi-edit on work_entry list

closes odoo#44118

Taskid: 2180263
X-original-commit: 0b5d8ce
Related: odoo/enterprise#7991
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
  • Loading branch information
jbm-odoo authored and tivisse committed Jan 28, 2020
1 parent 9d98c43 commit 41a465b
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 15 deletions.
27 changes: 25 additions & 2 deletions addons/hr_contract/models/hr_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class Contract(models.Model):
employee_id = fields.Many2one('hr.employee', string='Employee', tracking=True, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
department_id = fields.Many2one('hr.department', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", string="Department")
job_id = fields.Many2one('hr.job', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", string='Job Position')
date_start = fields.Date('Start Date', required=True, default=fields.Date.today,
date_start = fields.Date('Start Date', required=True, default=fields.Date.today, tracking=True,
help="Start date of the contract.")
date_end = fields.Date('End Date',
date_end = fields.Date('End Date', tracking=True,
help="End date of the contract (if it's a fixed-term contract).")
trial_date_end = fields.Date('End of Trial Period',
help="End date of the trial period (if there is one).")
Expand Down Expand Up @@ -133,6 +133,26 @@ def update_state(self):
self.search([('state', '=', 'draft'), ('kanban_state', '=', 'done'), ('date_start', '<=', fields.Date.to_string(date.today())),]).write({
'state': 'open'
})

contract_ids = self.search([('date_end', '=', False), ('state', '=', 'close'), ('employee_id', '!=', False)])
# Ensure all closed contract followed by a new contract have a end date.
# If closed contract has no closed date, the work entries will be generated for an unlimited period.
for contract in contract_ids:
next_contract = self.search([
('employee_id', '=', contract.employee_id.id),
('state', 'not in', ['cancel', 'new']),
('date_start', '>', contract.date_start)
], order="date_start asc", limit=1)
if next_contract:
contract.date_end = next_contract.date_start - relativedelta(days=1)
continue
next_contract = self.search([
('employee_id', '=', contract.employee_id.id),
('date_start', '>', contract.date_start)
], order="date_start asc", limit=1)
if next_contract:
contract.date_end = next_contract.date_start - relativedelta(days=1)

return True

def _assign_open_contract(self):
Expand All @@ -143,6 +163,9 @@ def write(self, vals):
res = super(Contract, self).write(vals)
if vals.get('state') == 'open':
self._assign_open_contract()
if vals.get('state') == 'close':
for contract in self.filtered(lambda c: not c.date_end):
contract.date_end = max(date.today(), contract.date_start)

calendar = vals.get('resource_calendar_id')
if calendar:
Expand Down
10 changes: 7 additions & 3 deletions addons/hr_work_entry/views/hr_work_entry_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<field name="arch" type="xml">
<form string="Work Entry" >
<header>
<field name="state" widget="statusbar" options="{'clickable': '1'}"/>
<field name="state" widget="statusbar" options="{'clickable': '1'}" statusbar_visible="draft,validated,conflict"/>
</header>
<sheet>
<div class="oe_title">
Expand Down Expand Up @@ -73,8 +73,8 @@
<field name="model">hr.work.entry</field>
<field name="arch" type="xml">
<tree multi_edit="1">
<field name="name" readonly="1"/>
<field name="work_entry_type_id" attrs="{'readonly': [('state', '=', 'validated')]}" options="{'no_create': True, 'no_open': True}"/>
<field name="name"/>
<field name="work_entry_type_id" options="{'no_create': True, 'no_open': True}"/>
<field name="duration" readonly="1"/>
<field name="state"/>
<field name="date_start" string="Beginning" readonly="1"/>
Expand All @@ -93,9 +93,13 @@
<filter name="work_entries_error" string="Conflicting" domain="[('state', '=', 'conflict')]"/>
<separator/>
<filter name="date_filter" string="Date" date="date_start"/>
<filter name="current_month" string="Current Month" domain="[
('date_stop', '&gt;=', (context_today()).strftime('%Y-%m-01')),
('date_start', '&lt;', (context_today() + relativedelta(months=1)).strftime('%Y-%m-01'))]"/>
<separator/>
<filter name="group_employee" string="Employee" context="{'group_by': 'employee_id'}"/>
<filter name="group_work_entry_type" string="Type" context="{'group_by': 'work_entry_type_id'}"/>
<filter name="group_start_date" string="Start Date" context="{'group_by': 'date_start'}"/>
<separator/>
<filter name="archived" string="Archived" domain="[('active', '=', False)]"/>
</search>
Expand Down
20 changes: 10 additions & 10 deletions addons/resource/models/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,9 @@ def _check_attendance(self):
# --------------------------------------------------
# Computation API
# --------------------------------------------------
def _attendance_intervals(self, start_dt, end_dt, resource=None, domain=None):
def _attendance_intervals(self, start_dt, end_dt, resource=None, domain=None, tz=None):
""" Return the attendance intervals in the given datetime range.
The returned intervals are expressed in the resource's timezone.
The returned intervals are expressed in specified tz or in the resource's timezone.
"""
assert start_dt.tzinfo and end_dt.tzinfo
combine = datetime.combine
Expand All @@ -331,8 +331,8 @@ def _attendance_intervals(self, start_dt, end_dt, resource=None, domain=None):
('display_type', '=', False),
]])

# express all dates and times in the resource's timezone
tz = timezone((resource or self).tz)
# express all dates and times in specified tz or in the resource's timezone
tz = tz if tz else timezone((resource or self).tz)
start_dt = start_dt.astimezone(tz)
end_dt = end_dt.astimezone(tz)

Expand Down Expand Up @@ -366,9 +366,9 @@ def _attendance_intervals(self, start_dt, end_dt, resource=None, domain=None):

return Intervals(result)

def _leave_intervals(self, start_dt, end_dt, resource=None, domain=None):
def _leave_intervals(self, start_dt, end_dt, resource=None, domain=None, tz=None):
""" Return the leave intervals in the given datetime range.
The returned intervals are expressed in the calendar's timezone.
The returned intervals are expressed in specified tz or in the calendar's timezone.
"""
assert start_dt.tzinfo and end_dt.tzinfo
self.ensure_one()
Expand All @@ -385,7 +385,7 @@ def _leave_intervals(self, start_dt, end_dt, resource=None, domain=None):
]

# retrieve leave intervals in (start_dt, end_dt)
tz = timezone((resource or self).tz)
tz = tz if tz else timezone((resource or self).tz)
start_dt = start_dt.astimezone(tz)
end_dt = end_dt.astimezone(tz)
result = []
Expand All @@ -396,10 +396,10 @@ def _leave_intervals(self, start_dt, end_dt, resource=None, domain=None):

return Intervals(result)

def _work_intervals(self, start_dt, end_dt, resource=None, domain=None):
def _work_intervals(self, start_dt, end_dt, resource=None, domain=None, tz=None):
""" Return the effective work intervals between the given datetimes. """
return (self._attendance_intervals(start_dt, end_dt, resource) -
self._leave_intervals(start_dt, end_dt, resource, domain))
return (self._attendance_intervals(start_dt, end_dt, resource, tz=tz) -
self._leave_intervals(start_dt, end_dt, resource, domain, tz=tz))

# --------------------------------------------------
# Private Methods / Helpers
Expand Down

0 comments on commit 41a465b

Please sign in to comment.