From 5b7043dccf7f2c0c14488287525c06b43bd33a0e Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Fri, 3 Jul 2020 08:42:56 -0700 Subject: [PATCH] [MOV] hr_payroll_commission: from Hibou Suite Enterprise for 13.0 --- hr_payroll_commission/__init__.py | 3 + hr_payroll_commission/__manifest__.py | 29 ++++++++ .../data/hr_payroll_commission_data.xml | 9 +++ .../data/hr_payroll_commission_demo.xml | 20 ++++++ hr_payroll_commission/models/__init__.py | 4 ++ hr_payroll_commission/models/hr_commission.py | 13 ++++ hr_payroll_commission/models/hr_payslip.py | 63 ++++++++++++++++++ .../static/description/icon.png | Bin 0 -> 6985 bytes hr_payroll_commission/tests/__init__.py | 3 + .../tests/test_payslip_commission.py | 37 ++++++++++ .../views/hr_commission_views.xml | 23 +++++++ .../views/hr_payslip_views.xml | 20 ++++++ 12 files changed, 224 insertions(+) create mode 100644 hr_payroll_commission/__init__.py create mode 100644 hr_payroll_commission/__manifest__.py create mode 100644 hr_payroll_commission/data/hr_payroll_commission_data.xml create mode 100644 hr_payroll_commission/data/hr_payroll_commission_demo.xml create mode 100644 hr_payroll_commission/models/__init__.py create mode 100644 hr_payroll_commission/models/hr_commission.py create mode 100644 hr_payroll_commission/models/hr_payslip.py create mode 100644 hr_payroll_commission/static/description/icon.png create mode 100755 hr_payroll_commission/tests/__init__.py create mode 100644 hr_payroll_commission/tests/test_payslip_commission.py create mode 100644 hr_payroll_commission/views/hr_commission_views.xml create mode 100644 hr_payroll_commission/views/hr_payslip_views.xml diff --git a/hr_payroll_commission/__init__.py b/hr_payroll_commission/__init__.py new file mode 100644 index 00000000..09434554 --- /dev/null +++ b/hr_payroll_commission/__init__.py @@ -0,0 +1,3 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import models diff --git a/hr_payroll_commission/__manifest__.py b/hr_payroll_commission/__manifest__.py new file mode 100644 index 00000000..7ac30f0c --- /dev/null +++ b/hr_payroll_commission/__manifest__.py @@ -0,0 +1,29 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +{ + 'name': 'Commissions in Payslips', + 'author': 'Hibou Corp.', + 'version': '13.0.1.0.0', + 'license': 'OPL-1', + 'category': 'Accounting/Commissions', + 'sequence': 95, + 'summary': 'Reimburse Commissions in Payslips', + 'description': """ +Reimburse Commissions in Payslips + """, + 'depends': [ + 'hr_commission', + 'hr_payroll', + ], + 'data': [ + 'views/hr_commission_views.xml', + 'views/hr_payslip_views.xml', + 'data/hr_payroll_commission_data.xml', + ], + 'demo': [ + 'data/hr_payroll_commission_demo.xml', + ], + 'installable': True, + 'application': False, + 'auto_install': True, +} diff --git a/hr_payroll_commission/data/hr_payroll_commission_data.xml b/hr_payroll_commission/data/hr_payroll_commission_data.xml new file mode 100644 index 00000000..a666f821 --- /dev/null +++ b/hr_payroll_commission/data/hr_payroll_commission_data.xml @@ -0,0 +1,9 @@ + + + + + Commissions + COMMISSION + + + diff --git a/hr_payroll_commission/data/hr_payroll_commission_demo.xml b/hr_payroll_commission/data/hr_payroll_commission_demo.xml new file mode 100644 index 00000000..77e8ccfa --- /dev/null +++ b/hr_payroll_commission/data/hr_payroll_commission_demo.xml @@ -0,0 +1,20 @@ + + + + + python + +result = inputs.COMMISSION.amount > 0.0 if inputs.COMMISSION else False + + code + +result = inputs.COMMISSION.amount if inputs.COMMISSION else 0 + + COMMISSION + + Commissions + + + + + diff --git a/hr_payroll_commission/models/__init__.py b/hr_payroll_commission/models/__init__.py new file mode 100644 index 00000000..ba3bcc23 --- /dev/null +++ b/hr_payroll_commission/models/__init__.py @@ -0,0 +1,4 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import hr_commission +from . import hr_payslip diff --git a/hr_payroll_commission/models/hr_commission.py b/hr_payroll_commission/models/hr_commission.py new file mode 100644 index 00000000..3cd127ff --- /dev/null +++ b/hr_payroll_commission/models/hr_commission.py @@ -0,0 +1,13 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import fields, models, _ + + +class CommissionPayment(models.Model): + _inherit = 'hr.commission.payment' + + pay_in_payslip = fields.Boolean(string="Reimburse In Next Payslip") + payslip_id = fields.Many2one('hr.payslip', string="Payslip", readonly=True) + + def action_report_in_next_payslip(self): + self.filtered(lambda p: not p.payslip_id).write({'pay_in_payslip': True}) diff --git a/hr_payroll_commission/models/hr_payslip.py b/hr_payroll_commission/models/hr_payslip.py new file mode 100644 index 00000000..510afdd7 --- /dev/null +++ b/hr_payroll_commission/models/hr_payslip.py @@ -0,0 +1,63 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import api, fields, models, _ + + +class HrPayslip(models.Model): + _inherit = 'hr.payslip' + + commission_payment_ids = fields.One2many( + 'hr.commission.payment', 'payslip_id', string='Commissions', + help="Commissions to reimburse to employee.", + states={'draft': [('readonly', False)], 'verify': [('readonly', False)]}) + commission_count = fields.Integer(compute='_compute_commission_count') + + @api.depends('commission_payment_ids.commission_ids', 'commission_payment_ids.payslip_id') + def _compute_commission_count(self): + for payslip in self: + payslip.commission_count = len(payslip.mapped('commission_payment_ids.commission_ids')) + + @api.onchange('input_line_ids') + def _onchange_input_line_ids(self): + commission_type = self.env.ref('hr_payroll_commission.commission_other_input', raise_if_not_found=False) + if not self.input_line_ids.filtered(lambda line: line.input_type_id == commission_type): + self.commission_payment_ids.write({'payslip_id': False}) + + @api.onchange('employee_id', 'struct_id', 'contract_id', 'date_from', 'date_to') + def _onchange_employee(self): + res = super()._onchange_employee() + if self.state == 'draft': + self.commission_payment_ids = self.env['hr.commission.payment'].search([ + ('employee_id', '=', self.employee_id.id), + ('pay_in_payslip', '=', True), + ('payslip_id', '=', False)]) + self._onchange_commission_payment_ids() + return res + + @api.onchange('commission_payment_ids') + def _onchange_commission_payment_ids(self): + commission_type = self.env.ref('hr_payroll_commission.commission_other_input', raise_if_not_found=False) + if not commission_type: + return + + total = sum(self.commission_payment_ids.mapped('commission_amount')) + if not total: + return + + lines_to_keep = self.input_line_ids.filtered(lambda x: x.input_type_id != commission_type) + input_lines_vals = [(5, 0, 0)] + [(4, line.id, False) for line in lines_to_keep] + input_lines_vals.append((0, 0, { + 'amount': total, + 'input_type_id': commission_type.id, + })) + self.update({'input_line_ids': input_lines_vals}) + + def open_commissions(self): + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': _('Reimbursed Commissions'), + 'res_model': 'hr.commission', + 'view_mode': 'tree,form', + 'domain': [('id', 'in', self.mapped('commission_payment_ids.commission_ids').ids)], + } diff --git a/hr_payroll_commission/static/description/icon.png b/hr_payroll_commission/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9ccbaed5c46a5dbe718c4f7945b1b4a7cb929bb4 GIT binary patch literal 6985 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 BYC8Y` literal 0 HcmV?d00001 diff --git a/hr_payroll_commission/tests/__init__.py b/hr_payroll_commission/tests/__init__.py new file mode 100755 index 00000000..0ce84cbf --- /dev/null +++ b/hr_payroll_commission/tests/__init__.py @@ -0,0 +1,3 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from . import test_payslip_commission diff --git a/hr_payroll_commission/tests/test_payslip_commission.py b/hr_payroll_commission/tests/test_payslip_commission.py new file mode 100644 index 00000000..0e1dcc17 --- /dev/null +++ b/hr_payroll_commission/tests/test_payslip_commission.py @@ -0,0 +1,37 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from datetime import date, timedelta + +from odoo.addons.hr_commission.tests import test_commission + + +class TestCommissionPayslip(test_commission.TestCommission): + + def test_commission(self): + super().test_commission() + commission_type = self.env.ref('hr_payroll_commission.commission_other_input') + payslip = self.env['hr.payslip'].create({ + 'name': 'test slip', + 'employee_id': self.employee.id, + 'date_from': date.today() - timedelta(days=1), + 'date_to': date.today() + timedelta(days=14), + }) + payslip._onchange_employee() + self.assertFalse(payslip.commission_payment_ids) + + # find unpaid commission payments from super().test_commission() + commission_payments = self.env['hr.commission.payment'].search([ + ('employee_id', '=', self.employee.id), + ]) + self.assertTrue(commission_payments) + + # press the button to pay it via payroll + commission_payments.action_report_in_next_payslip() + + payslip._onchange_employee() + # has attached commission payments + self.assertTrue(payslip.commission_payment_ids) + commission_input_lines = payslip.input_line_ids.filtered(lambda l: l.input_type_id == commission_type) + self.assertTrue(commission_input_lines) + self.assertEqual(sum(commission_input_lines.mapped('amount')), + sum(commission_payments.mapped('commission_amount'))) diff --git a/hr_payroll_commission/views/hr_commission_views.xml b/hr_payroll_commission/views/hr_commission_views.xml new file mode 100644 index 00000000..cbc1cb27 --- /dev/null +++ b/hr_payroll_commission/views/hr_commission_views.xml @@ -0,0 +1,23 @@ + + + + + hr.expense.sheet.view.form.payroll + hr.commission.payment + + + + + + + + + + + + + + + +