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
88 changes: 88 additions & 0 deletions payroll/tests/test_payslip_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from datetime import timedelta

from odoo.exceptions import UserError
from odoo.fields import Date
from odoo.tests import Form
from odoo.tools import test_reports
Expand Down Expand Up @@ -284,6 +285,93 @@ def test_compute_sheet_no_valid_contract(self):
"There are no lines because there are no valid contracts",
)

def test_payslip_employees_clear_action(self):
payslip_run = self.env["hr.payslip.run"].create(
{
"date_end": "2011-09-30",
"date_start": "2011-09-01",
"name": "Payslip wizard clear employees",
}
)
payslip_employee = self.env["hr.payslip.employees"].create(
{"employee_ids": [(4, self.richard_emp.id)]}
)

self.assertTrue(
payslip_employee.employee_ids,
"Wizard must start with at least one employee selected",
)

action = payslip_employee.with_context(
active_id=payslip_run.id,
active_model="hr.payslip.run",
).action_clear_employees()
self.assertFalse(
payslip_employee.employee_ids,
"Wizard employee selection should be empty after clear action",
)
self.assertTrue(
payslip_run.exists(),
"Payslip run must still exist after clear action on wizard",
)
self.assertEqual(
action.get("res_id"),
payslip_employee.id,
"Clear action should keep current wizard record opened",
)
self.assertEqual(
action.get("context", {}).get("active_id"),
payslip_run.id,
"Clear action should preserve active_id context for payslip run",
)

with self.assertRaises(UserError):
payslip_employee.with_context(active_id=payslip_run.id).compute_sheet()

def test_payslip_employees_apply_filter_action(self):
department = self.env["hr.department"].create(
{"name": "Payroll Filter Department"}
)
job = self.env["hr.job"].create({"name": "Payroll Filter Job"})
employee_match = self.env["hr.employee"].create(
{
"name": "Payroll Employee Match",
"department_id": department.id,
"job_id": job.id,
"company_id": self.env.company.id,
}
)
employee_other = self.env["hr.employee"].create(
{
"name": "Payroll Employee Other",
"company_id": self.env.company.id,
}
)
payslip_employee = self.env["hr.payslip.employees"].create(
{
"company_id": self.env.company.id,
"department_id": department.id,
"job_id": job.id,
"employee_ids": [(4, employee_other.id)],
}
)

action = payslip_employee.with_context(
active_id=999,
active_model="hr.payslip.run",
).action_apply_employee_filter()

self.assertEqual(
payslip_employee.employee_ids.ids,
employee_match.ids,
"Apply Filter must replace selected employees with domain results",
)
self.assertEqual(
action.get("context", {}).get("active_id"),
999,
"Apply Filter should preserve active_id context",
)

def _get_developer_rules(self):
developer_rules = self.SalaryRule
developer_rules |= self.rule_basic
Expand Down
38 changes: 38 additions & 0 deletions payroll/wizard/hr_payroll_payslips_by_employees.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,48 @@ class HrPayslipEmployees(models.TransientModel):
_name = "hr.payslip.employees"
_description = "Generate payslips for all selected employees"

company_id = fields.Many2one(
"res.company",
string="Company",
default=lambda self: self.env.company,
)
department_id = fields.Many2one("hr.department", string="Department")
job_id = fields.Many2one("hr.job", string="Job Position")
employee_ids = fields.Many2many(
"hr.employee", "hr_employee_group_rel", "payslip_id", "employee_id", "Employees"
)

def _get_reopen_wizard_action(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"res_model": self._name,
"res_id": self.id,
"view_mode": "form",
"target": "new",
"context": dict(self.env.context),
}

def action_apply_employee_filter(self):
self.ensure_one()
domain = []
if self.company_id:
domain.append(("company_id", "=", self.company_id.id))
if self.department_id:
domain.append(("department_id", "=", self.department_id.id))
if self.job_id:
domain.append(("job_id", "=", self.job_id.id))
employee_ids = self.env["hr.employee"].search(domain).ids
self.write({"employee_ids": [(6, 0, employee_ids)]})
return self._get_reopen_wizard_action()

def action_clear_employees(self):
self.ensure_one()
self.write({"employee_ids": [(5, 0, 0)]})
# Preserve originating action context (active_id on hr.payslip.run). A dict
# return is required: other truthy values become act_window_close in web.
return self._get_reopen_wizard_action()

def compute_sheet(self):
payslips = self.env["hr.payslip"]
[data] = self.read()
Expand Down
24 changes: 24 additions & 0 deletions payroll/wizard/hr_payroll_payslips_by_employees_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@
<field name="arch" type="xml">
<form string="Payslips by Employees">
<header>
<button
string="Apply Filter"
name="action_apply_employee_filter"
type="object"
icon="fa-filter"
class="oe_highlight"
/>
<button
string="Clear Employees"
name="action_clear_employees"
type="object"
icon="fa-cogs"
class="oe_highlight"
/>
<button
icon="fa-cogs"
string="Generate"
Expand All @@ -23,6 +37,16 @@
on Payslips Run.
</span>
</group>
<group string="Filter Employees">
<field name="company_id" />
<field name="department_id" />
<field name="job_id" />
</group>
<group>
<span colspan="2" nolabel="1">
Apply Filter replaces the current employee selection.
</span>
</group>
<group string="Employees">
<field name="employee_ids" nolabel="1" colspan="2" />
</group>
Expand Down
Loading