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,5 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import hr_payroll_payslips_by_employees
from . import hr_payroll_contribution_register_report
from . import hr_payslip_change_state

View file

@ -0,0 +1,29 @@
from datetime import datetime
from dateutil import relativedelta
from odoo import fields, models
class PayslipLinesContributionRegister(models.TransientModel):
_name = "payslip.lines.contribution.register"
_description = "Payslip Lines by Contribution Registers"
date_from = fields.Date(required=True, default=datetime.now().strftime("%Y-%m-01"))
date_to = fields.Date(
required=True,
default=str(
datetime.now() + relativedelta.relativedelta(months=+1, day=1, days=-1)
)[:10],
)
def print_report(self):
active_ids = self.env.context.get("active_ids", [])
datas = {
"ids": active_ids,
"model": "hr.contribution.register",
"form": self.read()[0],
}
return self.env.ref("payroll.action_contribution_register").report_action(
[], data=datas
)

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_payslip_lines_contribution_register" model="ir.ui.view">
<field name="name">payslip.lines.contribution.register</field>
<field name="model">payslip.lines.contribution.register</field>
<field name="arch" type="xml">
<form string="Contribution Register's Payslip Lines">
<group col="4" colspan="6">
<field name="date_from" />
<newline />
<field name="date_to" />
</group>
<footer>
<button
name="print_report"
string="Print"
type="object"
class="btn-primary"
/>
<button string="Cancel" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record
id="action_payslip_lines_contribution_register"
model="ir.actions.act_window"
>
<field name="name">PaySlip Lines</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">payslip.lines.contribution.register</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="model_hr_contribution_register" />
<field name="binding_type">report</field>
</record>
</odoo>

View file

@ -0,0 +1,52 @@
from odoo import _, fields, models
from odoo.exceptions import UserError
class HrPayslipEmployees(models.TransientModel):
_name = "hr.payslip.employees"
_description = "Generate payslips for all selected employees"
employee_ids = fields.Many2many(
"hr.employee", "hr_employee_group_rel", "payslip_id", "employee_id", "Employees"
)
def compute_sheet(self):
payslips = self.env["hr.payslip"]
[data] = self.read()
active_id = self.env.context.get("active_id")
if active_id:
[run_data] = (
self.env["hr.payslip.run"]
.browse(active_id)
.read(["date_start", "date_end", "credit_note", "struct_id"])
)
from_date = run_data.get("date_start")
to_date = run_data.get("date_end")
struct_id = run_data.get("struct_id")
if not data["employee_ids"]:
raise UserError(_("You must select employee(s) to generate payslip(s)."))
for employee in self.env["hr.employee"].browse(data["employee_ids"]):
slip_data = self.env["hr.payslip"].get_payslip_vals(
from_date, to_date, employee.id, contract_id=False, struct_id=struct_id
)
res = {
"employee_id": employee.id,
"name": slip_data["value"].get("name"),
"struct_id": slip_data["value"].get("struct_id"),
"contract_id": slip_data["value"].get("contract_id"),
"payslip_run_id": active_id,
"input_line_ids": [
(0, 0, x) for x in slip_data["value"].get("input_line_ids")
],
"worked_days_line_ids": [
(0, 0, x) for x in slip_data["value"].get("worked_days_line_ids")
],
"date_from": from_date,
"date_to": to_date,
"credit_note": run_data.get("credit_note"),
"company_id": employee.company_id.id,
}
payslips += self.env["hr.payslip"].create(res)
payslips._compute_name()
payslips.compute_sheet()
return {"type": "ir.actions.act_window_close"}

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_hr_payslip_by_employees" model="ir.ui.view">
<field name="name">payroll_payslip_employees</field>
<field name="model">hr.payslip.employees</field>
<field name="arch" type="xml">
<form string="Payslips by Employees">
<header>
<button
icon="fa-cogs"
string="Generate"
name="compute_sheet"
type="object"
class="oe_highlight"
/>
</header>
<sheet>
<group>
<span colspan="2" nolabel="1">
This wizard will generate payslips for all selected employee(s)
based on the dates and credit note
specified
on Payslips Run.
</span>
</group>
<group string="Employees">
<field name="employee_ids" nolabel="1" colspan="2" />
</group>
</sheet>
</form>
</field>
</record>
<record id="action_hr_payslip_by_employees" model="ir.actions.act_window">
<field name="name">Generate Payslips</field>
<field name="res_model">hr.payslip.employees</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_hr_payslip_by_employees" />
<field name="target">new</field>
</record>
</odoo>

View file

@ -0,0 +1,54 @@
<odoo>
<!-- Mail template are declared in a NOUPDATE block
so users can freely customize/delete them -->
<data noupdate="1">
<!-- Email template -->
<record id="mail_template_hr_payslip" model="mail.template">
<field name="name">Send payslip by email</field>
<field name="model_id" ref="payroll.model_hr_payslip" />
<field name="email_from">{{user.employee_id.work_email}}</field>
<field name="email_to">{{object.employee_id.address_home_id.email}}</field>
<field name="reply_to">{{user.employee_id.work_email}}</field>
<field
name="subject"
>Payslip for {{object.payslip_run_id.name}} from {{object.company_id.name}}</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p style="margin: 0px; padding: 0px; font-size: 13px;">
Dear <t t-out="object.employee_id.name" />,
<br /><br />
Please find attached the payslip for <t
t-out="object.payslip_run_id.name"
/>.
<br /><br />
Do not hesitate to contact us if you have any questions.
<br /><br />
<t t-out="user.signature" />
</p>
</div>
</field>
<field name="report_template" ref="payroll.action_report_payslip" />
<field name="lang">{{object.employee_id.address_home_id.lang}}</field>
<field name="auto_delete" eval="True" />
</record>
</data>
<data>
<record id="action_partner_mass_mail" model="ir.actions.act_window">
<field name="name">Send email with payslip</field>
<field name="res_model">mail.compose.message</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field
name="context"
eval="{
'default_composition_mode': 'mass_mail',
'default_use_template': True,
'default_template_id': ref('payroll.mail_template_hr_payslip'),
}"
/>
<field name="binding_model_id" ref="payroll.model_hr_payslip" />
<field name="binding_view_types">list</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,92 @@
# Copyright 2019 - Eficent http://www.eficent.com/
# Copyright 2019 Serpent Consulting Services Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
from odoo.exceptions import UserError
from odoo.tools.translate import _
class HrPayslipChangeState(models.TransientModel):
_name = "hr.payslip.change.state"
_description = "Change state of a payslip"
state = fields.Selection(
selection=[
("draft", "Set to Draft"),
("verify", "Compute Sheet"),
("done", "Confirm"),
("cancel", "Cancel Payslip"),
],
string="Action",
help="* When the payslip is created the status is 'Draft'.\
\n* If the payslip is under verification, the status is "
"'Compute Sheet'. \
\n* If the payslip is confirmed then status is set to 'Done'.\
\n* When user cancel payslip the status is 'Rejected'.",
)
def change_state_confirm(self):
record_ids = self.env.context.get("active_ids", False)
payslip_obj = self.env["hr.payslip"]
new_state = self.state
records = payslip_obj.browse(record_ids)
for rec in records:
if new_state == "draft":
if rec.state == "cancel":
rec.action_payslip_draft()
else:
raise UserError(
_(
"Only rejected payslips can be reset to "
"draft, the payslip %(nm)s is in "
"%(st)s state"
)
% {"nm": rec.name, "st": rec.state}
)
elif new_state == "verify":
if rec.state in ["draft", "verify"]:
rec.compute_sheet()
else:
raise UserError(
_(
"Only draft payslips can be verified,"
"the payslip %(nm)s is in "
"%(st)s state"
)
% {"nm": rec.name, "st": rec.state}
)
elif new_state == "done":
if rec.state in ("verify", "draft"):
rec.action_payslip_done()
else:
raise UserError(
_(
"Only payslips in states verify or draft"
" can be confirmed, the payslip %(nm)s is in "
"%(st)s state"
)
% {"nm": rec.name, "st": rec.state}
)
elif new_state == "cancel":
if rec.state != "cancel":
rec.action_payslip_cancel()
else:
raise UserError(
_(
"The payslip %(nm)s is already canceled "
"please deselect it"
)
% {"nm": rec.name}
)
return {
"domain": "[('id','in', [" + ",".join(map(str, record_ids)) + "])]",
"name": _("Payslips"),
"view_mode": "tree,form",
"res_model": "hr.payslip",
"view_id": False,
"context": False,
"type": "ir.actions.act_window",
}

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--Copyright 2019 - Eficent http://www.eficent.com/-->
<!--Copyright 2019 Serpent Consulting Services Pvt. Ltd.-->
<!--License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).-->
<record id="hr_payslip_change_state_form" model="ir.ui.view">
<field name="name">hr.payslip.change.state.form</field>
<field name="model">hr.payslip.change.state</field>
<field name="arch" type="xml">
<form string="Change state">
<group>
<field name="state" />
</group>
<footer>
<button
string="Execute"
name="change_state_confirm"
type="object"
class="oe_highlight"
/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_hr_payslip_change_state_form" model="ir.actions.act_window">
<field name="name">Change state</field>
<field name="res_model">hr.payslip.change.state</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="hr_payslip_change_state_form" />
<field name="target">new</field>
<field name="binding_model_id" ref="payroll.model_hr_payslip" />
</record>
</odoo>