Merge branch 'mig/12.0/hr_payroll_commission' into '12.0'

mig/12.0/hr_payroll_commission into 12.0

See merge request hibou-io/hibou-odoo/suite!883
This commit is contained in:
Jared Kipe
2021-04-08 19:39:21 +00:00
12 changed files with 233 additions and 2 deletions

View File

@@ -5,6 +5,11 @@ from odoo.tests import common
class TestCommission(common.TransactionCase): class TestCommission(common.TransactionCase):
def setUp(self):
super().setUp()
self.user = self.browse_ref('base.user_demo')
self.employee = self.browse_ref('hr.employee_qdp') # This is the employee associated with above user.
def _createUser(self): def _createUser(self):
return self.env['res.users'].create({ return self.env['res.users'].create({
'name': 'Coach', 'name': 'Coach',
@@ -23,6 +28,12 @@ class TestCommission(common.TransactionCase):
}) })
def _createContract(self, employee, commission_rate, admin_commission_rate=0.0): def _createContract(self, employee, commission_rate, admin_commission_rate=0.0):
other_contracts = self.env['hr.contract'].search([('employee_id', '=', employee.id)])
if other_contracts:
# couldn't get the demo contract to not be used if it is
# installed e.g. by payroll...
# tried, state->cancel + employee.invalidate_cache() etc.
other_contracts.unlink()
return self.env['hr.contract'].create({ return self.env['hr.contract'].create({
'date_start': '2016-01-01', 'date_start': '2016-01-01',
'date_end': '2030-12-31', 'date_end': '2030-12-31',
@@ -47,8 +58,8 @@ class TestCommission(common.TransactionCase):
coach = self._createEmployee(self.browse_ref('base.user_root')) coach = self._createEmployee(self.browse_ref('base.user_root'))
coach_contract = self._createContract(coach, 12.0, admin_commission_rate=2.0) coach_contract = self._createContract(coach, 12.0, admin_commission_rate=2.0)
user = self.browse_ref('base.user_demo') user = self.user
emp = self.browse_ref('hr.employee_qdp') # This is the employee associated with above user. emp = self.employee
emp.address_home_id = user.partner_id # Important field for payables. emp.address_home_id = user.partner_id # Important field for payables.
emp.coach_id = coach emp.coach_id = coach
@@ -62,9 +73,12 @@ class TestCommission(common.TransactionCase):
inv.action_invoice_open() # validate inv.action_invoice_open() # validate
self.assertEqual(inv.state, 'open') self.assertEqual(inv.state, 'open')
self.assertTrue(inv.commission_ids, 'Commissions not created when invoice is validated.') self.assertTrue(inv.commission_ids, 'Commissions not created when invoice is validated.')
self.assertTrue(inv.amount_for_commission())
user_commission = inv.commission_ids.filtered(lambda c: c.employee_id.id == emp.id) user_commission = inv.commission_ids.filtered(lambda c: c.employee_id.id == emp.id)
self.assertEqual(len(user_commission), 1, 'Incorrect commission count %d (expect 1)' % len(user_commission)) self.assertEqual(len(user_commission), 1, 'Incorrect commission count %d (expect 1)' % len(user_commission))
self.assertTrue(user_commission.rate)
self.assertTrue(user_commission.amount)
self.assertEqual(user_commission.state, 'draft', 'Commission is not draft.') self.assertEqual(user_commission.state, 'draft', 'Commission is not draft.')
self.assertFalse(user_commission.move_id, 'Commission has existing journal entry.') self.assertFalse(user_commission.move_id, 'Commission has existing journal entry.')

View File

@@ -0,0 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import models

View File

@@ -0,0 +1,28 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
{
'name': 'Commissions in Payslips',
'author': 'Hibou Corp.',
'version': '12.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',
],
'demo': [
'data/hr_payroll_commission_demo.xml',
],
'installable': True,
'application': False,
'auto_install': True,
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_salary_rule_commission" model="hr.salary.rule">
<field name="condition_select">python</field>
<field name="condition_python">
result = inputs.COMMISSION.amount > 0.0 if inputs.COMMISSION else False
</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = inputs.COMMISSION.amount if inputs.COMMISSION else 0
</field>
<field name="code">COMMISSION</field>
<field name="category_id" ref="hr_payroll.BASIC"/>
<field name="name">Commissions</field>
<field name="sequence" eval="90"/>
</record>
<record id="hr_payroll.structure_base" model="hr.payroll.structure">
<field eval="[(4, ref('hr_salary_rule_commission'), 0)]" name="rule_ids"/>
</record>
</odoo>

View File

@@ -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

View File

@@ -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})

View File

@@ -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_code = 'COMMISSION'
if not self.input_line_ids.filtered(lambda line: line.code == commission_code):
self.commission_payment_ids.write({'payslip_id': False})
@api.onchange('employee_id', 'date_from', 'date_to', 'contract_id')
def onchange_employee(self):
res = super().onchange_employee()
if self.state == 'draft' and self.contract_id:
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_code = 'COMMISSION'
total = sum(self.commission_payment_ids.mapped('commission_amount'))
if not total:
return
lines_to_keep = self.input_line_ids.filtered(lambda x: x.code != commission_code)
input_lines_vals = [(5, 0, 0)] + [(4, line.id, False) for line in lines_to_keep]
input_lines_vals.append((0, 0, {
'amount': total,
'name': 'Commissions',
'code': commission_code,
'contract_id': self.contract_id.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)],
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,3 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import test_payslip_commission

View File

@@ -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_code = 'COMMISSION'
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.code == commission_code)
self.assertTrue(commission_input_lines)
self.assertEqual(sum(commission_input_lines.mapped('amount')),
sum(commission_payments.mapped('commission_amount')))

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_hr_commission_payment_form_inherit" model="ir.ui.view">
<field name="name">hr.expense.sheet.view.form.payroll</field>
<field name="model">hr.commission.payment</field>
<field name="inherit_id" ref="hr_commission.view_hr_commission_payment_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='employee_id']" position="after">
<field name="pay_in_payslip" attrs="{'invisible': ['|', ('pay_in_payslip', '=', False), ('payslip_id', '!=', False)]}"/>
<field name="payslip_id" attrs="{'invisible':[('payslip_id','=',False)]}"/>
</xpath>
<xpath expr="//form/header" position="inside">
<button name="action_report_in_next_payslip" type="object"
string="Report in Next Payslip"
groups="account.group_account_manager"
attrs="{'invisible': ['|', ('pay_in_payslip', '=', True), ('payslip_id', '!=', False)]}"
class="oe_highlight"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_payslip_view_form_inherit" model="ir.ui.view">
<field name="name">hr.payslip.view.form.inherit</field>
<field name="model">hr.payslip</field>
<field name="inherit_id" ref="hr_payroll.view_hr_payslip_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_stat_button" name="open_commissions" type="object" icon="fa-dollar" attrs="{'invisible': [('commission_count', '=', 0)]}">
<field string="Commissions" name="commission_count" widget="statinfo"/>
</button>
</xpath>
<xpath expr="//field[@name='number']" position="after">
<field name="commission_payment_ids" invisible="1"/>
</xpath>
</field>
</record>
</odoo>