hr_contract_history.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo import api, fields, models, tools, _
  4. from collections import defaultdict
  5. class ContractHistory(models.Model):
  6. _name = 'hr.contract.history'
  7. _description = 'Contract history'
  8. _auto = False
  9. _order = 'is_under_contract'
  10. # Even though it would have been obvious to use the reference contract's id as the id of the
  11. # hr.contract.history model, it turned out it was a bad idea as this id could change (for instance if a
  12. # new contract is created with a later start date). The hr.contract.history is instead closely linked
  13. # to the employee. That's why we will use this id (employee_id) as the id of the hr.contract.history.
  14. contract_id = fields.Many2one('hr.contract', readonly=True)
  15. display_name = fields.Char(compute='_compute_display_name')
  16. name = fields.Char('Contract Name', readonly=True)
  17. date_hired = fields.Date('Hire Date', readonly=True)
  18. date_start = fields.Date('Start Date', readonly=True)
  19. date_end = fields.Date('End Date', readonly=True)
  20. employee_id = fields.Many2one('hr.employee', string='Employee', readonly=True)
  21. active_employee = fields.Boolean('Active Employee', readonly=True)
  22. is_under_contract = fields.Boolean('Is Currently Under Contract', readonly=True)
  23. department_id = fields.Many2one('hr.department', string='Department', readonly=True)
  24. structure_type_id = fields.Many2one('hr.payroll.structure.type', string='Salary Structure Type', readonly=True)
  25. hr_responsible_id = fields.Many2one('res.users', string='HR Responsible', readonly=True)
  26. job_id = fields.Many2one('hr.job', string='Job Position', readonly=True)
  27. state = fields.Selection([
  28. ('draft', 'New'),
  29. ('open', 'Running'),
  30. ('close', 'Expired'),
  31. ('cancel', 'Cancelled')
  32. ], string='Status', readonly=True)
  33. resource_calendar_id = fields.Many2one('resource.calendar', string="Working Schedule", readonly=True)
  34. wage = fields.Monetary('Wage', help="Employee's monthly gross wage.", readonly=True, group_operator="avg")
  35. company_id = fields.Many2one('res.company', string='Company', readonly=True)
  36. company_country_id = fields.Many2one('res.country', string="Company country", related='company_id.country_id', readonly=True)
  37. country_code = fields.Char(related='company_country_id.code', depends=['company_country_id'], readonly=True)
  38. currency_id = fields.Many2one(string='Currency', related='company_id.currency_id', readonly=True)
  39. contract_type_id = fields.Many2one('hr.contract.type', 'Contract Type', readonly=True)
  40. contract_ids = fields.One2many('hr.contract', string='Contracts', compute='_compute_contract_ids', readonly=True, compute_sudo=True)
  41. contract_count = fields.Integer(compute='_compute_contract_count', string="# Contracts")
  42. under_contract_state = fields.Selection([
  43. ('done', 'Under Contract'),
  44. ('blocked', 'Not Under Contract')
  45. ], string='Contractual Status', compute='_compute_under_contract_state')
  46. activity_state = fields.Selection(related='contract_id.activity_state')
  47. @api.depends('contract_ids')
  48. def _compute_contract_count(self):
  49. for history in self:
  50. history.contract_count = len(history.contract_ids)
  51. @api.depends('is_under_contract')
  52. def _compute_under_contract_state(self):
  53. for history in self:
  54. history.under_contract_state = 'done' if history.is_under_contract else 'blocked'
  55. @api.depends('employee_id.name')
  56. def _compute_display_name(self):
  57. for history in self:
  58. history.display_name = _("%s's Contracts History", history.employee_id.name)
  59. @api.model
  60. def _get_fields(self):
  61. return ','.join('contract.%s' % name for name, field in self._fields.items()
  62. if field.store
  63. and field.type not in ['many2many', 'one2many', 'related']
  64. and field.name not in ['id', 'contract_id', 'employee_id', 'date_hired', 'is_under_contract', 'active_employee'])
  65. def init(self):
  66. tools.drop_view_if_exists(self.env.cr, self._table)
  67. # Reference contract is the one with the latest start_date.
  68. self.env.cr.execute("""CREATE or REPLACE VIEW %s AS (
  69. WITH contract_information AS (
  70. SELECT DISTINCT employee_id,
  71. company_id,
  72. FIRST_VALUE(id) OVER w_partition AS id,
  73. MAX(CASE
  74. WHEN state='open' THEN 1
  75. WHEN state='draft' AND kanban_state='done' THEN 1
  76. ELSE 0 END) OVER w_partition AS is_under_contract
  77. FROM hr_contract AS contract
  78. WHERE contract.active = true
  79. WINDOW w_partition AS (
  80. PARTITION BY contract.employee_id, contract.company_id
  81. ORDER BY
  82. CASE
  83. WHEN contract.state = 'open' THEN 0
  84. WHEN contract.state = 'draft' THEN 1
  85. WHEN contract.state = 'close' THEN 2
  86. WHEN contract.state = 'cancel' THEN 3
  87. ELSE 4 END,
  88. contract.date_start DESC
  89. RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  90. )
  91. )
  92. SELECT DISTINCT employee.id AS id,
  93. employee.id AS employee_id,
  94. employee.active AS active_employee,
  95. contract.id AS contract_id,
  96. contract_information.is_under_contract::bool AS is_under_contract,
  97. employee.first_contract_date AS date_hired,
  98. %s
  99. FROM hr_contract AS contract
  100. INNER JOIN contract_information ON contract.id = contract_information.id
  101. RIGHT JOIN hr_employee AS employee
  102. ON contract_information.employee_id = employee.id
  103. AND contract.company_id = employee.company_id
  104. WHERE employee.employee_type IN ('employee', 'student', 'trainee')
  105. )""" % (self._table, self._get_fields()))
  106. @api.depends('employee_id.contract_ids')
  107. def _compute_contract_ids(self):
  108. sorted_contracts = self.mapped('employee_id.contract_ids').sorted('date_start', reverse=True)
  109. mapped_employee_contracts = defaultdict(lambda: self.env['hr.contract'])
  110. for contract in sorted_contracts:
  111. mapped_employee_contracts[contract.employee_id] |= contract
  112. for history in self:
  113. history.contract_ids = mapped_employee_contracts[history.employee_id]
  114. def hr_contract_view_form_new_action(self):
  115. self.ensure_one()
  116. action = self.env['ir.actions.actions']._for_xml_id('hr_contract.action_hr_contract')
  117. action.update({
  118. 'context': {'default_employee_id': self.employee_id.id},
  119. 'view_mode': 'form',
  120. 'view_id': self.env.ref('hr_contract.hr_contract_view_form').id,
  121. 'views': [(self.env.ref('hr_contract.hr_contract_view_form').id, 'form')],
  122. })
  123. return action