Initial commit: OCA Payroll packages (5 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:05 +02:00
commit d19274f581
407 changed files with 214057 additions and 0 deletions

View file

@ -0,0 +1,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import hr_contract
from . import hr_payslip_line
from . import hr_payslip_run
from . import hr_payslip
from . import hr_salary_rule

View file

@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class HrContract(models.Model):
_inherit = "hr.contract"
_description = "Employee Contract"
analytic_account_id = fields.Many2one(
"account.analytic.account", "Analytic Account"
)
journal_id = fields.Many2one("account.journal", "Salary Journal")

View file

@ -0,0 +1,398 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import _, api, fields, models
from odoo.exceptions import UserError
logger = logging.getLogger(__name__)
class HrPayslip(models.Model):
_inherit = "hr.payslip"
date = fields.Date(
"Date Account",
states={"draft": [("readonly", False)]},
readonly=True,
help="Keep empty to use the period of the validation(Payslip) date.",
)
journal_id = fields.Many2one(
"account.journal",
"Salary Journal",
readonly=True,
required=True,
states={"draft": [("readonly", False)]},
default=lambda self: self.env["account.journal"].search(
[("type", "=", "general")], limit=1
),
)
move_id = fields.Many2one(
"account.move", "Accounting Entry", readonly=True, copy=False
)
@api.model_create_multi
def create(self, vals_list):
if "journal_id" in self.env.context:
for vals in vals_list:
vals["journal_id"] = self.env.context.get("journal_id")
return super(HrPayslip, self).create(vals_list)
@api.onchange("contract_id")
def onchange_contract(self):
res = super(HrPayslip, self).onchange_contract()
self.journal_id = (
self.contract_id.journal_id.id
or (not self.contract_id and self.default_get(["journal_id"])["journal_id"])
or self.journal_id
)
return res
def action_payslip_cancel(self):
for payslip in self:
if not payslip.move_id.journal_id.restrict_mode_hash_table:
payslip.move_id.with_context(force_delete=True).button_cancel()
payslip.move_id.with_context(force_delete=True).unlink()
else:
payslip.move_id._reverse_moves()
payslip.move_id = False
return super(HrPayslip, self).action_payslip_cancel()
def action_payslip_done(self):
res = super(HrPayslip, self).action_payslip_done()
for slip in self:
line_ids = []
debit_sum = 0.0
credit_sum = 0.0
date = slip.date or slip.date_to
currency = (
slip.company_id.currency_id or slip.journal_id.company_id.currency_id
)
name = _("Payslip of %s") % (slip.employee_id.name)
move_dict = {
"narration": name,
"ref": slip.number,
"journal_id": slip.journal_id.id,
"date": date,
}
for line in slip.line_ids:
amount = currency.round(slip.credit_note and -line.total or line.total)
if currency.is_zero(amount):
continue
debit_account_id = line.salary_rule_id.account_debit.id
credit_account_id = line.salary_rule_id.account_credit.id
account_id = debit_account_id or credit_account_id
analytic_salary_id = line.salary_rule_id.analytic_account_id
move_line_analytic_ids = {}
if slip.contract_id.analytic_account_id:
move_line_analytic_ids.update(
{line.slip_id.contract_id.analytic_account_id.id: 100}
)
elif analytic_salary_id:
move_line_analytic_ids.update(
{line.salary_rule_id.analytic_account_id.id: 100}
)
tax_ids = False
tax_tag_ids = False
if line.salary_rule_id.tax_line_ids:
account_tax_ids = [
salary_rule_id.account_tax_id.id
for salary_rule_id in line.salary_rule_id.tax_line_ids
]
tax_ids = [
(4, account_tax_id, 0) for account_tax_id in account_tax_ids
]
tax_tag_ids = (
self.env["account.tax.repartition.line"]
.search(
[
("invoice_tax_id", "in", account_tax_ids),
("repartition_type", "=", "base"),
]
)
.tag_ids
)
tax_repartition_line_id = False
if line.salary_rule_id.account_tax_id:
tax_repartition_line_id = (
self.env["account.tax.repartition.line"]
.search(
[
(
"invoice_tax_id",
"=",
line.salary_rule_id.account_tax_id.id,
),
("account_id", "=", account_id),
]
)
.id
)
tax_tag_ids = (
self.env["account.tax.repartition.line"]
.search(
[
(
"invoice_tax_id",
"=",
line.salary_rule_id.account_tax_id.id,
),
("repartition_type", "=", "tax"),
("account_id", "=", account_id),
]
)
.tag_ids
)
if debit_account_id:
debit_line = (
0,
0,
{
"name": line.name,
"partner_id": line._get_partner_id(credit_account=False)
or slip.employee_id.address_home_id.id,
"account_id": debit_account_id,
"journal_id": slip.journal_id.id,
"date": date,
"debit": amount > 0.0 and amount or 0.0,
"credit": amount < 0.0 and -amount or 0.0,
"analytic_distribution": move_line_analytic_ids,
"tax_line_id": line.salary_rule_id.account_tax_id.id,
"tax_ids": tax_ids,
"tax_repartition_line_id": tax_repartition_line_id,
"tax_tag_ids": tax_tag_ids,
},
)
line_ids.append(debit_line)
debit_sum += debit_line[2]["debit"] - debit_line[2]["credit"]
if credit_account_id:
credit_line = (
0,
0,
{
"name": line.name,
"partner_id": line._get_partner_id(credit_account=True)
or slip.employee_id.address_home_id.id,
"account_id": credit_account_id,
"journal_id": slip.journal_id.id,
"date": date,
"debit": amount < 0.0 and -amount or 0.0,
"credit": amount > 0.0 and amount or 0.0,
"analytic_distribution": move_line_analytic_ids,
"tax_line_id": line.salary_rule_id.account_tax_id.id,
"tax_ids": tax_ids,
"tax_repartition_line_id": tax_repartition_line_id,
"tax_tag_ids": tax_tag_ids,
},
)
line_ids.append(credit_line)
credit_sum += credit_line[2]["credit"] - credit_line[2]["debit"]
if currency.compare_amounts(credit_sum, debit_sum) == -1:
acc_id = slip.journal_id.default_account_id.id
if not acc_id:
raise UserError(
_(
'The Expense Journal "%s" has not properly '
"configured the Credit Account!"
)
% (slip.journal_id.name)
)
adjust_credit = (
0,
0,
{
"name": _("Adjustment Entry"),
"partner_id": False,
"account_id": acc_id,
"journal_id": slip.journal_id.id,
"date": date,
"debit": 0.0,
"credit": currency.round(debit_sum - credit_sum),
},
)
line_ids.append(adjust_credit)
elif currency.compare_amounts(debit_sum, credit_sum) == -1:
acc_id = slip.journal_id.default_account_id.id
if not acc_id:
raise UserError(
_(
'The Expense Journal "%s" has not properly '
"configured the Debit Account!"
)
% (slip.journal_id.name)
)
adjust_debit = (
0,
0,
{
"name": _("Adjustment Entry"),
"partner_id": False,
"account_id": acc_id,
"journal_id": slip.journal_id.id,
"date": date,
"debit": currency.round(credit_sum - debit_sum),
"credit": 0.0,
},
)
line_ids.append(adjust_debit)
if len(line_ids) > 0:
move_dict["line_ids"] = line_ids
move = self.env["account.move"].create(move_dict)
slip.write({"move_id": move.id, "date": date})
move.action_post()
else:
logger.warning(
f"Payslip {slip.number} did not generate any account move lines"
)
return res
def _prepare_debit_line(
self,
line,
slip,
amount,
date,
debit_account_id,
analytic_salary_id,
tax_ids,
tax_tag_ids,
tax_repartition_line_id,
):
return {
"name": line.name,
"partner_id": line._get_partner_id(credit_account=False)
or slip.employee_id.address_home_id.id,
"account_id": debit_account_id,
"journal_id": slip.journal_id.id,
"date": date,
"debit": amount > 0.0 and amount or 0.0,
"credit": amount < 0.0 and -amount or 0.0,
"analytic_account_id": analytic_salary_id
or slip.contract_id.analytic_account_id.id,
"tax_line_id": line.salary_rule_id.account_tax_id.id,
"tax_ids": tax_ids,
"tax_repartition_line_id": tax_repartition_line_id,
"tax_tag_ids": tax_tag_ids,
}
def _prepare_credit_line(
self,
line,
slip,
amount,
date,
credit_account_id,
analytic_salary_id,
tax_ids,
tax_tag_ids,
tax_repartition_line_id,
):
return {
"name": line.name,
"partner_id": line._get_partner_id(credit_account=True)
or slip.employee_id.address_home_id.id,
"account_id": credit_account_id,
"journal_id": slip.journal_id.id,
"date": date,
"debit": amount < 0.0 and -amount or 0.0,
"credit": amount > 0.0 and amount or 0.0,
"analytic_account_id": analytic_salary_id
or slip.contract_id.analytic_account_id.id,
"tax_line_id": line.salary_rule_id.account_tax_id.id,
"tax_ids": tax_ids,
"tax_repartition_line_id": tax_repartition_line_id,
"tax_tag_ids": tax_tag_ids,
}
def _prepare_adjust_credit_line(
self, currency, credit_sum, debit_sum, journal, date
):
acc_id = journal.default_account_id.id
return {
"name": _("Adjustment Entry"),
"partner_id": False,
"account_id": acc_id,
"journal_id": journal.id,
"date": date,
"debit": 0.0,
"credit": currency.round(debit_sum - credit_sum),
}
def _prepare_adjust_debit_line(
self, currency, credit_sum, debit_sum, journal, date
):
acc_id = journal.default_account_id.id
return {
"name": _("Adjustment Entry"),
"partner_id": False,
"account_id": acc_id,
"journal_id": journal.id,
"date": date,
"debit": currency.round(credit_sum - debit_sum),
"credit": 0.0,
}
def _get_tax_details(self, line):
tax_ids = False
tax_tag_ids = False
salary_rule = line.salary_rule_id
if salary_rule.tax_line_ids:
account_tax_ids = [
salary_rule_id.account_tax_id.id
for salary_rule_id in salary_rule.tax_line_ids
]
tax_ids = [(4, account_tax_id, 0) for account_tax_id in account_tax_ids]
tax_tag_ids = (
self.env["account.tax.repartition.line"]
.search(
[
("invoice_tax_id", "in", account_tax_ids),
("repartition_type", "=", "base"),
]
)
.tag_ids
)
tax_repartition_line_id = False
if salary_rule.account_tax_id:
tax_repartition_line_id = (
self.env["account.tax.repartition.line"]
.search(
[
("invoice_tax_id", "=", salary_rule.account_tax_id.id),
(
"account_id",
"=",
salary_rule.account_debit.id
or salary_rule.account_credit.id,
),
]
)
.id
)
tax_tag_ids += (
self.env["account.tax.repartition.line"]
.search(
[
("invoice_tax_id", "=", salary_rule.account_tax_id.id),
("repartition_type", "=", "tax"),
(
"account_id",
"=",
salary_rule.account_debit.id
or salary_rule.account_credit.id,
),
]
)
.tag_ids
)
return tax_ids, tax_tag_ids, tax_repartition_line_id

View file

@ -0,0 +1,24 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class HrPayslipLine(models.Model):
_inherit = "hr.payslip.line"
def _get_partner_id(self, credit_account):
"""
Get partner_id of slip line to use in account_move_line
"""
# use partner of salary rule or fallback on employee's address
register_partner_id = self.salary_rule_id.register_id.partner_id
partner_id = (
register_partner_id.id or self.slip_id.employee_id.address_home_id.id
)
acc_type = self.salary_rule_id.account_debit.account_type
if credit_account:
acc_type = self.salary_rule_id.account_credit.account_type
if register_partner_id or acc_type in ("asset_receivable", "liability_payable"):
return partner_id
return False

View file

@ -0,0 +1,19 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class HrPayslipRun(models.Model):
_inherit = "hr.payslip.run"
journal_id = fields.Many2one(
"account.journal",
"Salary Journal",
states={"draft": [("readonly", False)]},
readonly=True,
required=True,
default=lambda self: self.env["account.journal"].search(
[("type", "=", "general")], limit=1
),
)

View file

@ -0,0 +1,34 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class HrSalaryRule(models.Model):
_inherit = "hr.salary.rule"
company_id = fields.Many2one(
"res.company",
string="Company",
required=True,
default=lambda self: self.env.company,
)
analytic_account_id = fields.Many2one(
"account.analytic.account", "Analytic Account"
)
account_tax_id = fields.Many2one("account.tax", "Tax")
account_debit = fields.Many2one(
"account.account",
"Debit Account",
domain=[("deprecated", "=", False)],
company_dependent=True,
)
account_credit = fields.Many2one(
"account.account",
"Credit Account",
domain=[("deprecated", "=", False)],
company_dependent=True,
)
tax_base_id = fields.Many2one("hr.salary.rule", "Base")
tax_line_ids = fields.One2many("hr.salary.rule", "tax_base_id", string="Tax lines")