From 6c3b77472f0472725a3b52892fe118f7a9561d34 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Thu, 17 Jan 2019 11:37:38 -0800 Subject: [PATCH 1/7] Initial commit of `hr_payroll_slip_ytd` for 11.0 --- hr_payroll_slip_ytd/__init__.py | 1 + hr_payroll_slip_ytd/__manifest__.py | 18 ++++++++++++++++++ hr_payroll_slip_ytd/models/__init__.py | 1 + hr_payroll_slip_ytd/models/payslip.py | 19 +++++++++++++++++++ hr_payroll_slip_ytd/views/payslip_views.xml | 14 ++++++++++++++ 5 files changed, 53 insertions(+) create mode 100755 hr_payroll_slip_ytd/__init__.py create mode 100755 hr_payroll_slip_ytd/__manifest__.py create mode 100644 hr_payroll_slip_ytd/models/__init__.py create mode 100644 hr_payroll_slip_ytd/models/payslip.py create mode 100644 hr_payroll_slip_ytd/views/payslip_views.xml diff --git a/hr_payroll_slip_ytd/__init__.py b/hr_payroll_slip_ytd/__init__.py new file mode 100755 index 00000000..0650744f --- /dev/null +++ b/hr_payroll_slip_ytd/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hr_payroll_slip_ytd/__manifest__.py b/hr_payroll_slip_ytd/__manifest__.py new file mode 100755 index 00000000..52aaf5f7 --- /dev/null +++ b/hr_payroll_slip_ytd/__manifest__.py @@ -0,0 +1,18 @@ +{ + 'name': 'Payroll Report Year to Date', + 'author': 'Hibou Corp. ', + 'version': '11.0.1.0.0', + 'category': 'Human Resources', + 'sequence': 95, + 'summary': 'Show YTD computations on Payslip Report', + 'description': """ +Show Year to Date (YTD) computations on Payslip Report. + """, + 'website': 'https://hibou.io/', + 'depends': ['hr_payroll'], + 'data': [ + 'views/payslip_views.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/hr_payroll_slip_ytd/models/__init__.py b/hr_payroll_slip_ytd/models/__init__.py new file mode 100644 index 00000000..305e17f9 --- /dev/null +++ b/hr_payroll_slip_ytd/models/__init__.py @@ -0,0 +1 @@ +from . import payslip diff --git a/hr_payroll_slip_ytd/models/payslip.py b/hr_payroll_slip_ytd/models/payslip.py new file mode 100644 index 00000000..44a4a59c --- /dev/null +++ b/hr_payroll_slip_ytd/models/payslip.py @@ -0,0 +1,19 @@ +from odoo import models + + +class Payslip(models.Model): + _inherit = 'hr.payslip' + + def ytd(self, code, allow_draft=False): + to_date = self.date_to + from_date = self.date_to[:4] + '-01-01' + state_allowed = ('done', 'verify') if not allow_draft else ('done', 'verify', 'draft') + self.env.cr.execute(""" + SELECT sum(total) as sum + FROM hr_payslip as hp + JOIN hr_payslip_line as pi ON hp.id = pi.slip_id + WHERE hp.employee_id = %s + AND hp.state in %s + AND hp.date_to >= %s AND hp.date_to <= %s AND pi.code = %s""", + (self.employee_id.id, state_allowed, from_date, to_date, code)) + return self.env.cr.fetchone()[0] or 0.0 diff --git a/hr_payroll_slip_ytd/views/payslip_views.xml b/hr_payroll_slip_ytd/views/payslip_views.xml new file mode 100644 index 00000000..be97b94f --- /dev/null +++ b/hr_payroll_slip_ytd/views/payslip_views.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file From 05a81fd2093ad58aa801a7ac785a9d0460b841b1 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Mon, 20 May 2019 16:06:24 -0700 Subject: [PATCH 2/7] MIG `hr_payroll_slip_ytd` to 12.0 --- hr_payroll_slip_ytd/__manifest__.py | 2 +- hr_payroll_slip_ytd/models/payslip.py | 2 +- hr_payroll_slip_ytd/views/payslip_views.xml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hr_payroll_slip_ytd/__manifest__.py b/hr_payroll_slip_ytd/__manifest__.py index 52aaf5f7..eff0199d 100755 --- a/hr_payroll_slip_ytd/__manifest__.py +++ b/hr_payroll_slip_ytd/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'Payroll Report Year to Date', 'author': 'Hibou Corp. ', - 'version': '11.0.1.0.0', + 'version': '12.0.1.0.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Show YTD computations on Payslip Report', diff --git a/hr_payroll_slip_ytd/models/payslip.py b/hr_payroll_slip_ytd/models/payslip.py index 44a4a59c..1dd7cbcf 100644 --- a/hr_payroll_slip_ytd/models/payslip.py +++ b/hr_payroll_slip_ytd/models/payslip.py @@ -6,7 +6,7 @@ class Payslip(models.Model): def ytd(self, code, allow_draft=False): to_date = self.date_to - from_date = self.date_to[:4] + '-01-01' + from_date = str(self.date_to.year) + '-01-01' state_allowed = ('done', 'verify') if not allow_draft else ('done', 'verify', 'draft') self.env.cr.execute(""" SELECT sum(total) as sum diff --git a/hr_payroll_slip_ytd/views/payslip_views.xml b/hr_payroll_slip_ytd/views/payslip_views.xml index be97b94f..1e80ce7c 100644 --- a/hr_payroll_slip_ytd/views/payslip_views.xml +++ b/hr_payroll_slip_ytd/views/payslip_views.xml @@ -2,13 +2,13 @@ \ No newline at end of file From 499aa1bcf60fd310fb10855fbe899311e1c0b2d1 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Wed, 19 Feb 2020 12:04:03 -0800 Subject: [PATCH 3/7] IMP `hr_payroll_slip_ytd` Add totals for Quantity and Amount for better YTD reporting. --- hr_payroll_slip_ytd/__manifest__.py | 2 +- hr_payroll_slip_ytd/models/payslip.py | 13 +++++++++++-- hr_payroll_slip_ytd/views/payslip_views.xml | 9 +++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/hr_payroll_slip_ytd/__manifest__.py b/hr_payroll_slip_ytd/__manifest__.py index eff0199d..a66480ff 100755 --- a/hr_payroll_slip_ytd/__manifest__.py +++ b/hr_payroll_slip_ytd/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'Payroll Report Year to Date', 'author': 'Hibou Corp. ', - 'version': '12.0.1.0.0', + 'version': '12.0.1.1.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Show YTD computations on Payslip Report', diff --git a/hr_payroll_slip_ytd/models/payslip.py b/hr_payroll_slip_ytd/models/payslip.py index 1dd7cbcf..fe46d655 100644 --- a/hr_payroll_slip_ytd/models/payslip.py +++ b/hr_payroll_slip_ytd/models/payslip.py @@ -9,11 +9,20 @@ class Payslip(models.Model): from_date = str(self.date_to.year) + '-01-01' state_allowed = ('done', 'verify') if not allow_draft else ('done', 'verify', 'draft') self.env.cr.execute(""" - SELECT sum(total) as sum + SELECT sum(total) as total, + sum(quantity) as quantity, + sum(amount) as amount FROM hr_payslip as hp JOIN hr_payslip_line as pi ON hp.id = pi.slip_id WHERE hp.employee_id = %s AND hp.state in %s AND hp.date_to >= %s AND hp.date_to <= %s AND pi.code = %s""", (self.employee_id.id, state_allowed, from_date, to_date, code)) - return self.env.cr.fetchone()[0] or 0.0 + res = self.env.cr.dictfetchone() + if res: + # Can return dictionary with NULL aka Nones + for key in res: + res[key] = res[key] or 0.0 + else: + res = {'total': 0.0, 'quantity': 0.0, 'amount': 0.0} + return res diff --git a/hr_payroll_slip_ytd/views/payslip_views.xml b/hr_payroll_slip_ytd/views/payslip_views.xml index 1e80ce7c..dbe60014 100644 --- a/hr_payroll_slip_ytd/views/payslip_views.xml +++ b/hr_payroll_slip_ytd/views/payslip_views.xml @@ -3,12 +3,17 @@ \ No newline at end of file From 673f4a34d50c8555e1e411c47a1ee64b1e113269 Mon Sep 17 00:00:00 2001 From: Cedric Collins Date: Wed, 24 Mar 2021 17:33:54 -0500 Subject: [PATCH 4/7] [IMP] hr_payroll_slip_ytd: add unit tests H4817 --- hr_payroll_slip_ytd/tests/__init__.py | 1 + .../tests/test_hr_payslip_ytd.py | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 hr_payroll_slip_ytd/tests/__init__.py create mode 100644 hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py diff --git a/hr_payroll_slip_ytd/tests/__init__.py b/hr_payroll_slip_ytd/tests/__init__.py new file mode 100644 index 00000000..63a156de --- /dev/null +++ b/hr_payroll_slip_ytd/tests/__init__.py @@ -0,0 +1 @@ +from . import test_hr_payslip_ytd diff --git a/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py b/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py new file mode 100644 index 00000000..6a563da6 --- /dev/null +++ b/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py @@ -0,0 +1,31 @@ +from odoo.addons.hr_payroll.tests.common import TestPayslipBase + + +class TestPayslipYtd(TestPayslipBase): + def test_00_payslip_ytd(self): + richard_payslip = self.env['hr.payslip'].create({ + 'name': 'Payslip of Richard', + 'employee_id': self.richard_emp.id, + 'contract_id': self.richard_emp.contract_id.id, + }) + + richard_payslip.compute_sheet() + richard_payslip.action_payslip_done() + basic_line = richard_payslip.line_ids.filtered(lambda l: l.code == 'BASIC') + self.assertEqual(basic_line.amount, 5000.0) + ytd = richard_payslip.ytd('BASIC', allow_draft=False) + self.assertEqual(ytd, {'total': 5000.0, 'quantity': 1.0, 'amount': 5000.0}) + + richard_payslip_next = self.env['hr.payslip'].create({ + 'name': 'Payslip of Richard', + 'employee_id': self.richard_emp.id, + 'contract_id': self.richard_emp.contract_id.id, + }) + + richard_payslip_next.compute_sheet() + basic_line = richard_payslip_next.line_ids.filtered(lambda l: l.code == 'BASIC') + self.assertEqual(basic_line.amount, 5000.0) + ytd = richard_payslip_next.ytd('BASIC', allow_draft=True) + self.assertEqual(ytd, {'total': 10000.0, 'quantity': 2.0, 'amount': 10000.0}) + ytd = richard_payslip_next.ytd('BASIC', allow_draft=False) + self.assertEqual(ytd, {'total': 5000.0, 'quantity': 1.0, 'amount': 5000.0}) From 444a1ce47a18dfb7778a41a04d13eef69718c8aa Mon Sep 17 00:00:00 2001 From: Cedric Collins Date: Thu, 25 Mar 2021 22:57:01 -0500 Subject: [PATCH 5/7] [MIG] hr_payroll_slip_ytd: migrate to 13.0 H4817 --- hr_payroll_slip_ytd/__manifest__.py | 2 +- hr_payroll_slip_ytd/models/payslip.py | 29 ++++++++++-- .../tests/test_hr_payslip_ytd.py | 47 ++++++++++++++----- hr_payroll_slip_ytd/views/payslip_views.xml | 28 ++++++++++- 4 files changed, 87 insertions(+), 19 deletions(-) diff --git a/hr_payroll_slip_ytd/__manifest__.py b/hr_payroll_slip_ytd/__manifest__.py index a66480ff..da94f254 100755 --- a/hr_payroll_slip_ytd/__manifest__.py +++ b/hr_payroll_slip_ytd/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'Payroll Report Year to Date', 'author': 'Hibou Corp. ', - 'version': '12.0.1.1.0', + 'version': '13.0.1.1.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Show YTD computations on Payslip Report', diff --git a/hr_payroll_slip_ytd/models/payslip.py b/hr_payroll_slip_ytd/models/payslip.py index fe46d655..984f0d16 100644 --- a/hr_payroll_slip_ytd/models/payslip.py +++ b/hr_payroll_slip_ytd/models/payslip.py @@ -7,11 +7,11 @@ class Payslip(models.Model): def ytd(self, code, allow_draft=False): to_date = self.date_to from_date = str(self.date_to.year) + '-01-01' - state_allowed = ('done', 'verify') if not allow_draft else ('done', 'verify', 'draft') + state_allowed = ('done',) if not allow_draft else ('done', 'verify', 'draft') self.env.cr.execute(""" - SELECT sum(total) as total, - sum(quantity) as quantity, - sum(amount) as amount + SELECT sum(pi.total) as total, + sum(pi.quantity) as quantity, + sum(pi.amount) as amount FROM hr_payslip as hp JOIN hr_payslip_line as pi ON hp.id = pi.slip_id WHERE hp.employee_id = %s @@ -26,3 +26,24 @@ class Payslip(models.Model): else: res = {'total': 0.0, 'quantity': 0.0, 'amount': 0.0} return res + + def worked_ytd(self, code, allow_draft=False): + to_date = self.date_to + from_date = str(self.date_to.year) + '-01-01' + state_allowed = ('done',) if not allow_draft else ('done', 'verify', 'draft') + self.env.cr.execute(""" + SELECT sum(wd.amount) as amount + FROM hr_payslip as hp + JOIN hr_payslip_worked_days as wd ON hp.id = wd.payslip_id + JOIN hr_work_entry_type as wt ON wd.work_entry_type_id = wt.id + WHERE hp.employee_id = %s + AND hp.state in %s + AND hp.date_to >= %s AND hp.date_to <= %s AND wt.code = %s""", + (self.employee_id.id, state_allowed, from_date, to_date, code)) + res = self.env.cr.dictfetchone() + if res: + # Can return dictionary with NULL aka Nones + res['amount'] = res.get('amount', 0.0) + else: + res = {'amount': 0.0} + return res diff --git a/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py b/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py index 6a563da6..17de71d8 100644 --- a/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py +++ b/hr_payroll_slip_ytd/tests/test_hr_payslip_ytd.py @@ -1,31 +1,54 @@ from odoo.addons.hr_payroll.tests.common import TestPayslipBase +from odoo.tests.common import Form +from datetime import date class TestPayslipYtd(TestPayslipBase): def test_00_payslip_ytd(self): - richard_payslip = self.env['hr.payslip'].create({ - 'name': 'Payslip of Richard', - 'employee_id': self.richard_emp.id, - 'contract_id': self.richard_emp.contract_id.id, + # The common test leaves the contract in draft mode + richard_contract = self.richard_emp.contract_ids[0] + richard_contract.state = 'open' + # pay structure in TestPayslipBase is missing default Salary Rules + # I don't need all of them, but I would like to use Basic Salary + self.developer_pay_structure.write({ + 'rule_ids': [(4, self.env['hr.salary.rule'].search([('code', '=', 'BASIC')], limit=1).id)] }) + payslip_form = Form(self.env['hr.payslip']) + payslip_form.employee_id = self.richard_emp + payslip_form.date_from = date(2020, 1, 1) + payslip_form.date_to = date(2020, 1, 31) + richard_payslip = payslip_form.save() + richard_payslip.compute_sheet() richard_payslip.action_payslip_done() + richard_payslip.flush() # Didn't have to do this in 12, but now the test fails if I don't + basic_line = richard_payslip.line_ids.filtered(lambda l: l.code == 'BASIC') self.assertEqual(basic_line.amount, 5000.0) - ytd = richard_payslip.ytd('BASIC', allow_draft=False) + self.assertEqual(basic_line.total, 5000.0) + ytd = richard_payslip.ytd(basic_line.code, allow_draft=False) self.assertEqual(ytd, {'total': 5000.0, 'quantity': 1.0, 'amount': 5000.0}) - richard_payslip_next = self.env['hr.payslip'].create({ - 'name': 'Payslip of Richard', - 'employee_id': self.richard_emp.id, - 'contract_id': self.richard_emp.contract_id.id, - }) + # get worked days + self.assertTrue(richard_payslip.worked_days_line_ids) + worked_day_line = richard_payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100') + self.assertEqual(worked_day_line.amount, 5000.0) + worked_day_ytd = richard_payslip.worked_ytd(worked_day_line.code, allow_draft=False) + self.assertEqual(worked_day_ytd, {'amount': 5000.0}) + payslip_form = Form(self.env['hr.payslip']) + payslip_form.employee_id = self.richard_emp + payslip_form.date_from = date(2020, 2, 1) + payslip_form.date_to = date(2020, 2, 29) + richard_payslip_next = payslip_form.save() richard_payslip_next.compute_sheet() + richard_payslip_next.flush() + basic_line = richard_payslip_next.line_ids.filtered(lambda l: l.code == 'BASIC') self.assertEqual(basic_line.amount, 5000.0) - ytd = richard_payslip_next.ytd('BASIC', allow_draft=True) + self.assertEqual(basic_line.total, 5000.0) + ytd = richard_payslip_next.ytd(basic_line.code, allow_draft=True) self.assertEqual(ytd, {'total': 10000.0, 'quantity': 2.0, 'amount': 10000.0}) - ytd = richard_payslip_next.ytd('BASIC', allow_draft=False) + ytd = richard_payslip_next.ytd(basic_line.code, allow_draft=False) self.assertEqual(ytd, {'total': 5000.0, 'quantity': 1.0, 'amount': 5000.0}) diff --git a/hr_payroll_slip_ytd/views/payslip_views.xml b/hr_payroll_slip_ytd/views/payslip_views.xml index dbe60014..c1a83a5a 100644 --- a/hr_payroll_slip_ytd/views/payslip_views.xml +++ b/hr_payroll_slip_ytd/views/payslip_views.xml @@ -1,15 +1,39 @@