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 00000000..9ccbaed5 Binary files /dev/null and b/hr_payroll_commission/static/description/icon.png differ 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 + + + + + + + + + + + + + + + +