diff options
| author | Lucas Lefèvre <lul@odoo.com> | 2018-12-19 12:32:05 +0000 |
|---|---|---|
| committer | Yannick Tivisse <yti@odoo.com> | 2019-02-01 12:16:54 +0000 |
| commit | 20bc2bbab5839bdbbf6b86972e677e10102e5bac (patch) | |
| tree | ed25aeb7b0619c5779032abd1d02e905853ad618 /addons/hr_payroll/tests/test_multi_contract.py | |
| parent | f5bca867ac7f7d8dd8d530f0df740d0931d18e0b (diff) | |
[IMP] hr_payroll: add multi contract support
Purpose
=======
We would like to be able to manage multi contracts payslips and contract transitions.
Specification
=============
General flow:
1/ Benefits are generated based on contracts's calendar.
2/ When validating additionnal benefits, calendar.attendances are created in the contract's calendar
3/ Create one payslip per contract in state [open|pending|close]
4/ For each payslip, generate worked day lines based on the contract's calendar
New Constraints:
1/ An employee cannot have more than one contract at the same time (excluding draft, closed and cancelled contracts).
2/ A leave cannot overlap multiple contracts.
3/ A contract cannot have starting and ending dates such that a leave become in the middle of
two contacts.
New state for a contract: Incoming. It means it's ready and will be running soon.
An incoming contract automatically becomes 'Running' if the start date is passed (cron)
Number of hours and days are always correct (according to the contract's calendar) on payslips as the
flow starts from the contract's calendar.
However, it is not correct in the Time Off app because it's counted based on the employee's calendar.
The number of days/hours for hr.leaves should be computed according to
the contract at the time of the leave.
e.g. If I currently work full time and I request 2 weeks off next month where I'll be working only
3 days/week and 5hours/day (other contract), It should count 6 days and 30 hours.
- in hr.benefits form view show the contract (below employee field)
TaskID: 1903483
closes odoo/odoo#30181
Diffstat (limited to 'addons/hr_payroll/tests/test_multi_contract.py')
| -rw-r--r-- | addons/hr_payroll/tests/test_multi_contract.py | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/addons/hr_payroll/tests/test_multi_contract.py b/addons/hr_payroll/tests/test_multi_contract.py new file mode 100644 index 00000000000..75f07871e45 --- /dev/null +++ b/addons/hr_payroll/tests/test_multi_contract.py @@ -0,0 +1,149 @@ + +import os +from datetime import datetime +from odoo.tools import config, test_reports +from odoo.tests.common import tagged +from odoo.exceptions import ValidationError +from odoo.addons.hr_payroll.tests.common import TestPayslipContractBase + +@tagged('payslips_multi_contract') +class TestPayslipMultiContract(TestPayslipContractBase): + + def create_leave(self, start, end): + return self.env['hr.leave'].create({ + 'name': 'Doctor Appointment', + 'employee_id': self.richard_emp.id, + 'holiday_status_id': self.leave_type.id, + 'date_from': start, + 'date_to': end, + }) + + def test_multi_contract(self): + # First contact: 40h, start of the month + payslip = self.env['hr.payslip'].create({ + 'name': 'November 2015', + 'employee_id': self.richard_emp.id, + 'date_from': datetime.strptime('2015-11-01', '%Y-%m-%d'), + 'date_to': datetime.strptime('2015-11-30', '%Y-%m-%d'), + 'contract_id': self.contract_cdd.id, + }) + payslip.onchange_employee() + self.assertEqual(payslip.worked_days_line_ids.number_of_hours, 80, "It should be 80 hours of work this month for this contract") + self.assertEqual(payslip.worked_days_line_ids.number_of_days, 10, "It should be 10 days of work this month for this contract") + + # Second contract: 35h, end of the month + payslip = self.env['hr.payslip'].create({ + 'name': 'November 2015', + 'employee_id': self.richard_emp.id, + 'date_from': datetime.strptime('2015-11-01', '%Y-%m-%d'), + 'date_to': datetime.strptime('2015-11-30', '%Y-%m-%d'), + 'contract_id': self.contract_cdi.id, + }) + payslip.onchange_employee() + self.assertEqual(payslip.worked_days_line_ids.number_of_hours, 77, "It should be 77 hours of work this month for this contract") + self.assertEqual(payslip.worked_days_line_ids.number_of_days, 11, "It should be 11 days of work this month for this contract") + + def test_multi_contract_holiday(self): + # Leave during second contract + leave = self.env['resource.calendar.leaves'].create({ + 'name': 'leave name', + 'date_from': datetime.strptime('2015-11-17 07:00:00', '%Y-%m-%d %H:%M:%S'), + 'date_to': datetime.strptime('2015-11-20 18:00:00', '%Y-%m-%d %H:%M:%S'), + 'resource_id': self.richard_emp.resource_id.id, + 'calendar_id': self.calendar_35h.id, + 'benefit_type_id': self.benefit_type_leave.id, + 'time_type': 'leave', + }) + payslip = self.env['hr.payslip'].create({ + 'name': 'November 2015', + 'employee_id': self.richard_emp.id, + 'date_from': datetime.strptime('2015-11-01', '%Y-%m-%d'), + 'date_to': datetime.strptime('2015-11-30', '%Y-%m-%d'), + 'contract_id': self.contract_cdi.id, + }) + payslip.onchange_employee() + work = payslip.worked_days_line_ids.filtered(lambda line: line.code == 'WORK100') + leave = payslip.worked_days_line_ids.filtered(lambda line: line.code == 'LEAVE100') + self.assertEqual(work.number_of_hours, 49, "It should be 49 hours of work this month for this contract") + self.assertEqual(leave.number_of_hours, 28, "It should be 28 hours of leave this month for this contract") + self.assertEqual(work.number_of_days, 7, "It should be 7 days of work this month for this contract") + self.assertEqual(leave.number_of_days, 4, "It should be 4 days of leave this month for this contract") + + def test_move_contract_in_leave(self): + # test move contract dates such that a leave is accross two contracts + start = datetime.strptime('2015-11-05 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-12-15 18:00:00', '%Y-%m-%d %H:%M:%S') + self.contract_cdi.write({'date_start': datetime.strptime('2015-12-30', '%Y-%m-%d').date()}) + print(self.contract_cdi.date_start, 'test') + # begins during contract, ends after contract + leave = self.create_leave(start, end) + leave.action_approve() + # move contract in the middle of the leave + with self.assertRaises(ValidationError): + self.contract_cdi.date_start = datetime.strptime('2015-11-17', '%Y-%m-%d').date() + + def test_create_contract_in_leave(self): + # test create contract such that a leave is accross two contracts + start = datetime.strptime('2015-11-05 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-12-15 18:00:00', '%Y-%m-%d %H:%M:%S') + self.contract_cdi.date_start = datetime.strptime('2015-12-30', '%Y-%m-%d').date() # remove this contract to be able to create the leave + # begins during contract, ends after contract + leave = self.create_leave(start, end) + leave.action_approve() + # move contract in the middle of the leave + with self.assertRaises(ValidationError): + contract = self.env['hr.contract'].create({ + 'date_start': datetime.strptime('2015-11-30', '%Y-%m-%d').date(), + 'name': 'Contract for Richard', + 'resource_calendar_id': self.calendar_40h.id, + 'wage': 5000.0, + 'type_id': self.ref('hr_contract.hr_contract_type_emp'), + 'employee_id': self.richard_emp.id, + 'struct_id': self.developer_pay_structure.id, + 'state': 'open', + }) + + def test_leave_outside_contract(self): + + # Leave outside contract => should not raise + start = datetime.strptime('2014-10-18 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2014-10-20 09:00:00', '%Y-%m-%d %H:%M:%S') + self.create_leave(start, end) + + # begins before contract, ends during contract => should not raise + start = datetime.strptime('2014-10-25 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-01-15 18:00:00', '%Y-%m-%d %H:%M:%S') + self.create_leave(start, end) + + # begins during contract, ends after contract => should not raise + self.contract_cdi.date_end = datetime.strptime('2015-11-30', '%Y-%m-%d').date() + start = datetime.strptime('2015-11-25 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-12-5 18:00:00', '%Y-%m-%d %H:%M:%S') + self.create_leave(start, end) + + def test_no_leave_overlapping_contracts(self): + + with self.assertRaises(ValidationError): + # Overlap two contracts + start = datetime.strptime('2015-11-12 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-11-17 18:00:00', '%Y-%m-%d %H:%M:%S') + leave = self.create_leave(start, end) + + # Leave inside fixed term contract => should not raise + start = datetime.strptime('2015-11-04 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-11-07 09:00:00', '%Y-%m-%d %H:%M:%S') + leave = self.create_leave(start, end) + + # Leave inside contract (no end) => should not raise + start = datetime.strptime('2015-11-18 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-11-20 09:00:00', '%Y-%m-%d %H:%M:%S') + leave = self.create_leave(start, end) + + def test_leave_request_next_contracts(self): + + start = datetime.strptime('2015-11-23 07:00:00', '%Y-%m-%d %H:%M:%S') + end = datetime.strptime('2015-11-24 18:00:00', '%Y-%m-%d %H:%M:%S') + leave = self.create_leave(start, end) + + leave._compute_number_of_hours_display() + self.assertEqual(leave.number_of_hours_display, 14, "It should count hours according to the future contract.") |
