fix: validate Standard Working Hours
- use standard working hours in metrics calculation
This commit is contained in:
parent
b7a7e1b11a
commit
0decc2b66a
@ -146,7 +146,6 @@
|
|||||||
"label": "Send Leave Notification"
|
"label": "Send Leave Notification"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "8",
|
|
||||||
"fieldname": "standard_working_hours",
|
"fieldname": "standard_working_hours",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"label": "Standard Working Hours"
|
"label": "Standard Working Hours"
|
||||||
@ -156,7 +155,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-16 15:45:18.467699",
|
"modified": "2021-04-26 10:52:56.192773",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR Settings",
|
"name": "HR Settings",
|
||||||
|
@ -19,13 +19,22 @@ class EmployeeHoursReport:
|
|||||||
self.to_date = getdate(self.filters.to_date)
|
self.to_date = getdate(self.filters.to_date)
|
||||||
|
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
|
self.validate_standard_working_hours()
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
self.day_span = (self.to_date - self.from_date).days
|
self.day_span = (self.to_date - self.from_date).days
|
||||||
|
|
||||||
if self.day_span <= 0:
|
if self.day_span <= 0:
|
||||||
frappe.throw(_('From Date must come before To Date'))
|
frappe.throw(_('From Date must come before To Date'))
|
||||||
|
|
||||||
|
def validate_standard_working_hours(self):
|
||||||
|
self.standard_working_hours = frappe.db.get_single_value('HR Settings', 'standard_working_hours')
|
||||||
|
if not self.standard_working_hours:
|
||||||
|
msg = _('The metrics for this report are calculated based on the Standard Working Hours. Please set {0} in {1}.').format(
|
||||||
|
frappe.bold('Standard Working Hours'), frappe.utils.get_link_to_form('HR Settings', 'HR Settings'))
|
||||||
|
|
||||||
|
frappe.throw(msg)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.generate_columns()
|
self.generate_columns()
|
||||||
self.generate_data()
|
self.generate_data()
|
||||||
@ -47,7 +56,7 @@ class EmployeeHoursReport:
|
|||||||
'label': _('Department'),
|
'label': _('Department'),
|
||||||
'options': 'Department',
|
'options': 'Department',
|
||||||
'fieldname': 'department',
|
'fieldname': 'department',
|
||||||
'fieldtype': 'Link',
|
'fieldtype': 'Link',
|
||||||
'width': 170
|
'width': 170
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -81,7 +90,7 @@ class EmployeeHoursReport:
|
|||||||
'width': 200
|
'width': 200
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def generate_data(self):
|
def generate_data(self):
|
||||||
self.generate_filtered_time_logs()
|
self.generate_filtered_time_logs()
|
||||||
self.generate_stats_by_employee()
|
self.generate_stats_by_employee()
|
||||||
@ -108,7 +117,7 @@ class EmployeeHoursReport:
|
|||||||
for emp, data in self.stats_by_employee.items():
|
for emp, data in self.stats_by_employee.items():
|
||||||
if data['department'] == self.filters.department:
|
if data['department'] == self.filters.department:
|
||||||
filtered_data[emp] = data
|
filtered_data[emp] = data
|
||||||
|
|
||||||
# Update stats
|
# Update stats
|
||||||
self.stats_by_employee = filtered_data
|
self.stats_by_employee = filtered_data
|
||||||
|
|
||||||
@ -126,8 +135,8 @@ class EmployeeHoursReport:
|
|||||||
|
|
||||||
self.filtered_time_logs = frappe.db.sql('''
|
self.filtered_time_logs = frappe.db.sql('''
|
||||||
SELECT tt.employee AS employee, ttd.hours AS hours, ttd.billable AS billable, ttd.project AS project
|
SELECT tt.employee AS employee, ttd.hours AS hours, ttd.billable AS billable, ttd.project AS project
|
||||||
FROM `tabTimesheet Detail` AS ttd
|
FROM `tabTimesheet Detail` AS ttd
|
||||||
JOIN `tabTimesheet` AS tt
|
JOIN `tabTimesheet` AS tt
|
||||||
ON ttd.parent = tt.name
|
ON ttd.parent = tt.name
|
||||||
WHERE tt.employee IS NOT NULL
|
WHERE tt.employee IS NOT NULL
|
||||||
AND tt.start_date BETWEEN '{0}' AND '{1}'
|
AND tt.start_date BETWEEN '{0}' AND '{1}'
|
||||||
@ -161,10 +170,9 @@ class EmployeeHoursReport:
|
|||||||
|
|
||||||
self.stats_by_employee[emp]['department'] = emp_dept
|
self.stats_by_employee[emp]['department'] = emp_dept
|
||||||
self.stats_by_employee[emp]['employee_name'] = emp_name
|
self.stats_by_employee[emp]['employee_name'] = emp_name
|
||||||
|
|
||||||
def calculate_utilizations(self):
|
def calculate_utilizations(self):
|
||||||
# (9.0) Will be fetched from HR settings
|
TOTAL_HOURS = flt(self.standard_working_hours * self.day_span, 2)
|
||||||
TOTAL_HOURS = flt(9.0 * self.day_span, 2)
|
|
||||||
for emp, data in iteritems(self.stats_by_employee):
|
for emp, data in iteritems(self.stats_by_employee):
|
||||||
data['total_hours'] = TOTAL_HOURS
|
data['total_hours'] = TOTAL_HOURS
|
||||||
data['untracked_hours'] = flt(TOTAL_HOURS - data['billed_hours'] - data['non_billed_hours'], 2)
|
data['untracked_hours'] = flt(TOTAL_HOURS - data['billed_hours'] - data['non_billed_hours'], 2)
|
||||||
@ -172,14 +180,14 @@ class EmployeeHoursReport:
|
|||||||
# To handle overtime edge-case
|
# To handle overtime edge-case
|
||||||
if data['untracked_hours'] < 0:
|
if data['untracked_hours'] < 0:
|
||||||
data['untracked_hours'] = 0.0
|
data['untracked_hours'] = 0.0
|
||||||
|
|
||||||
data['per_util'] = flt(((data['billed_hours'] + data['non_billed_hours']) / TOTAL_HOURS) * 100, 2)
|
data['per_util'] = flt(((data['billed_hours'] + data['non_billed_hours']) / TOTAL_HOURS) * 100, 2)
|
||||||
|
|
||||||
def generate_report_summary(self):
|
def generate_report_summary(self):
|
||||||
self.report_summary = []
|
self.report_summary = []
|
||||||
|
|
||||||
if not self.data:
|
if not self.data:
|
||||||
return
|
return
|
||||||
|
|
||||||
avg_utilization = 0.0
|
avg_utilization = 0.0
|
||||||
total_billed, total_non_billed = 0.0, 0.0
|
total_billed, total_non_billed = 0.0, 0.0
|
||||||
@ -233,7 +241,7 @@ class EmployeeHoursReport:
|
|||||||
billed_hours.append(row.get('billed_hours'))
|
billed_hours.append(row.get('billed_hours'))
|
||||||
non_billed_hours.append(row.get('non_billed_hours'))
|
non_billed_hours.append(row.get('non_billed_hours'))
|
||||||
untracked_hours.append(row.get('untracked_hours'))
|
untracked_hours.append(row.get('untracked_hours'))
|
||||||
|
|
||||||
self.chart = {
|
self.chart = {
|
||||||
'data': {
|
'data': {
|
||||||
'labels': labels[:30],
|
'labels': labels[:30],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user