You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

260 lines
12 KiB

# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Anfas Faisal K (odoo@cybrosys.info)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
import pandas as pd
from datetime import timedelta, datetime, date
from collections import defaultdict
from dateutil.relativedelta import relativedelta
from pytz import utc
from odoo.tools import float_utils
from odoo import api, fields, models
from odoo.http import request
ROUNDING_FACTOR = 16
class Employee(models.Model):
"""
This class extends the HR Employee model to include additional fields
and functionalities
"""
_inherit = 'hr.employee'
is_manager = fields.Boolean(compute='_compute_is_manager',
help="Flag indicating whether the employee is a"
"manager.")
def _compute_is_manager(self):
"""Compute function for checking whether it is a manager or not"""
for rec in self:
if (rec.env.user.has_group
('hr_payroll_community.group_hr_payroll_community_manager')):
rec.is_manager = True
else:
rec.is_manager = False
@api.model
def get_user_employee_info(self):
"""To get the employee information"""
uid = request.session.uid
employee_user_id = self.env['hr.employee'].sudo().search([
('user_id', '=', uid)
], limit=1)
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)], limit=1)
attendance_count = self.env['hr.attendance'].sudo().search(
[('employee_id', '=', employee_user_id.id),
('attendance_date', '=', date.today())])
manager_attendance_count = self.env['hr.attendance'].sudo().search(
[('attendance_date', '=', date.today())])
leave_request_count = self.env['hr.leave'].sudo().search(
[('employee_id', '=', employee_user_id.id),
('request_date_from', '=', date.today())])
manager_leave_request = self.env['hr.leave'].sudo().search(
[('request_date_from', '=', date.today())])
employee_contracts = self.env['hr.contract'].sudo().search([
('employee_id', '=', employee_user_id.id)])
payslips = self.env['hr.payslip'].sudo().search([
('employee_id', '=', employee_user_id.id)])
salary_rules = self.env['hr.salary.rule'].sudo().search([])
salary_structures = self.env['hr.payroll.structure'].sudo().search([])
salary_rule_count = len(salary_rules)
salary_structure_count = len(salary_structures)
emp_leave = len(manager_leave_request) if employee_user_id.is_manager \
else len(leave_request_count)
payslip_count = len(payslips) if not employee_user_id.is_manager \
else len(self.env['hr.payslip'].sudo().search([]))
emp_contracts_count = len(employee_contracts) \
if not employee_user_id.is_manager else len(
self.env['hr.contract'].sudo().search([]))
attendance_today = len(manager_attendance_count) \
if employee_user_id.is_manager else len(attendance_count)
if employee:
data = {
'emp_timesheets': attendance_today,
'emp_leave': emp_leave,
'emp_contracts_count': emp_contracts_count,
'payslip_count': payslip_count,
'leave_requests': leave_request_count,
'salary_rule_count': salary_rule_count,
'salary_structure_count': salary_structure_count,
'attendance_state': employee[0]['attendance_state'],
}
employee[0].update(data)
return employee
def get_work_days_dashboard(self, from_datetime, to_datetime,
compute_leaves=False, calendar=None,
domain=None):
"""
Calculate the total work days between two datetimes.
"""
resource = self.resource_id
calendar = calendar or self.resource_calendar_id
if not from_datetime.tzinfo:
from_datetime = from_datetime.replace(tzinfo=utc)
if not to_datetime.tzinfo:
to_datetime = to_datetime.replace(tzinfo=utc)
from_full = from_datetime - timedelta(days=1)
to_full = to_datetime + timedelta(days=1)
intervals = calendar._attendance_intervals_batch(from_full, to_full,
resource)
day_total = defaultdict(float)
for start, stop, meta in intervals[resource.id]:
day_total[start.date()] += (stop - start).total_seconds() / 3600
if compute_leaves:
intervals = calendar._work_intervals_batch(from_datetime,
to_datetime, resource,
domain)
else:
intervals = calendar._attendance_intervals_batch(from_datetime,
to_datetime,
resource)
day_hours = defaultdict(float)
for start, stop, meta in intervals[resource.id]:
day_hours[start.date()] += (stop - start).total_seconds() / 3600
days = sum(
float_utils.round(ROUNDING_FACTOR * day_hours[day] / day_total[
day]) / ROUNDING_FACTOR
for day in day_hours
)
return days
@api.model
def get_department_leave(self):
""" Return department leave details."""
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y')
for i in range(5, -1, -1)]
self.env.cr.execute(
"""select id, name from hr_department where active=True """)
departments = self.env.cr.dictfetchall()
department_list = [x['name'] for x in departments]
graph_result = [{
'l_month': month,
'leave': {dept['name']: 0 for dept in departments}
} for month in month_list]
sql = """
SELECT h.id, h.employee_id,h.department_id
, extract('month' FROM y)::int AS leave_month
, to_char(y, 'Month YYYY') as month_year
, GREATEST(y , h.date_from) AS date_from
, LEAST (y + interval '1 month', h.date_to) AS date_to
FROM (select * from hr_leave where state = 'validate') h
, generate_series(date_trunc('month', date_from::timestamp)
, date_trunc('month', date_to::timestamp)
, interval '1 month') y
where date_trunc('month', GREATEST(y , h.date_from)) >= date_trunc('month', now()) - interval '6 month' and
date_trunc('month', GREATEST(y , h.date_from)) <= date_trunc('month', now())
and h.department_id is not null
"""
self.env.cr.execute(sql)
results = self.env.cr.dictfetchall()
leave_lines = [{
'department': line['department_id'],
'l_month': line['month_year'],
'days': self.browse(line['employee_id']).get_work_days_dashboard(
fields.Datetime.from_string(line['date_from']),
fields.Datetime.from_string(line['date_to'])
)
} for line in results]
if leave_lines:
df = pd.DataFrame(leave_lines)
rf = df.groupby(['l_month', 'department']).sum()
result_lines = rf.to_dict('index')
for month in month_list:
for line in result_lines:
if month.replace(' ', '') == line[0].replace(' ', ''):
match = list(filter(lambda d: d['l_month'] in [month],
graph_result))[0]['leave']
dept_name = self.env['hr.department'].browse(
line[1]).name
if match:
match[dept_name] = result_lines[line]['days']
for result in graph_result:
result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[
:3] + " " + \
result['l_month'].split(' ')[1:2][0]
return graph_result, department_list
@api.model
def employee_leave_trend(self):
"""Return employee monthly leave details"""
month_list = [format(datetime.now() - relativedelta(months=i), '%B %Y')
for i in range(5, -1, -1)]
uid = request.session.uid
employee = False
employee_user = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
employees = self.env['hr.employee'].sudo().search_read([], limit=1)
if employee_user:
employee = self.env['hr.employee'].sudo().search_read([
('user_id', '=', uid)
], limit=1)
elif employees:
employee = self.env['hr.employee'].sudo().search_read([], limit=1)
graph_result = [{
'l_month': month,
'leave': 0
} for month in month_list]
sql = """
SELECT h.id, h.employee_id
, extract('month' FROM y)::int AS leave_month
, to_char(y, 'Month YYYY') as month_year
, GREATEST(y , h.date_from) AS date_from
, LEAST (y + interval '1 month', h.date_to) AS date_to
FROM (select * from hr_leave where state = 'validate') h
, generate_series(date_trunc('month', date_from::timestamp)
, date_trunc('month', date_to::timestamp)
, interval '1 month') y
where date_trunc('month', GREATEST(y , h.date_from)) >=
date_trunc('month', now()) - interval '6 month' and
date_trunc('month', GREATEST(y , h.date_from)) <=
date_trunc('month', now())
and h.employee_id = %s
"""
if employee:
self.env.cr.execute(sql, (employee[0]['id'],))
results = self.env.cr.dictfetchall()
leave_lines = [{
'l_month': line['month_year'],
'days': self.browse(
line['employee_id']).get_work_days_dashboard(
fields.Datetime.from_string(line['date_from']),
fields.Datetime.from_string(line['date_to'])
)
} for line in results]
if leave_lines:
df = pd.DataFrame(leave_lines)
rf = df.groupby(['l_month']).sum()
result_lines = rf.to_dict('index')
for line in result_lines:
match = list(filter(lambda d: d['l_month'].replace(
' ', '') == line.replace(' ', ''), graph_result))
if match:
match[0]['leave'] = result_lines[line]['days']
for result in graph_result:
result['l_month'] = result['l_month'].split(' ')[:1][0].strip()[
:3] \
+ " " + result['l_month'].split(' ')[1:2][0]
return graph_result
else:
return False