1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/hr_payroll/report/hr_payroll_report.py
2024-12-10 09:04:09 +07:00

162 lines
7.7 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from ast import literal_eval
from psycopg2 import sql
from odoo import api, fields, models, tools
class HrPayrollReport(models.Model):
_name = "hr.payroll.report"
_description = "Payroll Analysis Report"
_auto = False
_rec_name = 'date_from'
_order = 'date_from desc'
count = fields.Integer('# Payslip', group_operator="sum", readonly=True)
count_work = fields.Integer('Work Days', group_operator="sum", readonly=True)
count_work_hours = fields.Integer('Work Hours', group_operator="sum", readonly=True)
count_leave = fields.Integer('Days of Paid Time Off', group_operator="sum", readonly=True)
count_leave_unpaid = fields.Integer('Days of Unpaid Time Off', group_operator="sum", readonly=True)
count_unforeseen_absence = fields.Integer('Days of Unforeseen Absence', group_operator="sum", readonly=True)
name = fields.Char('Payslip Name', readonly=True)
type = fields.Char('Type', readonly=True)
date_from = fields.Date('Start Date', readonly=True)
date_to = fields.Date('End Date', readonly=True)
company_id = fields.Many2one('res.company', 'Company', readonly=True)
employee_id = fields.Many2one('hr.employee', 'Employee', readonly=True)
department_id = fields.Many2one('hr.department', 'Department', readonly=True)
master_department_id = fields.Many2one('hr.department', 'Master Department', readonly=True)
job_id = fields.Many2one('hr.job', 'Job Position', readonly=True)
number_of_days = fields.Float('Number of Days', readonly=True)
number_of_hours = fields.Float('Number of Hours', readonly=True)
net_wage = fields.Float('Net Wage', readonly=True)
basic_wage = fields.Float('Basic Wage', readonly=True)
gross_wage = fields.Float('Gross Wage', readonly=True)
leave_basic_wage = fields.Float('Basic Wage for Time Off', readonly=True)
# Some of those might not be enabled (requiring respective work_entry modules) but adding them separately would require
# a module just for that
work_entry_source = fields.Selection([
('calendar', 'Working Schedule'),
('attendance', 'Attendances'),
('planning', 'Planning')], readonly=True)
work_code = fields.Many2one('hr.work.entry.type', 'Work type', readonly=True)
work_type = fields.Selection([
('1', 'Regular Working Day'),
('2', 'Paid Time Off'),
('3', 'Unpaid Time Off')], string='Work, (un)paid Time Off', readonly=True)
def _select(self, additional_rules):
select_str = """
SELECT
row_number() over() as id,
CASE WHEN wd.id IS NOT DISTINCT FROM min_id.min_line THEN 1 ELSE 0 END as count,
CASE WHEN wet.is_leave THEN 0 ELSE wd.number_of_days END as count_work,
CASE WHEN wet.is_leave THEN 0 ELSE wd.number_of_hours END as count_work_hours,
CASE WHEN wet.is_leave and wd.amount <> 0 THEN wd.number_of_days ELSE 0 END as count_leave,
CASE WHEN wet.is_leave and wd.amount = 0 THEN wd.number_of_days ELSE 0 END as count_leave_unpaid,
CASE WHEN wet.is_unforeseen THEN wd.number_of_days ELSE 0 END as count_unforeseen_absence,
CASE WHEN wet.is_leave THEN wd.amount ELSE 0 END as leave_basic_wage,
p.name as name,
wd.name as type,
p.date_from as date_from,
p.date_to as date_to,
e.id as employee_id,
e.department_id as department_id,
d.master_department_id as master_department_id,
c.job_id as job_id,
c.work_entry_source as work_entry_source,
e.company_id as company_id,
wet.id as work_code,
CASE WHEN wet.is_leave IS NOT TRUE THEN '1' WHEN wd.amount = 0 THEN '3' ELSE '2' END as work_type,
wd.number_of_days as number_of_days,
wd.number_of_hours as number_of_hours,"""
handled_fields = []
for rule in additional_rules:
field_name = rule._get_report_field_name()
if field_name in handled_fields:
continue
handled_fields.append(field_name)
select_str += """
SUM(
DISTINCT CASE WHEN wd.id IS NOT DISTINCT FROM min_id.min_line THEN "%s".total ELSE 0 END
) as "%s",""" % (field_name, field_name)
select_str += """
CASE WHEN wd.id IS NOT DISTINCT FROM min_id.min_line THEN pln.total ELSE 0 END as net_wage,
CASE WHEN wd.id IS NOT DISTINCT FROM min_id.min_line THEN plb.total ELSE 0 END as basic_wage,
CASE WHEN wd.id IS NOT DISTINCT FROM min_id.min_line THEN plg.total ELSE 0 END as gross_wage"""
return select_str
def _from(self, additional_rules):
from_str = """
FROM
(SELECT * FROM hr_payslip WHERE state IN ('done', 'paid')) p
left join hr_employee e on (p.employee_id = e.id)
left join hr_payslip_worked_days wd on (wd.payslip_id = p.id)
left join hr_work_entry_type wet on (wet.id = wd.work_entry_type_id)
left join (select payslip_id, min(id) as min_line from hr_payslip_worked_days group by payslip_id) min_id on (min_id.payslip_id = p.id)
left join hr_payslip_line pln on (pln.slip_id = p.id and pln.code = 'NET')
left join hr_payslip_line plb on (plb.slip_id = p.id and plb.code = 'BASIC')
left join hr_payslip_line plg on (plg.slip_id = p.id and plg.code = 'GROSS')
left join hr_contract c on (p.contract_id = c.id)
left join hr_department d on (e.department_id = d.id)"""
handled_fields = []
for rule in additional_rules:
field_name = rule._get_report_field_name()
if field_name in handled_fields:
continue
handled_fields.append(field_name)
from_str += """
left join hr_payslip_line "%s" on ("%s".slip_id = p.id and "%s".code = '%s')""" % (
field_name, field_name, field_name, rule.code)
return from_str
def _group_by(self, additional_rules):
group_by_str = """
GROUP BY
e.id,
e.department_id,
d.master_department_id,
e.company_id,
wd.id,
wet.id,
p.id,
p.name,
wd.name,
p.date_from,
p.date_to,
pln.total,
plb.total,
plg.total,
min_id.min_line,
c.id"""
return group_by_str
def init(self):
additional_rules = self.env['hr.salary.rule'].sudo().search([('appears_on_payroll_report', '=', True)])
query = """
%s
%s
%s""" % (self._select(additional_rules), self._from(additional_rules), self._group_by(additional_rules))
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute(
sql.SQL("CREATE or REPLACE VIEW {} as ({})").format(
sql.Identifier(self._table),
sql.SQL(query)))
@api.model
def _get_action(self):
# We have to make a method as literal_eval is forbidden in the server action safe_eval context
action = self.env['ir.actions.act_window']._for_xml_id('hr_payroll.payroll_report_action')
context = literal_eval(action.get('context', '{}'))
country_code = self.env.company.country_id.code
if country_code:
context.update({'country_code': country_code.lower()})
action['context'] = context
return action