Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions payroll_contract_advantages/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===========================
Payroll Contract Advantages
===========================
Expand All @@ -17,7 +13,7 @@ Payroll Contract Advantages
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpayroll-lightgray.png?logo=github
Expand Down Expand Up @@ -89,13 +85,13 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-nimarosa| image:: https://github.com/nimarosa.png?size=40px
:target: https://github.com/nimarosa
:alt: nimarosa
.. |maintainer-nimarosa, CristianoMafraJunior| image:: https://github.com/nimarosa, CristianoMafraJunior.png?size=40px
:target: https://github.com/nimarosa, CristianoMafraJunior
:alt: nimarosa, CristianoMafraJunior

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-nimarosa|
|maintainer-nimarosa, CristianoMafraJunior|

This module is part of the `OCA/payroll <https://github.com/OCA/payroll/tree/18.0/payroll_contract_advantages>`_ project on GitHub.

Expand Down
2 changes: 1 addition & 1 deletion payroll_contract_advantages/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"views/hr_contract_views.xml",
],
"application": True,
"maintainers": ["nimarosa"],
"maintainers": ["nimarosa, CristianoMafraJunior"],
}
42 changes: 30 additions & 12 deletions payroll_contract_advantages/models/hr_contract_advantage.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ class HrContractAdvantage(models.Model):
_name = "hr.contract.advantage"
_description = "Employee's Advantages on Contract"

contract_id = fields.Many2one("hr.contract")
_sql_constraints = [
(
"contract_template_unique",
"unique(contract_id, advantage_template_id)",
"This advantage is already set on this contract.",
)
]

contract_id = fields.Many2one("hr.contract", required=True, ondelete="cascade")
advantage_template_id = fields.Many2one(
"hr.contract.advantage.template", string="Advantage Template"
"hr.contract.advantage.template", string="Advantage Template", required=True
)
advantage_template_code = fields.Char(
string="Code", related="advantage_template_id.code", readonly=True
Expand All @@ -21,7 +29,7 @@ class HrContractAdvantage(models.Model):
advantage_upper_bound = fields.Float(
string="Upper Bound", related="advantage_template_id.upper_bound", readonly=True
)
amount = fields.Float()
amount = fields.Float(digits="Payroll")

@api.onchange("advantage_template_id")
def _onchange_advantage_template_id(self):
Expand All @@ -31,12 +39,22 @@ def _onchange_advantage_template_id(self):
@api.constrains("amount")
def _check_bound_limits(self):
for record in self:
if record.amount and record.amount != 0.00:
if record.amount > record.advantage_upper_bound:
raise ValidationError(
_("Advantage amount can't be greater than upper bound limit.")
)
elif record.amount < record.advantage_lower_bound:
raise ValidationError(
_("Advantage amount can't be less than lower bound limit.")
)
if not record.amount:
continue
# A bound left at 0.0 (the field default) means "no limit on that
# side", so a template without explicit bounds doesn't block
# every non-zero amount.
if (
record.advantage_upper_bound
and record.amount > record.advantage_upper_bound
):
raise ValidationError(
_("Advantage amount can't be greater than upper bound limit.")
)
if (
record.advantage_lower_bound
and record.amount < record.advantage_lower_bound
):
raise ValidationError(
_("Advantage amount can't be less than lower bound limit.")
)
Original file line number Diff line number Diff line change
@@ -1,18 +1,58 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import fields, models
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError


class HrContractAdvandageTemplate(models.Model):
_name = "hr.contract.advantage.template"
_description = "Employee's Advantage on Contract"

_sql_constraints = [
(
"code_unique",
"unique(code)",
"The code must be unique per advantage template: it is used to "
"reference the advantage from salary rules and payslip lines.",
)
]

name = fields.Char(required=True)
code = fields.Char(required=True)
lower_bound = fields.Float(
help="Lower bound authorized by the employer for this advantage"
digits="Payroll",
help="Lower bound authorized by the employer for this advantage. "
"Leave at 0 for no lower bound.",
)
upper_bound = fields.Float(
help="Upper bound authorized by the employer for this advantage"
digits="Payroll",
help="Upper bound authorized by the employer for this advantage. "
"Leave at 0 for no upper bound.",
)
default_value = fields.Float()
default_value = fields.Float(digits="Payroll")

@api.constrains("lower_bound", "upper_bound", "default_value")
def _check_bounds_consistency(self):
for template in self:
# 0.0 means "no bound on that side", matching
# hr.contract.advantage._check_bound_limits().
if (
template.lower_bound
and template.upper_bound
and template.lower_bound > template.upper_bound
):
raise ValidationError(
_("Lower bound can't be greater than upper bound.")
)
if template.upper_bound and template.default_value > template.upper_bound:
raise ValidationError(
_("Default value can't be greater than upper bound.")
)
if (
template.lower_bound
and template.default_value
and template.default_value < template.lower_bound
):
raise ValidationError(
_("Default value can't be less than lower bound.")
)
28 changes: 11 additions & 17 deletions payroll_contract_advantages/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Payroll Contract Advantages</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="payroll-contract-advantages">
<h1 class="title">Payroll Contract Advantages</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="payroll-contract-advantages">
<h1>Payroll Contract Advantages</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:706d779c96faf6e778887d097e408087320cb4122365deb05346b17775b513ce
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/payroll/tree/18.0/payroll_contract_advantages"><img alt="OCA/payroll" src="https://img.shields.io/badge/github-OCA%2Fpayroll-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/payroll-18-0/payroll-18-0-payroll_contract_advantages"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/payroll&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/payroll/tree/18.0/payroll_contract_advantages"><img alt="OCA/payroll" src="https://img.shields.io/badge/github-OCA%2Fpayroll-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/payroll-18-0/payroll-18-0-payroll_contract_advantages"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/payroll&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module adds support for advantages templates and advantages to be
set in contract form. The advantages can be set in the contract form as
a list of advantages templates. Then it can be used in the calculation
Expand All @@ -393,7 +388,7 @@ <h1>Payroll Contract Advantages</h1>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
<ul class="simple">
<li>Set the advantages templates in the payroll module with lower and
upper bounds and default value.</li>
Expand All @@ -404,30 +399,30 @@ <h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/payroll/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/payroll/issues/new?body=module:%20payroll_contract_advantages%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-3">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-4">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>Nimarosa</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Nimarosa (Nicolas Rodriguez) &lt;<a class="reference external" href="mailto:nicolasrsande&#64;gmail.com">nicolasrsande&#64;gmail.com</a>&gt;</li>
<li>Cristiano Mafra Junior &lt;<a class="reference external" href="mailto:cristiano.mafra&#64;escodoo.com.br">cristiano.mafra&#64;escodoo.com.br</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand All @@ -436,12 +431,11 @@ <h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/nimarosa"><img alt="nimarosa" src="https://github.com/nimarosa.png?size=40px" /></a></p>
<p><a class="reference external image-reference" href="https://github.com/nimarosa,CristianoMafraJunior"><img alt="nimarosa, CristianoMafraJunior" src="https://github.com/nimarosa,CristianoMafraJunior.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/payroll/tree/18.0/payroll_contract_advantages">OCA/payroll</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Copyright 2025 - TODAY, Cristiano Mafra Junior <cristiano.mafra@escodoo.com.br>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from psycopg2 import IntegrityError

from odoo.exceptions import ValidationError
from odoo.tests.common import mute_logger

from odoo.addons.payroll.tests.common import TestPayslipBase

Expand Down Expand Up @@ -33,7 +36,7 @@ def _create_template(

def test_onchange_advantage_template_sets_default_amount(self):
"""Onchange should set amount from template default value."""
template = self._create_template(default=123.45)
template = self._create_template(default=95.45)
advantage = self.Advantage.new(
{
"contract_id": self.richard_contract.id,
Expand Down Expand Up @@ -72,6 +75,21 @@ def test_constraint_raises_if_above_upper_bound(self):
}
)

def test_constraint_allows_amount_when_bounds_unset(self):
"""A template left with default (0.0) bounds must not block every
non-zero amount: 0.0 means "no limit on that side"."""
template = self._create_template(lower=0.0, upper=0.0, default=0.0)

advantage = self.Advantage.create(
{
"contract_id": self.richard_contract.id,
"advantage_template_id": template.id,
"amount": 30.0,
}
)

self.assertEqual(advantage.amount, 30.0)

def test_constraint_raises_if_below_lower_bound(self):
"""Constraint should raise when amount is below lower bound."""
template = self._create_template(lower=10.0, upper=100.0)
Expand All @@ -85,6 +103,54 @@ def test_constraint_raises_if_below_lower_bound(self):
}
)

def test_duplicate_advantage_on_same_contract_is_rejected(self):
"""The same advantage template can't be set twice on one contract:
it would silently overwrite one amount with the other in
get_current_contract_dict()."""
template = self._create_template()
self.Advantage.create(
{
"contract_id": self.richard_contract.id,
"advantage_template_id": template.id,
"amount": 10.0,
}
)

with (
self.assertRaises(IntegrityError),
mute_logger("odoo.sql_db"),
self.cr.savepoint(),
):
self.Advantage.create(
{
"contract_id": self.richard_contract.id,
"advantage_template_id": template.id,
"amount": 20.0,
}
)

def test_template_code_must_be_unique(self):
"""Two templates can't share the same code: it's used as the key to
expose the advantage to salary rules (advantages.<CODE>)."""
self._create_template(code="DUP")

with (
self.assertRaises(IntegrityError),
mute_logger("odoo.sql_db"),
self.cr.savepoint(),
):
self._create_template(code="DUP", name="Other Benefit")

def test_template_lower_bound_cannot_exceed_upper_bound(self):
"""A template can't be saved with lower_bound > upper_bound."""
with self.assertRaises(ValidationError):
self._create_template(lower=100.0, upper=10.0)

def test_template_default_value_must_respect_bounds(self):
"""A template's default_value can't fall outside its own bounds."""
with self.assertRaises(ValidationError):
self._create_template(lower=0.0, upper=100.0, default=150.0)

def test_get_current_contract_dict_contains_advantages(self):
"""get_current_contract_dict should expose advantages by code."""
template = self._create_template(
Expand Down
Loading