hr_contract.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. # -*- coding:utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. import datetime
  4. import pytz
  5. from datetime import date, datetime
  6. from odoo import api, models
  7. from odoo.osv.expression import OR
  8. class HrContract(models.Model):
  9. _inherit = 'hr.contract'
  10. _description = 'Employee Contract'
  11. @api.constrains('date_start', 'date_end', 'state')
  12. def _check_contracts(self):
  13. self._get_leaves()._check_contracts()
  14. def _get_leaves(self):
  15. return self.env['hr.leave'].search([
  16. ('state', '!=', 'refuse'),
  17. ('employee_id', 'in', self.mapped('employee_id.id')),
  18. ('date_from', '<=', max([end or date.max for end in self.mapped('date_end')])),
  19. ('date_to', '>=', min(self.mapped('date_start'))),
  20. ])
  21. # override to add work_entry_type from leave
  22. def _get_leave_work_entry_type(self, leave):
  23. if leave.holiday_id:
  24. return leave.holiday_id.holiday_status_id.work_entry_type_id
  25. else:
  26. return leave.work_entry_type_id
  27. def _get_more_vals_leave_interval(self, interval, leaves):
  28. result = super()._get_more_vals_leave_interval(interval, leaves)
  29. for leave in leaves:
  30. if interval[0] >= leave[0] and interval[1] <= leave[1]:
  31. result.append(('leave_id', leave[2].holiday_id.id))
  32. return result
  33. def _get_interval_leave_work_entry_type(self, interval, leaves, bypassing_codes):
  34. # returns the work entry time related to the leave that
  35. # includes the whole interval.
  36. # Overriden in hr_work_entry_contract_holiday to select the
  37. # global time off first (eg: Public Holiday > Home Working)
  38. self.ensure_one()
  39. if 'work_entry_type_id' in interval[2] and interval[2].work_entry_type_id.code in bypassing_codes:
  40. return interval[2].work_entry_type_id
  41. interval_start = interval[0].astimezone(pytz.utc).replace(tzinfo=None)
  42. interval_stop = interval[1].astimezone(pytz.utc).replace(tzinfo=None)
  43. including_rcleaves = [l[2] for l in leaves if l[2] and interval_start >= l[2].date_from and interval_stop <= l[2].date_to]
  44. including_global_rcleaves = [l for l in including_rcleaves if not l.holiday_id]
  45. including_holiday_rcleaves = [l for l in including_rcleaves if l.holiday_id]
  46. rc_leave = False
  47. # Example: In CP200: Long term sick > Public Holidays (which is global)
  48. if bypassing_codes:
  49. bypassing_rc_leave = [l for l in including_holiday_rcleaves if l.holiday_id.holiday_status_id.work_entry_type_id.code in bypassing_codes]
  50. else:
  51. bypassing_rc_leave = []
  52. if bypassing_rc_leave:
  53. rc_leave = bypassing_rc_leave[0]
  54. elif including_global_rcleaves:
  55. rc_leave = including_global_rcleaves[0]
  56. elif including_holiday_rcleaves:
  57. rc_leave = including_holiday_rcleaves[0]
  58. if rc_leave:
  59. return self._get_leave_work_entry_type_dates(rc_leave, interval_start, interval_stop, self.employee_id)
  60. return self.env.ref('hr_work_entry_contract.work_entry_type_leave')
  61. def _get_sub_leave_domain(self):
  62. domain = super()._get_sub_leave_domain()
  63. return OR([
  64. domain,
  65. [('holiday_id.employee_id', 'in', self.employee_id.ids)] # see https://github.com/odoo/enterprise/pull/15091
  66. ])
  67. def write(self, vals):
  68. # Special case when setting a contract as running:
  69. # If there is already a validated time off over another contract
  70. # with a different schedule, split the time off, before the
  71. # _check_contracts raises an issue.
  72. if 'state' not in vals or vals['state'] != 'open':
  73. return super().write(vals)
  74. specific_contracts = self.env['hr.contract']
  75. all_new_leave_origin = []
  76. all_new_leave_vals = []
  77. leaves_state = {}
  78. for contract in self:
  79. leaves = contract._get_leaves()
  80. for leave in leaves:
  81. overlapping_contracts = leave._get_overlapping_contracts(contract_states=[('state', '!=', 'cancel')])
  82. if len(overlapping_contracts.resource_calendar_id) <= 1:
  83. continue
  84. if leave.id not in leaves_state:
  85. leaves_state[leave.id] = leave.state
  86. if leave.state != 'refuse':
  87. leave.action_refuse()
  88. super(HrContract, contract).write(vals)
  89. specific_contracts += contract
  90. for overlapping_contract in overlapping_contracts:
  91. # Exclude other draft contracts that are not set to running on this
  92. # transaction
  93. if overlapping_contract.state == 'draft' and overlapping_contract not in self:
  94. continue
  95. new_date_from = max(leave.date_from, datetime.combine(overlapping_contract.date_start, datetime.min.time()))
  96. new_date_to = min(leave.date_to, datetime.combine(overlapping_contract.date_end or date.max, datetime.max.time()))
  97. new_leave_vals = leave.copy_data({
  98. 'date_from': new_date_from,
  99. 'date_to': new_date_to,
  100. 'state': leaves_state[leave.id],
  101. })[0]
  102. new_leave = self.env['hr.leave'].new(new_leave_vals)
  103. new_leave._compute_date_from_to()
  104. new_leave._compute_number_of_days()
  105. # Could happen for part-time contract, that time off is not necessary
  106. # anymore.
  107. if new_leave.date_from < new_leave.date_to:
  108. all_new_leave_origin.append(leave)
  109. all_new_leave_vals.append(new_leave._convert_to_write(new_leave._cache))
  110. if all_new_leave_vals:
  111. new_leaves = self.env['hr.leave'].with_context(
  112. tracking_disable=True,
  113. mail_activity_automation_skip=True,
  114. leave_fast_create=True,
  115. leave_skip_state_check=True
  116. ).create(all_new_leave_vals)
  117. new_leaves.filtered(lambda l: l.state in 'validate')._validate_leave_request()
  118. for index, new_leave in enumerate(new_leaves):
  119. subtype_note = self.env.ref('mail.mt_note')
  120. new_leave.message_post_with_view(
  121. 'mail.message_origin_link',
  122. values={'self': new_leave, 'origin': all_new_leave_origin[index]},
  123. subtype_id=subtype_note.id)
  124. return super(HrContract, self - specific_contracts).write(vals)