@@ -265,7 +265,7 @@
// Hide all containers first
$(".pivot-wrapper").hide();
-
+
// Determine current container and row config
let containerId = "";
let rowsConfig = [];
@@ -280,14 +280,14 @@
containerId = "pivot-onboarding";
rowsConfig = ["Recruitment","Candidates","Stage","Stage Manager","Task","Task Manager","Company"];
}
-
+
// Show relevant container
$("#" + containerId).show();
$.getJSON(url, function (data) {
// Add Plotly renderers correctly
let plotlyRenderers = $.pivotUtilities.plotly_renderers;
-
+
// Initialize pivot table with Plotly enabled
$("#" + containerId).pivotUI(data, {
rows: rowsConfig,
@@ -296,7 +296,7 @@
rendererName: "Table", // Default view as Table
onRefresh: function (config) {
let currentRenderer = config.rendererName;
- if (currentRenderer === "Table" || currentRenderer === "Table Barchart" ||
+ if (currentRenderer === "Table" || currentRenderer === "Table Barchart" ||
currentRenderer === "Heatmap" || currentRenderer === "Row Heatmap" || currentRenderer === "Col Heatmap" ) {
$("#export-btn").show(); // Show button for tables
} else {
@@ -329,12 +329,12 @@
window.loadFilteredPivotData =function loadFilteredPivotData() {
const selectedModel = $("#model-select").val();
const formData = $("#filterForm").serialize();
-
+
$(".pivot-wrapper").hide();
-
+
let containerId = "";
let rowsConfig = [];
-
+
if (selectedModel === "candidate") {
containerId = "pivot-candidate";
rowsConfig = ["Recruitment","Job Position","Department","Candidate","Gender","Email"];
@@ -345,13 +345,13 @@
containerId = "pivot-onboarding";
rowsConfig = ["Recruitment","Candidates","Stage","Stage Manager","Task","Task Manager","Company"];
}
-
+
$("#" + containerId).show();
-
+
$.getJSON(`recruitment-pivot?model=${selectedModel}&${formData}`, function (data) {
const plotlyRenderers = $.pivotUtilities.plotly_renderers;
-
+
$("#" + containerId).pivotUI(data, {
rows: rowsConfig,
cols: [],
@@ -365,7 +365,7 @@
} else {
$("#export-btn").hide();
}
-
+
}
});
});
@@ -374,7 +374,7 @@
// Initial load with all models
loadPivotData("candidate");
-
+
// Model selection change event
$("#model-select").on("change", function () {
let selectedModel = $(this).val();
@@ -397,14 +397,14 @@
alert("No table found to export.");
return;
}
-
+
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("Pivot Data");
const baseRow = 5;
const baseCol = 5;
-
+
let currentRow = baseRow;
-
+
// Add company details first (if not 'all')
if ('{{company}}' !== 'all') {
const companyDetails = {
@@ -415,7 +415,7 @@
city: "{{ company.city|escapejs }}",
zip: "{{ company.zip|escapejs }}"
};
-
+
function getBase64FromUrl(url) {
return fetch(url)
.then(response => response.blob())
@@ -426,7 +426,7 @@
reader.readAsDataURL(blob);
}));
}
-
+
const logoUrl = "{{ protocol }}://{{ host }}{{ company.icon.url }}";
await getBase64FromUrl(logoUrl).then((base64) => {
const base64Data = base64.split(',')[1];
@@ -434,13 +434,13 @@
base64: base64Data,
extension: 'png'
});
-
+
worksheet.addImage(imageId, {
tl: { col: baseCol - 1, row: currentRow - 1 },
ext: { width: 80, height: 80 }
});
});
-
+
// Merge cells for company details text
const companyTextCell = worksheet.getCell(currentRow, baseCol + 1);
worksheet.mergeCells(currentRow, baseCol + 1, currentRow, baseCol + 2);
@@ -458,35 +458,35 @@
wrapText: true
};
worksheet.getRow(currentRow).height = 80;
-
+
currentRow += 2; // Leave a blank row
}
-
+
// Add timestamp
const timestamp = new Date().toLocaleDateString('en-GB') + ' ' +
new Date().toLocaleTimeString('en-US', {
hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true
});
-
+
const downloadCell = worksheet.getCell(currentRow, baseCol);
worksheet.mergeCells(currentRow, baseCol, currentRow, baseCol + 3);
downloadCell.value = `Generated on: ${timestamp}`;
downloadCell.alignment = { horizontal: 'left', vertical: 'middle', wrapText: true };
downloadCell.font = { size: 10, italic: true, color: { argb: 'FF666666' }, bold: true };
-
+
currentRow += 3; // Leave some rows before the table
-
+
// ------------------------
// Render pivot table
// ------------------------
const cellMap = {};
const allRows = Array.from(table.rows);
const lastRowIndex = allRows.length - 1;
-
+
allRows.forEach((row, rowIndex) => {
-
+
let colIndex = baseCol;
-
+
Array.from(row.cells).forEach((cell) => {
if (
@@ -495,21 +495,21 @@
cell.classList.contains("pvtAggregator") ||
cell.classList.contains("pvtGrandTotal")
) return;
-
+
while (cellMap[`${currentRow + rowIndex}-${colIndex}`]) {
colIndex++;
}
-
+
const rowspan = parseInt(cell.getAttribute("rowspan")) || 1;
const colspan = parseInt(cell.getAttribute("colspan")) || 1;
const cellValue = cell.textContent.trim();
-
+
const excelCell = worksheet.getCell(currentRow + rowIndex, colIndex);
excelCell.value = cellValue;
-
+
const isHeader = rowIndex === 0;
const isLastRow = rowIndex === lastRowIndex;
-
+
excelCell.font = {
bold: isHeader || isLastRow,
size: isHeader ? 12 : 11,
@@ -519,7 +519,7 @@
'FF000000'
}
};
-
+
excelCell.fill = {
type: 'pattern',
pattern: 'solid',
@@ -537,7 +537,7 @@
right: { style: 'thin' }
};
excelCell.alignment = { horizontal: "center", vertical: "middle" };
-
+
// Merge
if (rowspan > 1 || colspan > 1) {
worksheet.mergeCells(
@@ -546,7 +546,7 @@
currentRow + rowIndex + rowspan - 1,
colIndex + colspan - 1
);
-
+
for (let r = 0; r < rowspan; r++) {
for (let c = 0; c < colspan; c++) {
cellMap[`${currentRow + rowIndex + r}-${colIndex + c}`] = true;
@@ -555,15 +555,15 @@
} else {
cellMap[`${currentRow + rowIndex}-${colIndex}`] = true;
}
-
+
colIndex++;
});
});
-
+
worksheet.getRow(currentRow + lastRowIndex).height = 25; // adjust height for Total
-
+
worksheet.getRow(currentRow).height = 30; // adjust height for Heading
-
+
// Auto-adjust column widths
worksheet.columns.forEach(column => {
let maxLength = 2;
@@ -573,13 +573,13 @@
});
column.width = maxLength + 3;
});
-
+
// Save
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
-
+
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
@@ -587,7 +587,7 @@
}
});
-
+
diff --git a/report/urls.py b/report/urls.py
index 8296004f0..f25c872e3 100644
--- a/report/urls.py
+++ b/report/urls.py
@@ -1,70 +1,84 @@
-
-from django.urls import path
-from report.views import asset_report, employee_report,attendance_report,leave_report,payroll_report, pms_report,recruitment_report
from django.apps import apps
+from django.urls import path
-
-
+from report.views import (
+ asset_report,
+ attendance_report,
+ employee_report,
+ leave_report,
+ payroll_report,
+ pms_report,
+ recruitment_report,
+)
urlpatterns = [
-
- path("employee-report",employee_report.employee_report,name='employee-report'),
- path("employee-pivot",employee_report.employee_pivot,name='employee-pivot'),
-
+ path("employee-report", employee_report.employee_report, name="employee-report"),
+ path("employee-pivot", employee_report.employee_pivot, name="employee-pivot"),
]
if apps.is_installed("recruitment"):
urlpatterns.extend(
[
- path("recruitment-report",recruitment_report.recruitment_report,name='recruitment-report'),
- path("recruitment-pivot",recruitment_report.recruitment_pivot,name='recruitment-pivot'),
-
+ path(
+ "recruitment-report",
+ recruitment_report.recruitment_report,
+ name="recruitment-report",
+ ),
+ path(
+ "recruitment-pivot",
+ recruitment_report.recruitment_pivot,
+ name="recruitment-pivot",
+ ),
]
)
if apps.is_installed("attendance"):
urlpatterns.extend(
[
- path("attendance-report",attendance_report.attendance_report,name='attendance-report'),
- path("attendance-pivot",attendance_report.attendance_pivot,name='attendance-pivot'),
-
+ path(
+ "attendance-report",
+ attendance_report.attendance_report,
+ name="attendance-report",
+ ),
+ path(
+ "attendance-pivot",
+ attendance_report.attendance_pivot,
+ name="attendance-pivot",
+ ),
]
)
if apps.is_installed("leave"):
urlpatterns.extend(
[
- path("leave-report",leave_report.leave_report, name="leave-report"),
- path("leave-pivot",leave_report.leave_pivot,name='leave-pivot'),
-
+ path("leave-report", leave_report.leave_report, name="leave-report"),
+ path("leave-pivot", leave_report.leave_pivot, name="leave-pivot"),
]
)
if apps.is_installed("payroll"):
urlpatterns.extend(
[
- path("payroll-report",payroll_report.payroll_report, name="payroll-report"),
- path("payroll-pivot",payroll_report.payroll_pivot,name='payroll-pivot'),
-
+ path(
+ "payroll-report", payroll_report.payroll_report, name="payroll-report"
+ ),
+ path("payroll-pivot", payroll_report.payroll_pivot, name="payroll-pivot"),
]
)
if apps.is_installed("asset"):
urlpatterns.extend(
[
- path("asset-report",asset_report.asset_report, name="asset-report"),
- path("asset-pivot",asset_report.asset_pivot,name='asset-pivot'),
-
+ path("asset-report", asset_report.asset_report, name="asset-report"),
+ path("asset-pivot", asset_report.asset_pivot, name="asset-pivot"),
]
)
if apps.is_installed("pms"):
urlpatterns.extend(
[
- path("pms-report",pms_report.pms_report, name="pms-report"),
- path("pms-pivot",pms_report.pms_pivot,name='pms-pivot'),
-
+ path("pms-report", pms_report.pms_report, name="pms-report"),
+ path("pms-pivot", pms_report.pms_pivot, name="pms-pivot"),
]
)
-
diff --git a/report/views/asset_report.py b/report/views/asset_report.py
index ed861d580..fbec4c5ae 100644
--- a/report/views/asset_report.py
+++ b/report/views/asset_report.py
@@ -1,25 +1,32 @@
+from django.apps import apps
from django.http import JsonResponse
from django.shortcuts import render
-from django.apps import apps
if apps.is_installed("asset"):
from asset.filters import AssetFilter
+ from asset.models import Asset
from base.models import Company
from horilla_views.cbv_methods import login_required, permission_required
- from asset.models import Asset
@login_required
@permission_required(perm="asset.view_asset")
def asset_report(request):
- company = 'all'
+ company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
+ if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
asset_filter_form = AssetFilter()
- return render(request, "report/asset_report.html",{"company":company,"asset_filter_form": asset_filter_form.form,})
+ return render(
+ request,
+ "report/asset_report.html",
+ {
+ "company": company,
+ "asset_filter_form": asset_filter_form.form,
+ },
+ )
@login_required
@permission_required(perm="asset.view_asset")
@@ -27,52 +34,121 @@ if apps.is_installed("asset"):
qs = Asset.objects.all()
if asset_name := request.GET.get("asset_name"):
- qs = qs.filter(asset_name = asset_name)
+ qs = qs.filter(asset_name=asset_name)
if asset_tracking_id := request.GET.get("asset_tracking_id"):
- qs = qs.filter(asset_tracking_id = asset_tracking_id)
+ qs = qs.filter(asset_tracking_id=asset_tracking_id)
if asset_purchase_cost := request.GET.get("asset_purchase_cost"):
- qs = qs.filter(asset_purchase_cost = asset_purchase_cost)
+ qs = qs.filter(asset_purchase_cost=asset_purchase_cost)
if asset_lot_number_id := request.GET.get("asset_lot_number_id"):
- qs = qs.filter(asset_lot_number_id = asset_lot_number_id)
+ qs = qs.filter(asset_lot_number_id=asset_lot_number_id)
if asset_category_id := request.GET.get("asset_category_id"):
- qs = qs.filter(asset_category_id = asset_category_id)
+ qs = qs.filter(asset_category_id=asset_category_id)
if asset_status := request.GET.get("asset_status"):
- qs = qs.filter(asset_status = asset_status)
+ qs = qs.filter(asset_status=asset_status)
if asset_purchase_date := request.GET.get("asset_purchase_date"):
- qs = qs.filter(asset_purchase_date = asset_purchase_date)
+ qs = qs.filter(asset_purchase_date=asset_purchase_date)
- data = list(qs.values(
- "asset_name","asset_purchase_date","asset_tracking_id",
- "asset_purchase_cost","asset_status","asset_category_id__asset_category_name","asset_lot_number_id__lot_number",
- "expiry_date","assetassignment__assigned_by_employee_id__employee_work_info__department_id__department",
- "assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position",
- "assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role",
- "assetassignment__assigned_by_employee_id__email","assetassignment__assigned_by_employee_id__phone",
- "assetassignment__assigned_by_employee_id__gender","assetassignment__assigned_by_employee_id__employee_first_name",
- "assetassignment__assigned_by_employee_id__employee_last_name","assetassignment__assigned_date",
- "assetassignment__return_date","assetassignment__return_status",
-
- ))
+ data = list(
+ qs.values(
+ "asset_name",
+ "asset_purchase_date",
+ "asset_tracking_id",
+ "asset_purchase_cost",
+ "asset_status",
+ "asset_category_id__asset_category_name",
+ "asset_lot_number_id__lot_number",
+ "expiry_date",
+ "assetassignment__assigned_by_employee_id__employee_work_info__department_id__department",
+ "assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position",
+ "assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role",
+ "assetassignment__assigned_by_employee_id__email",
+ "assetassignment__assigned_by_employee_id__phone",
+ "assetassignment__assigned_by_employee_id__gender",
+ "assetassignment__assigned_by_employee_id__employee_first_name",
+ "assetassignment__assigned_by_employee_id__employee_last_name",
+ "assetassignment__assigned_date",
+ "assetassignment__return_date",
+ "assetassignment__return_status",
+ )
+ )
data_list = [
{
- "Asset Name" : item["asset_name"],
- "Asset User": f"{item['assetassignment__assigned_by_employee_id__employee_first_name']} {item['assetassignment__assigned_by_employee_id__employee_last_name']}" if item["assetassignment__assigned_by_employee_id__employee_first_name"] or item["assetassignment__assigned_by_employee_id__employee_last_name"] else "-",
- "Email":item["assetassignment__assigned_by_employee_id__email"] if item["assetassignment__assigned_by_employee_id__email"] else "-",
- "Phone":item["assetassignment__assigned_by_employee_id__phone"] if item["assetassignment__assigned_by_employee_id__phone"] else "-",
- "Gender":item["assetassignment__assigned_by_employee_id__gender"] if item["assetassignment__assigned_by_employee_id__gender"] else "-",
- "Department":item["assetassignment__assigned_by_employee_id__employee_work_info__department_id__department"] if item["assetassignment__assigned_by_employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position":item["assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position"] if item["assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role":item["assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role"] if item["assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Asset Purchce Date":item["asset_purchase_date"],
- "Asset Cost":item["asset_purchase_cost"],
- "Status":item["asset_status"],
- "Assigned Date":item["assetassignment__assigned_date"] if item["assetassignment__assigned_date"] else "-",
- "Return Date":item["assetassignment__return_date"] if item["assetassignment__return_date"] else "-",
- "Return Condition":item["assetassignment__return_status"] if item["assetassignment__return_status"] else "-",
- "Category":item["asset_category_id__asset_category_name"],
- "Batch Number":item["asset_lot_number_id__lot_number"],
- "Tracking ID":item["asset_tracking_id"],
- "Expiry Date":item["expiry_date"] if item["expiry_date"] else "-",
- }for item in data
+ "Asset Name": item["asset_name"],
+ "Asset User": (
+ f"{item['assetassignment__assigned_by_employee_id__employee_first_name']} {item['assetassignment__assigned_by_employee_id__employee_last_name']}"
+ if item[
+ "assetassignment__assigned_by_employee_id__employee_first_name"
+ ]
+ or item[
+ "assetassignment__assigned_by_employee_id__employee_last_name"
+ ]
+ else "-"
+ ),
+ "Email": (
+ item["assetassignment__assigned_by_employee_id__email"]
+ if item["assetassignment__assigned_by_employee_id__email"]
+ else "-"
+ ),
+ "Phone": (
+ item["assetassignment__assigned_by_employee_id__phone"]
+ if item["assetassignment__assigned_by_employee_id__phone"]
+ else "-"
+ ),
+ "Gender": (
+ item["assetassignment__assigned_by_employee_id__gender"]
+ if item["assetassignment__assigned_by_employee_id__gender"]
+ else "-"
+ ),
+ "Department": (
+ item[
+ "assetassignment__assigned_by_employee_id__employee_work_info__department_id__department"
+ ]
+ if item[
+ "assetassignment__assigned_by_employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "assetassignment__assigned_by_employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item[
+ "assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ if item[
+ "assetassignment__assigned_by_employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ else "-"
+ ),
+ "Asset Purchce Date": item["asset_purchase_date"],
+ "Asset Cost": item["asset_purchase_cost"],
+ "Status": item["asset_status"],
+ "Assigned Date": (
+ item["assetassignment__assigned_date"]
+ if item["assetassignment__assigned_date"]
+ else "-"
+ ),
+ "Return Date": (
+ item["assetassignment__return_date"]
+ if item["assetassignment__return_date"]
+ else "-"
+ ),
+ "Return Condition": (
+ item["assetassignment__return_status"]
+ if item["assetassignment__return_status"]
+ else "-"
+ ),
+ "Category": item["asset_category_id__asset_category_name"],
+ "Batch Number": item["asset_lot_number_id__lot_number"],
+ "Tracking ID": item["asset_tracking_id"],
+ "Expiry Date": item["expiry_date"] if item["expiry_date"] else "-",
+ }
+ for item in data
]
return JsonResponse(data_list, safe=False)
diff --git a/report/views/attendance_report.py b/report/views/attendance_report.py
index d9d810102..95f635f9a 100644
--- a/report/views/attendance_report.py
+++ b/report/views/attendance_report.py
@@ -1,16 +1,15 @@
-from datetime import time,datetime
-
-from django.http import JsonResponse
-from django.shortcuts import render
+from datetime import datetime, time
from django.apps import apps
+from django.http import JsonResponse
+from django.shortcuts import render
if apps.is_installed("attendance"):
from attendance.filters import AttendanceFilters
+ from attendance.models import Attendance
from base.models import Company
from horilla_views.cbv_methods import login_required, permission_required
- from attendance.models import Attendance
def convert_time_to_decimal_w(time_str):
try:
@@ -20,15 +19,13 @@ if apps.is_installed("attendance"):
hours, minutes = time_str.hour, time_str.minute
else:
return "00.00"
-
+
# Format as HH.MM
formatted_time = f"{hours:02}.{minutes:02}"
return formatted_time
except (ValueError, TypeError):
return "00.00"
-
-
def convert_time_to_decimal(time_str):
"""Format time as HH.MM for aggregation."""
try:
@@ -45,30 +42,53 @@ if apps.is_installed("attendance"):
except Exception:
return "00.00"
- @login_required
+ @login_required
@permission_required(perm="attendance.view_attendance")
def attendance_report(request):
- company = 'all'
+ company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
+ if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
- return render(request, "report/attendance_report.html",{'company':company,"f": AttendanceFilters() })
+ return render(
+ request,
+ "report/attendance_report.html",
+ {"company": company, "f": AttendanceFilters()},
+ )
@login_required
@permission_required(perm="attendance.view_attendance")
def attendance_pivot(request):
- qs =Attendance.objects.all()
+ qs = Attendance.objects.all()
filter_obj = AttendanceFilters(request.GET, queryset=qs)
qs = filter_obj.qs
- data = list(qs.values(
- 'employee_id__employee_first_name','employee_id__employee_last_name','attendance_date','attendance_clock_in','attendance_clock_out',
- 'attendance_worked_hour','minimum_hour','attendance_overtime','at_work_second','work_type_id__work_type','shift_id__employee_shift',
- 'attendance_day__day','employee_id__gender','employee_id__email','employee_id__phone','employee_id__employee_work_info__department_id__department', 'employee_id__employee_work_info__job_role_id__job_role',
- 'employee_id__employee_work_info__job_position_id__job_position', 'employee_id__employee_work_info__employee_type_id__employee_type',
- 'employee_id__employee_work_info__experience', 'batch_attendance_id__title','employee_id__employee_work_info__company_id__company',
- ))
+ data = list(
+ qs.values(
+ "employee_id__employee_first_name",
+ "employee_id__employee_last_name",
+ "attendance_date",
+ "attendance_clock_in",
+ "attendance_clock_out",
+ "attendance_worked_hour",
+ "minimum_hour",
+ "attendance_overtime",
+ "at_work_second",
+ "work_type_id__work_type",
+ "shift_id__employee_shift",
+ "attendance_day__day",
+ "employee_id__gender",
+ "employee_id__email",
+ "employee_id__phone",
+ "employee_id__employee_work_info__department_id__department",
+ "employee_id__employee_work_info__job_role_id__job_role",
+ "employee_id__employee_work_info__job_position_id__job_position",
+ "employee_id__employee_work_info__employee_type_id__employee_type",
+ "employee_id__employee_work_info__experience",
+ "batch_attendance_id__title",
+ "employee_id__employee_work_info__company_id__company",
+ )
+ )
DAY = {
"monday": "Monday",
"tuesday": "Tuesday",
@@ -89,44 +109,80 @@ if apps.is_installed("attendance"):
"Gender": choice_gender.get(item["employee_id__gender"]),
"Email": item["employee_id__email"],
"Phone": item["employee_id__phone"],
- "Department": item["employee_id__employee_work_info__department_id__department"] if item["employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position": item["employee_id__employee_work_info__job_position_id__job_position"] if item["employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role": item["employee_id__employee_work_info__job_role_id__job_role"] if item["employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Work Type": item["work_type_id__work_type"] if item["work_type_id__work_type"] else "-",
- "Shift": item["shift_id__employee_shift"] if item["shift_id__employee_shift"] else "-",
+ "Department": (
+ item["employee_id__employee_work_info__department_id__department"]
+ if item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item["employee_id__employee_work_info__job_role_id__job_role"]
+ if item["employee_id__employee_work_info__job_role_id__job_role"]
+ else "-"
+ ),
+ "Work Type": (
+ item["work_type_id__work_type"]
+ if item["work_type_id__work_type"]
+ else "-"
+ ),
+ "Shift": (
+ item["shift_id__employee_shift"]
+ if item["shift_id__employee_shift"]
+ else "-"
+ ),
"Experience": item["employee_id__employee_work_info__experience"],
- "Attendance Date": item['attendance_date'],
- "Attendance Day": DAY.get(item['attendance_day__day']),
- "Clock-in": format_time(item['attendance_clock_in']),
- "Clock-out": format_time(item['attendance_clock_out']),
- "At Work": format_seconds_to_time(item['at_work_second']),
- "Worked Hour": item['attendance_worked_hour'],
- "Minimum Hour": item['minimum_hour'],
- "Overtime": item['attendance_overtime'],
- "Batch":item['batch_attendance_id__title'] if item['batch_attendance_id__title'] else "-",
- "Company":item['employee_id__employee_work_info__company_id__company'],
-
+ "Attendance Date": item["attendance_date"],
+ "Attendance Day": DAY.get(item["attendance_day__day"]),
+ "Clock-in": format_time(item["attendance_clock_in"]),
+ "Clock-out": format_time(item["attendance_clock_out"]),
+ "At Work": format_seconds_to_time(item["at_work_second"]),
+ "Worked Hour": item["attendance_worked_hour"],
+ "Minimum Hour": item["minimum_hour"],
+ "Overtime": item["attendance_overtime"],
+ "Batch": (
+ item["batch_attendance_id__title"]
+ if item["batch_attendance_id__title"]
+ else "-"
+ ),
+ "Company": item["employee_id__employee_work_info__company_id__company"],
# For correct total
- "Clock-in Decimal": convert_time_to_decimal(item["attendance_clock_in"]),
- "Clock-out Decimal": convert_time_to_decimal(item["attendance_clock_out"]),
- "At Work Decimal": convert_time_to_decimal_w(format_seconds_to_time(item['at_work_second'])),
- "Worked Hour Decimal": convert_time_to_decimal_w(item["attendance_worked_hour"]),
- "Minimum Hour Decimal": convert_time_to_decimal_w(item["minimum_hour"]),
- "Overtime Decimal": convert_time_to_decimal_w(item['attendance_overtime']),
-
+ "Clock-in Decimal": convert_time_to_decimal(
+ item["attendance_clock_in"]
+ ),
+ "Clock-out Decimal": convert_time_to_decimal(
+ item["attendance_clock_out"]
+ ),
+ "At Work Decimal": convert_time_to_decimal_w(
+ format_seconds_to_time(item["at_work_second"])
+ ),
+ "Worked Hour Decimal": convert_time_to_decimal_w(
+ item["attendance_worked_hour"]
+ ),
+ "Minimum Hour Decimal": convert_time_to_decimal_w(item["minimum_hour"]),
+ "Overtime Decimal": convert_time_to_decimal_w(
+ item["attendance_overtime"]
+ ),
}
for item in data
]
return JsonResponse(data_list, safe=False)
-
# Helper function to format time
def format_time(time_value):
if isinstance(time_value, str): # In case time is string
time_value = datetime.strptime(time_value, "%H:%M:%S").time()
return time_value.strftime("%H:%M") if time_value else ""
-
def format_seconds_to_time(seconds):
"""Convert seconds to HH:MM format."""
try:
@@ -136,4 +192,3 @@ if apps.is_installed("attendance"):
return f"{hours:02}:{minutes:02}"
except (ValueError, TypeError):
return "00:00"
-
diff --git a/report/views/employee_report.py b/report/views/employee_report.py
index f33a9541a..07c28adb4 100644
--- a/report/views/employee_report.py
+++ b/report/views/employee_report.py
@@ -3,54 +3,105 @@ from django.shortcuts import render
from base.models import Company
from employee.filters import EmployeeFilter
-from horilla_views.cbv_methods import login_required, permission_required
from employee.models import Employee
+from horilla_views.cbv_methods import login_required, permission_required
@login_required
@permission_required(perm="employee.view_employee")
def employee_report(request):
- company = 'all'
+ company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
+ if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
- return render(request, "report/employee_report.html",{'company':company, "f":EmployeeFilter()})
+ return render(
+ request,
+ "report/employee_report.html",
+ {"company": company, "f": EmployeeFilter()},
+ )
+
@login_required
@permission_required(perm="employee.view_employee")
def employee_pivot(request):
qs = Employee.objects.all()
- filtered_qs = EmployeeFilter(request.GET,queryset=qs)
+ filtered_qs = EmployeeFilter(request.GET, queryset=qs)
qs = filtered_qs.qs
- data = list(qs.values(
- 'employee_first_name','employee_last_name', 'gender','email','phone','employee_work_info__department_id__department','employee_work_info__job_position_id__job_position',
- 'employee_work_info__job_role_id__job_role','employee_work_info__work_type_id__work_type','employee_work_info__shift_id__employee_shift','employee_work_info__employee_type_id__employee_type',
- 'employee_work_info__reporting_manager_id__employee_first_name','employee_work_info__reporting_manager_id__employee_last_name','employee_work_info__company_id__company','employee_work_info__date_joining',
- 'employee_work_info__experience'
- ))
+ data = list(
+ qs.values(
+ "employee_first_name",
+ "employee_last_name",
+ "gender",
+ "email",
+ "phone",
+ "employee_work_info__department_id__department",
+ "employee_work_info__job_position_id__job_position",
+ "employee_work_info__job_role_id__job_role",
+ "employee_work_info__work_type_id__work_type",
+ "employee_work_info__shift_id__employee_shift",
+ "employee_work_info__employee_type_id__employee_type",
+ "employee_work_info__reporting_manager_id__employee_first_name",
+ "employee_work_info__reporting_manager_id__employee_last_name",
+ "employee_work_info__company_id__company",
+ "employee_work_info__date_joining",
+ "employee_work_info__experience",
+ )
+ )
choice_gender = {
"male": "Male",
"female": "Female",
"other": "Other",
}
-
+
# Transform data to match format
data_list = [
{
- "Name": f"{item['employee_first_name']} {item['employee_last_name']}",
+ "Name": f"{item['employee_first_name']} {item['employee_last_name']}",
"Gender": choice_gender.get(item["gender"]),
"Email": item["email"],
"Phone": item["phone"],
- "Department": item["employee_work_info__department_id__department"] if item["employee_work_info__department_id__department"] else "-",
- "Job Position": item["employee_work_info__job_position_id__job_position"] if item["employee_work_info__job_position_id__job_position"] else "-",
- "Job Role": item["employee_work_info__job_role_id__job_role"] if item["employee_work_info__job_role_id__job_role"] else "-",
- "Work Type": item["employee_work_info__work_type_id__work_type"] if item["employee_work_info__work_type_id__work_type"] else "-",
- "Shift": item["employee_work_info__shift_id__employee_shift"] if item["employee_work_info__shift_id__employee_shift"] else "-",
- "Employee Type": item["employee_work_info__employee_type_id__employee_type"] if item["employee_work_info__employee_type_id__employee_type"] else "-",
- "Reporting Manager": f"{item['employee_work_info__reporting_manager_id__employee_first_name']} {item['employee_work_info__reporting_manager_id__employee_last_name']}" if item['employee_work_info__reporting_manager_id__employee_first_name'] else '-' ,
- "Date of Joining": item["employee_work_info__date_joining"] if item["employee_work_info__date_joining"] else '-',
+ "Department": (
+ item["employee_work_info__department_id__department"]
+ if item["employee_work_info__department_id__department"]
+ else "-"
+ ),
+ "Job Position": (
+ item["employee_work_info__job_position_id__job_position"]
+ if item["employee_work_info__job_position_id__job_position"]
+ else "-"
+ ),
+ "Job Role": (
+ item["employee_work_info__job_role_id__job_role"]
+ if item["employee_work_info__job_role_id__job_role"]
+ else "-"
+ ),
+ "Work Type": (
+ item["employee_work_info__work_type_id__work_type"]
+ if item["employee_work_info__work_type_id__work_type"]
+ else "-"
+ ),
+ "Shift": (
+ item["employee_work_info__shift_id__employee_shift"]
+ if item["employee_work_info__shift_id__employee_shift"]
+ else "-"
+ ),
+ "Employee Type": (
+ item["employee_work_info__employee_type_id__employee_type"]
+ if item["employee_work_info__employee_type_id__employee_type"]
+ else "-"
+ ),
+ "Reporting Manager": (
+ f"{item['employee_work_info__reporting_manager_id__employee_first_name']} {item['employee_work_info__reporting_manager_id__employee_last_name']}"
+ if item["employee_work_info__reporting_manager_id__employee_first_name"]
+ else "-"
+ ),
+ "Date of Joining": (
+ item["employee_work_info__date_joining"]
+ if item["employee_work_info__date_joining"]
+ else "-"
+ ),
"Experience": round(float(item["employee_work_info__experience"] or 0), 2),
"Company": item["employee_work_info__company_id__company"],
}
diff --git a/report/views/leave_report.py b/report/views/leave_report.py
index fb3799550..dafefe30e 100644
--- a/report/views/leave_report.py
+++ b/report/views/leave_report.py
@@ -1,6 +1,6 @@
+from django.apps import apps
from django.http import JsonResponse
from django.shortcuts import render
-from django.apps import apps
if apps.is_installed("leave"):
@@ -9,40 +9,63 @@ if apps.is_installed("leave"):
from leave.filters import AssignedLeaveFilter, LeaveRequestFilter
from leave.models import AvailableLeave, LeaveRequest
-
@login_required
@permission_required(perm="leave.view_leaverequest")
def leave_report(request):
company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
- company = Company.objects.filter(id = selected_company).first()
+ if selected_company != "all":
+ company = Company.objects.filter(id=selected_company).first()
leave_request_filter = LeaveRequestFilter()
- return render(request, "report/leave_report.html",{'company' : company, "form": leave_request_filter.form, "f": AssignedLeaveFilter(),} )
+ return render(
+ request,
+ "report/leave_report.html",
+ {
+ "company": company,
+ "form": leave_request_filter.form,
+ "f": AssignedLeaveFilter(),
+ },
+ )
@login_required
@permission_required(perm="leave.view_leaverequest")
def leave_pivot(request):
- model_type = request.GET.get("model", "leave_request") # Default to LeaveRequest
+ model_type = request.GET.get(
+ "model", "leave_request"
+ ) # Default to LeaveRequest
if model_type == "leave_request":
-
+
qs = LeaveRequest.objects.all()
leave_filter = LeaveRequestFilter(request.GET, queryset=qs)
qs = leave_filter.qs
- data = list(qs.values(
- "employee_id__employee_first_name","employee_id__employee_last_name","leave_type_id__name",
- "start_date","start_date_breakdown","end_date","end_date_breakdown","requested_days","status",
- 'employee_id__gender','employee_id__email','employee_id__phone','employee_id__employee_work_info__department_id__department',
- 'employee_id__employee_work_info__job_role_id__job_role','employee_id__employee_work_info__job_position_id__job_position',
- 'employee_id__employee_work_info__employee_type_id__employee_type','employee_id__employee_work_info__experience',
- 'employee_id__employee_work_info__work_type_id__work_type','employee_id__employee_work_info__shift_id__employee_shift',
- 'employee_id__employee_work_info__company_id__company'
-
- ))
+ data = list(
+ qs.values(
+ "employee_id__employee_first_name",
+ "employee_id__employee_last_name",
+ "leave_type_id__name",
+ "start_date",
+ "start_date_breakdown",
+ "end_date",
+ "end_date_breakdown",
+ "requested_days",
+ "status",
+ "employee_id__gender",
+ "employee_id__email",
+ "employee_id__phone",
+ "employee_id__employee_work_info__department_id__department",
+ "employee_id__employee_work_info__job_role_id__job_role",
+ "employee_id__employee_work_info__job_position_id__job_position",
+ "employee_id__employee_work_info__employee_type_id__employee_type",
+ "employee_id__employee_work_info__experience",
+ "employee_id__employee_work_info__work_type_id__work_type",
+ "employee_id__employee_work_info__shift_id__employee_shift",
+ "employee_id__employee_work_info__company_id__company",
+ )
+ )
BREAKDOWN_MAP = {
"full_day": "Full Day",
"first_half": "First Half",
@@ -67,38 +90,95 @@ if apps.is_installed("leave"):
"Gender": choice_gender.get(item["employee_id__gender"]),
"Email": item["employee_id__email"],
"Phone": item["employee_id__phone"],
- "Department": item["employee_id__employee_work_info__department_id__department"] if item["employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position": item["employee_id__employee_work_info__job_position_id__job_position"] if item["employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role": item["employee_id__employee_work_info__job_role_id__job_role"] if item["employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Work Type": item["employee_id__employee_work_info__work_type_id__work_type"] if item["employee_id__employee_work_info__work_type_id__work_type"] else "-",
- "Shift": item["employee_id__employee_work_info__shift_id__employee_shift"] if item["employee_id__employee_work_info__shift_id__employee_shift"] else "-",
+ "Department": (
+ item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ if item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item["employee_id__employee_work_info__job_role_id__job_role"]
+ if item[
+ "employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ else "-"
+ ),
+ "Work Type": (
+ item["employee_id__employee_work_info__work_type_id__work_type"]
+ if item[
+ "employee_id__employee_work_info__work_type_id__work_type"
+ ]
+ else "-"
+ ),
+ "Shift": (
+ item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ if item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ else "-"
+ ),
"Experience": item["employee_id__employee_work_info__experience"],
"Leave Type": item["leave_type_id__name"],
"Start Date": item["start_date"],
- "Start Date Breakdown": BREAKDOWN_MAP.get(item["start_date_breakdown"], "-"),
- "End Date Breakdown": BREAKDOWN_MAP.get(item["end_date_breakdown"], "-"),
+ "Start Date Breakdown": BREAKDOWN_MAP.get(
+ item["start_date_breakdown"], "-"
+ ),
+ "End Date Breakdown": BREAKDOWN_MAP.get(
+ item["end_date_breakdown"], "-"
+ ),
"End Date": item["end_date"],
"Requested Days": item["requested_days"],
"Status": LEAVE_STATUS.get(item["status"]),
- "Company":item['employee_id__employee_work_info__company_id__company'],
+ "Company": item[
+ "employee_id__employee_work_info__company_id__company"
+ ],
}
for item in data
]
elif model_type == "available_leave":
qs = AvailableLeave.objects.all()
- available_leave_filter = AssignedLeaveFilter(request.GET, queryset= qs)
+ available_leave_filter = AssignedLeaveFilter(request.GET, queryset=qs)
qs = available_leave_filter.qs
- data = list(qs.values(
- "employee_id__employee_first_name","employee_id__employee_last_name","leave_type_id__name",
- "available_days","carryforward_days","total_leave_days","assigned_date","reset_date","expired_date",
- 'employee_id__gender','employee_id__email','employee_id__phone','employee_id__employee_work_info__department_id__department',
- 'employee_id__employee_work_info__job_role_id__job_role','employee_id__employee_work_info__job_position_id__job_position',
- 'employee_id__employee_work_info__employee_type_id__employee_type','employee_id__employee_work_info__experience',
- 'employee_id__employee_work_info__work_type_id__work_type','employee_id__employee_work_info__shift_id__employee_shift',
- 'employee_id__employee_work_info__company_id__company',
- ))
+ data = list(
+ qs.values(
+ "employee_id__employee_first_name",
+ "employee_id__employee_last_name",
+ "leave_type_id__name",
+ "available_days",
+ "carryforward_days",
+ "total_leave_days",
+ "assigned_date",
+ "reset_date",
+ "expired_date",
+ "employee_id__gender",
+ "employee_id__email",
+ "employee_id__phone",
+ "employee_id__employee_work_info__department_id__department",
+ "employee_id__employee_work_info__job_role_id__job_role",
+ "employee_id__employee_work_info__job_position_id__job_position",
+ "employee_id__employee_work_info__employee_type_id__employee_type",
+ "employee_id__employee_work_info__experience",
+ "employee_id__employee_work_info__work_type_id__work_type",
+ "employee_id__employee_work_info__shift_id__employee_shift",
+ "employee_id__employee_work_info__company_id__company",
+ )
+ )
choice_gender = {
"male": "Male",
"female": "Female",
@@ -110,11 +190,47 @@ if apps.is_installed("leave"):
"Gender": choice_gender.get(item["employee_id__gender"]),
"Email": item["employee_id__email"],
"Phone": item["employee_id__phone"],
- "Department": item["employee_id__employee_work_info__department_id__department"] if item["employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position": item["employee_id__employee_work_info__job_position_id__job_position"] if item["employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role": item["employee_id__employee_work_info__job_role_id__job_role"] if item["employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Work Type": item["employee_id__employee_work_info__work_type_id__work_type"] if item["employee_id__employee_work_info__work_type_id__work_type"] else "-",
- "Shift": item["employee_id__employee_work_info__shift_id__employee_shift"] if item["employee_id__employee_work_info__shift_id__employee_shift"] else "-",
+ "Department": (
+ item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ if item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item["employee_id__employee_work_info__job_role_id__job_role"]
+ if item[
+ "employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ else "-"
+ ),
+ "Work Type": (
+ item["employee_id__employee_work_info__work_type_id__work_type"]
+ if item[
+ "employee_id__employee_work_info__work_type_id__work_type"
+ ]
+ else "-"
+ ),
+ "Shift": (
+ item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ if item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ else "-"
+ ),
"Experience": item["employee_id__employee_work_info__experience"],
"Leave Type": item["leave_type_id__name"],
"Available Days": item["available_days"],
@@ -123,7 +239,9 @@ if apps.is_installed("leave"):
"Assigned Date": item["assigned_date"],
"Reset Date": item.get("reset_date", "-") or "-",
"Expired Date": item.get("expired_date", "-") or "-",
- "Company":item['employee_id__employee_work_info__company_id__company'],
+ "Company": item[
+ "employee_id__employee_work_info__company_id__company"
+ ],
}
for item in data
]
diff --git a/report/views/payroll_report.py b/report/views/payroll_report.py
index e3e317ef4..8c03aacdc 100644
--- a/report/views/payroll_report.py
+++ b/report/views/payroll_report.py
@@ -1,7 +1,7 @@
+from django.apps import apps
from django.http import JsonResponse
from django.shortcuts import render
from django.utils.dateparse import parse_date
-from django.apps import apps
if apps.is_installed("payroll"):
@@ -10,30 +10,35 @@ if apps.is_installed("payroll"):
from payroll.filters import PayslipFilter
from payroll.models.models import Payslip
-
@login_required
@permission_required(perm="payroll.view_payslip")
def payroll_report(request):
- company = 'all'
+ company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
+ if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
-
+
if request.user.has_perm("payroll.view_payslip"):
payslips = Payslip.objects.all()
else:
- payslips = Payslip.objects.filter(employee_id__employee_user_id=request.user)
+ payslips = Payslip.objects.filter(
+ employee_id__employee_user_id=request.user
+ )
filter_form = PayslipFilter(request.GET, payslips)
- return render(request, "report/payroll_report.html",{'company':company,"f":filter_form})
+ return render(
+ request,
+ "report/payroll_report.html",
+ {"company": company, "f": filter_form},
+ )
@login_required
@permission_required(perm="payroll.view_payslip")
def payroll_pivot(request):
model_type = request.GET.get("model", "payslip")
- if model_type == 'payslip':
+ if model_type == "payslip":
qs = Payslip.objects.all()
if employee_id := request.GET.getlist("employee_id"):
@@ -56,7 +61,7 @@ if apps.is_installed("payroll"):
qs = qs.filter(end_date__gte=end_date_from)
if end_date_to:
qs = qs.filter(end_date__lte=end_date_to)
-
+
# Gross Pay Range
gross_pay_gte = request.GET.get("gross_pay__gte")
gross_pay_lte = request.GET.get("gross_pay__lte")
@@ -81,16 +86,32 @@ if apps.is_installed("payroll"):
if net_pay_lte:
qs = qs.filter(net_pay__lte=net_pay_lte)
-
- data = list(qs.values(
- 'id', # Include payslip ID to fetch pay_head_data later
- 'employee_id__employee_first_name', 'employee_id__employee_last_name', 'employee_id__gender', 'employee_id__email',
- 'employee_id__phone', 'start_date', 'end_date', 'contract_wage', 'basic_pay', 'gross_pay', 'deduction', 'net_pay','group_name',
- 'status', 'employee_id__employee_work_info__department_id__department', 'employee_id__employee_work_info__job_role_id__job_role',
- 'employee_id__employee_work_info__job_position_id__job_position', 'employee_id__employee_work_info__work_type_id__work_type',
- 'employee_id__employee_work_info__shift_id__employee_shift', 'employee_id__employee_work_info__employee_type_id__employee_type',
- 'employee_id__employee_work_info__experience',
- ))
+ data = list(
+ qs.values(
+ "id", # Include payslip ID to fetch pay_head_data later
+ "employee_id__employee_first_name",
+ "employee_id__employee_last_name",
+ "employee_id__gender",
+ "employee_id__email",
+ "employee_id__phone",
+ "start_date",
+ "end_date",
+ "contract_wage",
+ "basic_pay",
+ "gross_pay",
+ "deduction",
+ "net_pay",
+ "group_name",
+ "status",
+ "employee_id__employee_work_info__department_id__department",
+ "employee_id__employee_work_info__job_role_id__job_role",
+ "employee_id__employee_work_info__job_position_id__job_position",
+ "employee_id__employee_work_info__work_type_id__work_type",
+ "employee_id__employee_work_info__shift_id__employee_shift",
+ "employee_id__employee_work_info__employee_type_id__employee_type",
+ "employee_id__employee_work_info__experience",
+ )
+ )
choice_gender = {
"male": "Male",
@@ -102,12 +123,16 @@ if apps.is_installed("payroll"):
"draft": "Draft",
"review_ongoing": "Review Ongoing",
"confirmed": "Confirmed",
- "paid": "Paid"
+ "paid": "Paid",
}
# Fetch pay_head_data separately and map by payslip ID
payslip_ids = [item["id"] for item in data]
- pay_head_data_dict = dict(Payslip.objects.filter(id__in=payslip_ids).values_list("id", "pay_head_data"))
+ pay_head_data_dict = dict(
+ Payslip.objects.filter(id__in=payslip_ids).values_list(
+ "id", "pay_head_data"
+ )
+ )
data_list = []
for item in data:
@@ -116,59 +141,136 @@ if apps.is_installed("payroll"):
# Extract allowances and deductions
allowances = pay_head_data.get("allowances", [])
- deductions = (
- pay_head_data.get("pretax_deductions", []) + pay_head_data.get("post_tax_deductions", [])
- )
+ deductions = pay_head_data.get(
+ "pretax_deductions", []
+ ) + pay_head_data.get("post_tax_deductions", [])
# Prepare allowance and deduction lists with properly rounded amounts
- allowance_titles = ", ".join([allowance["title"] for allowance in allowances]) or "-"
- allowance_amounts = ", ".join(
- [str(round(float(allowance["amount"] or 0), 2)) for allowance in allowances]
- ) or "-"
+ allowance_titles = (
+ ", ".join([allowance["title"] for allowance in allowances]) or "-"
+ )
+ allowance_amounts = (
+ ", ".join(
+ [
+ str(round(float(allowance["amount"] or 0), 2))
+ for allowance in allowances
+ ]
+ )
+ or "-"
+ )
- deduction_titles = ", ".join([deduction["title"] for deduction in deductions]) or "-"
- deduction_amounts = ", ".join(
- [str(round(float(deduction["amount"] or 0), 2)) for deduction in deductions]
- ) or "-"
+ deduction_titles = (
+ ", ".join([deduction["title"] for deduction in deductions]) or "-"
+ )
+ deduction_amounts = (
+ ", ".join(
+ [
+ str(round(float(deduction["amount"] or 0), 2))
+ for deduction in deductions
+ ]
+ )
+ or "-"
+ )
# Calculate total allowance amount
total_allowance_amount = sum(
- [round(float(allowance["amount"] or 0), 2) for allowance in allowances]
+ [
+ round(float(allowance["amount"] or 0), 2)
+ for allowance in allowances
+ ]
)
# Calculate total deduction amount
total_deduction_amount = sum(
- [round(float(deduction["amount"] or 0), 2) for deduction in deductions]
+ [
+ round(float(deduction["amount"] or 0), 2)
+ for deduction in deductions
+ ]
)
# Main data structure
- data_list.append({
- "Employee": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}",
- "Gender": choice_gender.get(item["employee_id__gender"]),
- "Email": item["employee_id__email"],
- "Phone": item["employee_id__phone"],
- "Department": item["employee_id__employee_work_info__department_id__department"] if item["employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position": item["employee_id__employee_work_info__job_position_id__job_position"] if item["employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role": item["employee_id__employee_work_info__job_role_id__job_role"] if item["employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Work Type": item["employee_id__employee_work_info__work_type_id__work_type"] if item["employee_id__employee_work_info__work_type_id__work_type"] else "-",
- "Shift": item["employee_id__employee_work_info__shift_id__employee_shift"] if item["employee_id__employee_work_info__shift_id__employee_shift"] else "-",
- "Employee Type": item["employee_id__employee_work_info__employee_type_id__employee_type"] if item["employee_id__employee_work_info__employee_type_id__employee_type"] else "-",
- "Payslip Start Date": item["start_date"],
- "Payslip End Date": item["end_date"],
- "Batch Name": item['group_name'] if item['group_name'] else '-',
- "Contract Wage": round(float(item["contract_wage"] or 0), 2),
- "Basic Salary": round(float(item["basic_pay"] or 0), 2),
- "Gross Pay": round(float(item["gross_pay"] or 0), 2),
- "Net Pay": round(float(item["net_pay"] or 0), 2),
- "Allowance Title": allowance_titles,
- "Allowance Amount": allowance_amounts,
- "Total Allowance Amount": round(total_allowance_amount, 2),
- "Deduction Title": deduction_titles,
- "Deduction Amount": deduction_amounts,
- "Total Deduction Amount": round(total_deduction_amount, 2),
- "Status": STATUS.get(item["status"]),
- "Experience": round(float(item["employee_id__employee_work_info__experience"] or 0), 2),
- })
+ data_list.append(
+ {
+ "Employee": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}",
+ "Gender": choice_gender.get(item["employee_id__gender"]),
+ "Email": item["employee_id__email"],
+ "Phone": item["employee_id__phone"],
+ "Department": (
+ item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ if item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item[
+ "employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ else "-"
+ ),
+ "Work Type": (
+ item[
+ "employee_id__employee_work_info__work_type_id__work_type"
+ ]
+ if item[
+ "employee_id__employee_work_info__work_type_id__work_type"
+ ]
+ else "-"
+ ),
+ "Shift": (
+ item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ if item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ else "-"
+ ),
+ "Employee Type": (
+ item[
+ "employee_id__employee_work_info__employee_type_id__employee_type"
+ ]
+ if item[
+ "employee_id__employee_work_info__employee_type_id__employee_type"
+ ]
+ else "-"
+ ),
+ "Payslip Start Date": item["start_date"],
+ "Payslip End Date": item["end_date"],
+ "Batch Name": item["group_name"] if item["group_name"] else "-",
+ "Contract Wage": round(float(item["contract_wage"] or 0), 2),
+ "Basic Salary": round(float(item["basic_pay"] or 0), 2),
+ "Gross Pay": round(float(item["gross_pay"] or 0), 2),
+ "Net Pay": round(float(item["net_pay"] or 0), 2),
+ "Allowance Title": allowance_titles,
+ "Allowance Amount": allowance_amounts,
+ "Total Allowance Amount": round(total_allowance_amount, 2),
+ "Deduction Title": deduction_titles,
+ "Deduction Amount": deduction_amounts,
+ "Total Deduction Amount": round(total_deduction_amount, 2),
+ "Status": STATUS.get(item["status"]),
+ "Experience": round(
+ float(
+ item["employee_id__employee_work_info__experience"] or 0
+ ),
+ 2,
+ ),
+ }
+ )
elif model_type == "allowance":
@@ -177,13 +279,24 @@ if apps.is_installed("payroll"):
payslip_filter = PayslipFilter(request.GET, queryset=payslips)
filtered_qs = payslip_filter.qs # This uses all custom filters you defined
- data = list(filtered_qs.values(
- 'id', # Include payslip ID to fetch pay_head_data later
- 'employee_id__employee_first_name', 'employee_id__employee_last_name', 'employee_id__gender', 'employee_id__email',
- 'employee_id__phone', 'start_date', 'end_date','status', 'employee_id__employee_work_info__department_id__department',
- 'employee_id__employee_work_info__job_role_id__job_role','employee_id__employee_work_info__job_position_id__job_position',
- 'employee_id__employee_work_info__work_type_id__work_type','employee_id__employee_work_info__shift_id__employee_shift',
- ))
+ data = list(
+ filtered_qs.values(
+ "id", # Include payslip ID to fetch pay_head_data later
+ "employee_id__employee_first_name",
+ "employee_id__employee_last_name",
+ "employee_id__gender",
+ "employee_id__email",
+ "employee_id__phone",
+ "start_date",
+ "end_date",
+ "status",
+ "employee_id__employee_work_info__department_id__department",
+ "employee_id__employee_work_info__job_role_id__job_role",
+ "employee_id__employee_work_info__job_position_id__job_position",
+ "employee_id__employee_work_info__work_type_id__work_type",
+ "employee_id__employee_work_info__shift_id__employee_shift",
+ )
+ )
choice_gender = {
"male": "Male",
@@ -195,12 +308,16 @@ if apps.is_installed("payroll"):
"draft": "Draft",
"review_ongoing": "Review Ongoing",
"confirmed": "Confirmed",
- "paid": "Paid"
+ "paid": "Paid",
}
# Fetch pay_head_data separately and map by payslip ID
payslip_ids = [item["id"] for item in data]
- pay_head_data_dict = dict(Payslip.objects.filter(id__in=payslip_ids).values_list("id", "pay_head_data"))
+ pay_head_data_dict = dict(
+ Payslip.objects.filter(id__in=payslip_ids).values_list(
+ "id", "pay_head_data"
+ )
+ )
data_list = []
for item in data:
@@ -212,43 +329,88 @@ if apps.is_installed("payroll"):
# Add Allowances to combined data
for allowance in pay_head_data.get("allowances", []):
- all_pay_data.append({
- "Pay Type": "Allowance",
- "Title": allowance["title"],
- "Amount": round(float(allowance["amount"] or 0), 2),
- })
+ all_pay_data.append(
+ {
+ "Pay Type": "Allowance",
+ "Title": allowance["title"],
+ "Amount": round(float(allowance["amount"] or 0), 2),
+ }
+ )
# Add Deductions to combined data
- for deduction in (
- pay_head_data.get("pretax_deductions", []) + pay_head_data.get("post_tax_deductions", [])
- ):
- all_pay_data.append({
- "Pay Type": "Deduction",
- "Title": deduction["title"],
- "Amount": round(float(deduction["amount"] or 0), 2),
- })
+ for deduction in pay_head_data.get(
+ "pretax_deductions", []
+ ) + pay_head_data.get("post_tax_deductions", []):
+ all_pay_data.append(
+ {
+ "Pay Type": "Deduction",
+ "Title": deduction["title"],
+ "Amount": round(float(deduction["amount"] or 0), 2),
+ }
+ )
# Add combined data to main data list
for pay_item in all_pay_data:
- data_list.append({
- "Employee": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}",
- "Gender": choice_gender.get(item["employee_id__gender"]),
- "Email": item["employee_id__email"],
- "Phone": item["employee_id__phone"],
- "Department": item["employee_id__employee_work_info__department_id__department"] if item["employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position": item["employee_id__employee_work_info__job_position_id__job_position"] if item["employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role": item["employee_id__employee_work_info__job_role_id__job_role"] if item["employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Work Type": item["employee_id__employee_work_info__work_type_id__work_type"] if item["employee_id__employee_work_info__work_type_id__work_type"] else "-",
- "Shift": item["employee_id__employee_work_info__shift_id__employee_shift"] if item["employee_id__employee_work_info__shift_id__employee_shift"] else "-",
- "Payslip Start Date": item["start_date"],
- "Payslip End Date": item["end_date"],
- "Allowance & Deduction": pay_item["Pay Type"],
- "Allowance & Deduction Title": pay_item["Title"],
- "Allowance & Deduction Amount": pay_item["Amount"],
- "Status": STATUS.get(item["status"]),
- })
+ data_list.append(
+ {
+ "Employee": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}",
+ "Gender": choice_gender.get(item["employee_id__gender"]),
+ "Email": item["employee_id__email"],
+ "Phone": item["employee_id__phone"],
+ "Department": (
+ item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ if item[
+ "employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item[
+ "employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ if item[
+ "employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ else "-"
+ ),
+ "Work Type": (
+ item[
+ "employee_id__employee_work_info__work_type_id__work_type"
+ ]
+ if item[
+ "employee_id__employee_work_info__work_type_id__work_type"
+ ]
+ else "-"
+ ),
+ "Shift": (
+ item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ if item[
+ "employee_id__employee_work_info__shift_id__employee_shift"
+ ]
+ else "-"
+ ),
+ "Payslip Start Date": item["start_date"],
+ "Payslip End Date": item["end_date"],
+ "Allowance & Deduction": pay_item["Pay Type"],
+ "Allowance & Deduction Title": pay_item["Title"],
+ "Allowance & Deduction Amount": pay_item["Amount"],
+ "Status": STATUS.get(item["status"]),
+ }
+ )
else:
data_list = []
return JsonResponse(data_list, safe=False)
-
diff --git a/report/views/pms_report.py b/report/views/pms_report.py
index 29d7027ca..d5ab7147f 100644
--- a/report/views/pms_report.py
+++ b/report/views/pms_report.py
@@ -1,6 +1,6 @@
+from django.apps import apps
from django.http import JsonResponse
from django.shortcuts import render
-from django.apps import apps
if apps.is_installed("pms"):
@@ -10,22 +10,23 @@ if apps.is_installed("pms"):
from pms.models import EmployeeKeyResult, EmployeeObjective, Feedback, Objective
from pms.views import objective_filter_pagination
-
@login_required
@permission_required(perm="pms.view_objective")
def pms_report(request):
- company = 'all'
+ company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
+ if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
employee = request.user.employee_get
objective_own = EmployeeObjective.objects.filter(
- employee_id=employee, archive=False
+ employee_id=employee, archive=False
)
objective_own = objective_own.distinct()
- feedback = request.GET.get("search") # if the search is none the filter will works
+ feedback = request.GET.get(
+ "search"
+ ) # if the search is none the filter will works
if feedback is None:
feedback = ""
self_feedback = Feedback.objects.filter(employee_id=employee).filter(
@@ -37,17 +38,21 @@ if apps.is_installed("pms"):
)
context = objective_filter_pagination(request, objective_own)
- cm = {'company':company,"feedback_filter_form":feedback_filter_own.form,"emp_obj_form": EmployeeObjectiveFilter()}
+ cm = {
+ "company": company,
+ "feedback_filter_form": feedback_filter_own.form,
+ "emp_obj_form": EmployeeObjectiveFilter(),
+ }
context.update(cm)
- return render(request, "report/pms_report.html",context)
+ return render(request, "report/pms_report.html", context)
@login_required
@permission_required(perm="pms.view_objective")
def pms_pivot(request):
-
- model_type = request.GET.get('model', 'objective')
- if model_type == 'objective':
+
+ model_type = request.GET.get("model", "objective")
+ if model_type == "objective":
qs = Objective.objects.all()
if managers := request.GET.getlist("managers"):
@@ -59,41 +64,74 @@ if apps.is_installed("pms"):
if key_result_id := request.GET.get("employee_objective__key_result_id"):
qs = qs.filter(key_result_id=key_result_id)
- data = list(qs.values(
- "title","managers__employee_first_name","managers__employee_last_name",
- "assignees__employee_first_name","assignees__employee_last_name",
- "key_result_id__title","key_result_id__target_value","duration_unit","duration",
- "company_id__company","key_result_id__progress_type","key_result_id__duration",
- 'assignees__employee_work_info__department_id__department', 'assignees__employee_work_info__job_role_id__job_role',
- 'assignees__employee_work_info__job_position_id__job_position',
- ))
+ data = list(
+ qs.values(
+ "title",
+ "managers__employee_first_name",
+ "managers__employee_last_name",
+ "assignees__employee_first_name",
+ "assignees__employee_last_name",
+ "key_result_id__title",
+ "key_result_id__target_value",
+ "duration_unit",
+ "duration",
+ "company_id__company",
+ "key_result_id__progress_type",
+ "key_result_id__duration",
+ "assignees__employee_work_info__department_id__department",
+ "assignees__employee_work_info__job_role_id__job_role",
+ "assignees__employee_work_info__job_position_id__job_position",
+ )
+ )
DURATION_UNIT = {
- "days" :"Days",
- "months" :"Months",
- "years" :"Years",
+ "days": "Days",
+ "months": "Months",
+ "years": "Years",
}
KEY_RESULT_TARGET = {
- "%" :"%",
- "#" :"Number",
- "Currency" :"Currency",
+ "%": "%",
+ "#": "Number",
+ "Currency": "Currency",
}
data_list = [
{
- "Objective":item["title"],
- "Objective Duration":f'{item["duration"]} {DURATION_UNIT.get(item["duration_unit"])}',
- "Manager":f"{item['managers__employee_first_name']} {item['managers__employee_last_name']}" if item['managers__employee_first_name'] else "-",
- "Assignees":f"{item['assignees__employee_first_name']} {item['assignees__employee_last_name']}",
- "Assignee Department":item["assignees__employee_work_info__department_id__department"] if item["assignees__employee_work_info__department_id__department"] else "-",
- "Assignee Job Position":item["assignees__employee_work_info__job_position_id__job_position"] if item["assignees__employee_work_info__job_position_id__job_position"] else "-",
- "Assignee Job Role":item["assignees__employee_work_info__job_role_id__job_role"] if item["assignees__employee_work_info__job_role_id__job_role"] else"-",
- "Key Results":item["key_result_id__title"],
- "Key Result Duration":f'{item["key_result_id__duration"]} {"Days"}',
- "Key Result Target":f'{item["key_result_id__target_value"]} {KEY_RESULT_TARGET.get(item["key_result_id__progress_type"])}',
- "Company":item["company_id__company"]
-
- }for item in data
+ "Objective": item["title"],
+ "Objective Duration": f'{item["duration"]} {DURATION_UNIT.get(item["duration_unit"])}',
+ "Manager": (
+ f"{item['managers__employee_first_name']} {item['managers__employee_last_name']}"
+ if item["managers__employee_first_name"]
+ else "-"
+ ),
+ "Assignees": f"{item['assignees__employee_first_name']} {item['assignees__employee_last_name']}",
+ "Assignee Department": (
+ item["assignees__employee_work_info__department_id__department"]
+ if item[
+ "assignees__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Assignee Job Position": (
+ item[
+ "assignees__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "assignees__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Assignee Job Role": (
+ item["assignees__employee_work_info__job_role_id__job_role"]
+ if item["assignees__employee_work_info__job_role_id__job_role"]
+ else "-"
+ ),
+ "Key Results": item["key_result_id__title"],
+ "Key Result Duration": f'{item["key_result_id__duration"]} {"Days"}',
+ "Key Result Target": f'{item["key_result_id__target_value"]} {KEY_RESULT_TARGET.get(item["key_result_id__progress_type"])}',
+ "Company": item["company_id__company"],
+ }
+ for item in data
]
- elif model_type == 'feedback':
+ elif model_type == "feedback":
data_list = []
@@ -106,13 +144,13 @@ if apps.is_installed("pms"):
feedbacks = Feedback.objects.select_related(
"manager_id", "employee_id", "question_template_id"
).prefetch_related(
- "colleague_id", "subordinate_id",
+ "colleague_id",
+ "subordinate_id",
"question_template_id__question",
"feedback_answer__question_id", # related_name
"feedback_answer__employee_id",
)
-
# ✅ FILTERS added here
if review_cycle := request.GET.get("review_cycle"):
feedbacks = feedbacks.filter(review_cycle=review_cycle)
@@ -131,63 +169,102 @@ if apps.is_installed("pms"):
if end_date := request.GET.get("end_date"):
feedbacks = feedbacks.filter(created_at__date__lte=end_date)
-
for feedback in feedbacks:
- manager = f"{feedback.manager_id.employee_first_name} {feedback.manager_id.employee_last_name}" if feedback.manager_id else ""
- employee = f"{feedback.employee_id.employee_first_name} {feedback.employee_id.employee_last_name}" if feedback.employee_id else ""
+ manager = (
+ f"{feedback.manager_id.employee_first_name} {feedback.manager_id.employee_last_name}"
+ if feedback.manager_id
+ else ""
+ )
+ employee = (
+ f"{feedback.employee_id.employee_first_name} {feedback.employee_id.employee_last_name}"
+ if feedback.employee_id
+ else ""
+ )
- answerable_employees = list(feedback.colleague_id.all()) + list(feedback.subordinate_id.all())
- answerable_names = ', '.join(
- f"{e.employee_first_name} {e.employee_last_name}" for e in answerable_employees
- ) or "-"
+ answerable_employees = list(feedback.colleague_id.all()) + list(
+ feedback.subordinate_id.all()
+ )
+ answerable_names = (
+ ", ".join(
+ f"{e.employee_first_name} {e.employee_last_name}"
+ for e in answerable_employees
+ )
+ or "-"
+ )
questions = feedback.question_template_id.question.all()
# Fetch ALL answers for this feedback and map them grouped by question
- answers = feedback.feedback_answer.select_related("employee_id", "question_id")
+ answers = feedback.feedback_answer.select_related(
+ "employee_id", "question_id"
+ )
for question in questions:
- question_answers = [ans for ans in answers if ans.question_id_id == question.id]
+ question_answers = [
+ ans for ans in answers if ans.question_id_id == question.id
+ ]
# If no one answered this question, still show the question
if not question_answers:
- data_list.append({
- "Title":feedback.review_cycle,
- "Manager": manager,
- "Employee": employee,
- "Answerable Employees": answerable_names,
- "Questions": question.question,
- "Answer": "",
- "Answered Employees": "-",
- "Status": feedback.status,
- "Start Date": feedback.start_date,
- "End Date": feedback.end_date,
- "Is Cyclic": "Yes" if feedback.cyclic_feedback else "No",
- "Cycle Period": f"{feedback.cyclic_feedback_days_count} {PERIOD.get(feedback.cyclic_feedback_period)}" if feedback.cyclic_feedback_days_count else "-"
- })
- else:
- for answer in question_answers:
- answer_value = answer.answer.get("answer") if answer.answer else ""
- answered_by = f"{answer.employee_id.employee_first_name} {answer.employee_id.employee_last_name}" if answer.employee_id else "-"
- data_list.append({
- "Title":feedback.review_cycle,
+ data_list.append(
+ {
+ "Title": feedback.review_cycle,
"Manager": manager,
"Employee": employee,
"Answerable Employees": answerable_names,
"Questions": question.question,
- "Answer": answer_value,
- "Answered Employees": answered_by,
+ "Answer": "",
+ "Answered Employees": "-",
"Status": feedback.status,
"Start Date": feedback.start_date,
"End Date": feedback.end_date,
- "Is Cyclic": "Yes" if feedback.cyclic_feedback else "No",
- "Cycle Period": f"{feedback.cyclic_feedback_days_count} {PERIOD.get(feedback.cyclic_feedback_period)}" if feedback.cyclic_feedback_days_count else "-"
- })
- elif model_type == 'employeeobjective':
+ "Is Cyclic": (
+ "Yes" if feedback.cyclic_feedback else "No"
+ ),
+ "Cycle Period": (
+ f"{feedback.cyclic_feedback_days_count} {PERIOD.get(feedback.cyclic_feedback_period)}"
+ if feedback.cyclic_feedback_days_count
+ else "-"
+ ),
+ }
+ )
+ else:
+ for answer in question_answers:
+ answer_value = (
+ answer.answer.get("answer") if answer.answer else ""
+ )
+ answered_by = (
+ f"{answer.employee_id.employee_first_name} {answer.employee_id.employee_last_name}"
+ if answer.employee_id
+ else "-"
+ )
+ data_list.append(
+ {
+ "Title": feedback.review_cycle,
+ "Manager": manager,
+ "Employee": employee,
+ "Answerable Employees": answerable_names,
+ "Questions": question.question,
+ "Answer": answer_value,
+ "Answered Employees": answered_by,
+ "Status": feedback.status,
+ "Start Date": feedback.start_date,
+ "End Date": feedback.end_date,
+ "Is Cyclic": (
+ "Yes" if feedback.cyclic_feedback else "No"
+ ),
+ "Cycle Period": (
+ f"{feedback.cyclic_feedback_days_count} {PERIOD.get(feedback.cyclic_feedback_period)}"
+ if feedback.cyclic_feedback_days_count
+ else "-"
+ ),
+ }
+ )
+ elif model_type == "employeeobjective":
from django.utils.dateparse import parse_date
- qs=EmployeeKeyResult.objects.all()
+ qs = EmployeeKeyResult.objects.all()
# Filter section
if assignees := request.GET.getlist("employee_id"):
@@ -211,44 +288,87 @@ if apps.is_installed("pms"):
if end_date_to:
qs = qs.filter(end_date__lte=end_date_to)
- data = list(qs.values(
- "key_result","employee_objective_id__employee_id__employee_first_name","employee_objective_id__employee_id__employee_last_name",
- "employee_objective_id__objective_id__title","employee_objective_id__objective_id__duration_unit","employee_objective_id__objective_id__duration",
- "start_value","current_value","target_value","start_date","end_date","status","progress_type",'employee_objective_id__employee_id__employee_work_info__department_id__department',
- 'employee_objective_id__employee_id__employee_work_info__job_role_id__job_role','employee_objective_id__employee_id__employee_work_info__job_position_id__job_position',
-
- ))
+ data = list(
+ qs.values(
+ "key_result",
+ "employee_objective_id__employee_id__employee_first_name",
+ "employee_objective_id__employee_id__employee_last_name",
+ "employee_objective_id__objective_id__title",
+ "employee_objective_id__objective_id__duration_unit",
+ "employee_objective_id__objective_id__duration",
+ "start_value",
+ "current_value",
+ "target_value",
+ "start_date",
+ "end_date",
+ "status",
+ "progress_type",
+ "employee_objective_id__employee_id__employee_work_info__department_id__department",
+ "employee_objective_id__employee_id__employee_work_info__job_role_id__job_role",
+ "employee_objective_id__employee_id__employee_work_info__job_position_id__job_position",
+ )
+ )
DURATION_UNIT = {
- "days" :"Days",
- "months" :"Months",
- "years" :"Years",
+ "days": "Days",
+ "months": "Months",
+ "years": "Years",
}
KEY_RESULT_TARGET = {
- "%" :"%",
- "#" :"Number",
- "Currency" :"Currency",
+ "%": "%",
+ "#": "Number",
+ "Currency": "Currency",
}
data_list = [
{
"Employee": f"{item['employee_objective_id__employee_id__employee_first_name']} {item['employee_objective_id__employee_id__employee_last_name']}",
- "Department":item["employee_objective_id__employee_id__employee_work_info__department_id__department"] if item["employee_objective_id__employee_id__employee_work_info__department_id__department"] else "-",
- "Job Position":item["employee_objective_id__employee_id__employee_work_info__job_position_id__job_position"] if item["employee_objective_id__employee_id__employee_work_info__job_position_id__job_position"] else "-",
- "Job Role":item["employee_objective_id__employee_id__employee_work_info__job_role_id__job_role"] if item["employee_objective_id__employee_id__employee_work_info__job_role_id__job_role"] else "-",
- "Employee Keyresult":item["key_result"],
- "Objective":item["employee_objective_id__objective_id__title"],
- "Objective Duration":f'{item["employee_objective_id__objective_id__duration"]} {DURATION_UNIT.get(item["employee_objective_id__objective_id__duration_unit"])}',
- "Keyresult Start Value":f'{item["start_value"]} {KEY_RESULT_TARGET.get(item["progress_type"])}',
- "Keyresult Target Value":f'{item["target_value"]} {KEY_RESULT_TARGET.get(item["progress_type"])}',
- "Keyresult Current Value":f'{item["current_value"]} {KEY_RESULT_TARGET.get(item["progress_type"])}' if item["current_value"] else "-",
- "Keyresult Start Date":item["start_date"] if item["start_date"] else "-",
- "Keyresult End Date":item["end_date"] if item["end_date"] else "-",
- "status":item["status"],
-
- }for item in data
+ "Department": (
+ item[
+ "employee_objective_id__employee_id__employee_work_info__department_id__department"
+ ]
+ if item[
+ "employee_objective_id__employee_id__employee_work_info__department_id__department"
+ ]
+ else "-"
+ ),
+ "Job Position": (
+ item[
+ "employee_objective_id__employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ if item[
+ "employee_objective_id__employee_id__employee_work_info__job_position_id__job_position"
+ ]
+ else "-"
+ ),
+ "Job Role": (
+ item[
+ "employee_objective_id__employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ if item[
+ "employee_objective_id__employee_id__employee_work_info__job_role_id__job_role"
+ ]
+ else "-"
+ ),
+ "Employee Keyresult": item["key_result"],
+ "Objective": item["employee_objective_id__objective_id__title"],
+ "Objective Duration": f'{item["employee_objective_id__objective_id__duration"]} {DURATION_UNIT.get(item["employee_objective_id__objective_id__duration_unit"])}',
+ "Keyresult Start Value": f'{item["start_value"]} {KEY_RESULT_TARGET.get(item["progress_type"])}',
+ "Keyresult Target Value": f'{item["target_value"]} {KEY_RESULT_TARGET.get(item["progress_type"])}',
+ "Keyresult Current Value": (
+ f'{item["current_value"]} {KEY_RESULT_TARGET.get(item["progress_type"])}'
+ if item["current_value"]
+ else "-"
+ ),
+ "Keyresult Start Date": (
+ item["start_date"] if item["start_date"] else "-"
+ ),
+ "Keyresult End Date": item["end_date"] if item["end_date"] else "-",
+ "status": item["status"],
+ }
+ for item in data
]
else:
- data_list =[]
+ data_list = []
- return JsonResponse(data_list, safe = False)
+ return JsonResponse(data_list, safe=False)
diff --git a/report/views/recruitment_report.py b/report/views/recruitment_report.py
index b9d557845..1dc90129b 100644
--- a/report/views/recruitment_report.py
+++ b/report/views/recruitment_report.py
@@ -1,6 +1,6 @@
+from django.apps import apps
from django.http import JsonResponse
from django.shortcuts import render
-from django.apps import apps
if apps.is_installed("recruitment"):
@@ -14,15 +14,24 @@ if apps.is_installed("recruitment"):
@login_required
@permission_required(perm="recruitment.view_recruitment")
def recruitment_report(request):
- company = 'all'
+ company = "all"
selected_company = request.session.get("selected_company")
- if selected_company != 'all':
+ if selected_company != "all":
company = Company.objects.filter(id=selected_company).first()
- return render(request, "report/recruitment_report.html",{'company':company, "f":CandidateFilter(), "fr":RecruitmentFilter(),"fo":OnboardingStageFilter()})
+ return render(
+ request,
+ "report/recruitment_report.html",
+ {
+ "company": company,
+ "f": CandidateFilter(),
+ "fr": RecruitmentFilter(),
+ "fo": OnboardingStageFilter(),
+ },
+ )
@login_required
@permission_required(perm="recruitment.view_recruitment")
- def recruitment_pivot(request):
+ def recruitment_pivot(request):
model_type = request.GET.get("model", "candidate") # Default to Candidate
if model_type == "candidate":
@@ -30,97 +39,157 @@ if apps.is_installed("recruitment"):
filter_obj = CandidateFilter(request.GET, queryset=qs)
qs = filter_obj.qs
- data = list(qs.values(
- "name","recruitment_id__title","job_position_id__job_position","stage_id__stage","email","mobile",
- "gender","offer_letter_status","recruitment_id__closed","recruitment_id__vacancy","country",
- "recruitment_id__company_id__company","address","dob","state","city","source","job_position_id__department_id__department"
- ))
+ data = list(
+ qs.values(
+ "name",
+ "recruitment_id__title",
+ "job_position_id__job_position",
+ "stage_id__stage",
+ "email",
+ "mobile",
+ "gender",
+ "offer_letter_status",
+ "recruitment_id__closed",
+ "recruitment_id__vacancy",
+ "country",
+ "recruitment_id__company_id__company",
+ "address",
+ "dob",
+ "state",
+ "city",
+ "source",
+ "job_position_id__department_id__department",
+ )
+ )
choice_gender = {
"male": "Male",
"female": "Female",
"other": "Other",
}
OFFER_LETTER_STATUS = {
- "not_sent" : "Not Sent",
- "sent" : "Sent",
- "accepted" : "Accepted",
- "rejected" : "Rejected",
- "joined" : "Joined",
+ "not_sent": "Not Sent",
+ "sent": "Sent",
+ "accepted": "Accepted",
+ "rejected": "Rejected",
+ "joined": "Joined",
}
SOURCE_CHOICE = {
- "application":"Application Form",
- "software":"Inside Software",
- "other":"Other",
+ "application": "Application Form",
+ "software": "Inside Software",
+ "other": "Other",
}
-
data_list = [
{
- "Candidate":item["name"],
- "Email":item["email"],
- "Phone":item["mobile"],
- "Gender":choice_gender.get(item["gender"]),
- "Address":item["address"],
- "Date Of Birth":item["dob"],
- "Country":item["country"] if item["country"] else "-",
- "State":item["state"] if item["state"] else "-",
- "City":item["city"] if item["city"] else "-",
- "Source":SOURCE_CHOICE.get(item["source"]) if item["source"] else "-",
- "Job Position":item["job_position_id__job_position"],
- "Department":item["job_position_id__department_id__department"],
- "Offer Letter":OFFER_LETTER_STATUS.get(item["offer_letter_status"]),
- "Recruitment":item["recruitment_id__title"],
- "Current Stage":item["stage_id__stage"],
- "Recruitment Status": 'Closed' if item["recruitment_id__closed"] else 'Open',
- "Vacancy" : item["recruitment_id__vacancy"],
- "Company" : item["recruitment_id__company_id__company"],
-
-
- }for item in data
+ "Candidate": item["name"],
+ "Email": item["email"],
+ "Phone": item["mobile"],
+ "Gender": choice_gender.get(item["gender"]),
+ "Address": item["address"],
+ "Date Of Birth": item["dob"],
+ "Country": item["country"] if item["country"] else "-",
+ "State": item["state"] if item["state"] else "-",
+ "City": item["city"] if item["city"] else "-",
+ "Source": (
+ SOURCE_CHOICE.get(item["source"]) if item["source"] else "-"
+ ),
+ "Job Position": item["job_position_id__job_position"],
+ "Department": item["job_position_id__department_id__department"],
+ "Offer Letter": OFFER_LETTER_STATUS.get(
+ item["offer_letter_status"]
+ ),
+ "Recruitment": item["recruitment_id__title"],
+ "Current Stage": item["stage_id__stage"],
+ "Recruitment Status": (
+ "Closed" if item["recruitment_id__closed"] else "Open"
+ ),
+ "Vacancy": item["recruitment_id__vacancy"],
+ "Company": item["recruitment_id__company_id__company"],
+ }
+ for item in data
]
elif model_type == "recruitment":
qs = Recruitment.objects.all()
- filter_obj = RecruitmentFilter(request.GET, queryset = qs)
+ filter_obj = RecruitmentFilter(request.GET, queryset=qs)
qs = filter_obj.qs
- data = list(qs.values(
- "title","vacancy","closed","open_positions__job_position","start_date","end_date","is_published",
- "recruitment_managers__employee_first_name","recruitment_managers__employee_last_name","company_id__company",
- ))
+ data = list(
+ qs.values(
+ "title",
+ "vacancy",
+ "closed",
+ "open_positions__job_position",
+ "start_date",
+ "end_date",
+ "is_published",
+ "recruitment_managers__employee_first_name",
+ "recruitment_managers__employee_last_name",
+ "company_id__company",
+ )
+ )
data_list = [
{
- "Recruitment" : item["title"],
+ "Recruitment": item["title"],
"Manager": f"{item['recruitment_managers__employee_first_name']} {item['recruitment_managers__employee_last_name']}",
- "Is Closed": 'Closed' if item["closed"] else 'Open',
- "Status": 'Published' if item["is_published"] else 'Not Published',
- "Start Date":item["start_date"],
- "End Date":item["end_date"],
- "Job Position":item["open_positions__job_position"],
- "Vacancy":item["vacancy"],
- "Company" : item["company_id__company"],
- }for item in data
+ "Is Closed": "Closed" if item["closed"] else "Open",
+ "Status": "Published" if item["is_published"] else "Not Published",
+ "Start Date": item["start_date"],
+ "End Date": item["end_date"],
+ "Job Position": item["open_positions__job_position"],
+ "Vacancy": item["vacancy"],
+ "Company": item["company_id__company"],
+ }
+ for item in data
]
elif model_type == "onboarding":
qs = OnboardingStage.objects.all()
- filter_obj = OnboardingStageFilter(request.GET, queryset = qs)
+ filter_obj = OnboardingStageFilter(request.GET, queryset=qs)
qs = filter_obj.qs
- data = list(qs.values(
- "stage_title","recruitment_id__title","employee_id__employee_first_name","employee_id__employee_last_name",
- "onboarding_task__task_title",
- "onboarding_task__employee_id__employee_first_name","onboarding_task__employee_id__employee_last_name",
- "onboarding_task__candidates__name","recruitment_id__company_id__company",
- ))
+ data = list(
+ qs.values(
+ "stage_title",
+ "recruitment_id__title",
+ "employee_id__employee_first_name",
+ "employee_id__employee_last_name",
+ "onboarding_task__task_title",
+ "onboarding_task__employee_id__employee_first_name",
+ "onboarding_task__employee_id__employee_last_name",
+ "onboarding_task__candidates__name",
+ "recruitment_id__company_id__company",
+ )
+ )
data_list = [
{
"Recruitment": item["recruitment_id__title"],
"Stage": item["stage_title"],
- "Stage Manager": f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}" if item['employee_id__employee_first_name'] else "-",
- "Task": item["onboarding_task__task_title"] if item["onboarding_task__task_title"] else "-",
- "Task Manager": f"{item['onboarding_task__employee_id__employee_first_name']} {item['onboarding_task__employee_id__employee_last_name']}" if item['onboarding_task__employee_id__employee_first_name'] else "-",
- "Candidates": item["onboarding_task__candidates__name"] if item["onboarding_task__candidates__name"] else "-",
- "Company" : item["recruitment_id__company_id__company"] if item["recruitment_id__company_id__company"] else "-",
- }for item in data
+ "Stage Manager": (
+ f"{item['employee_id__employee_first_name']} {item['employee_id__employee_last_name']}"
+ if item["employee_id__employee_first_name"]
+ else "-"
+ ),
+ "Task": (
+ item["onboarding_task__task_title"]
+ if item["onboarding_task__task_title"]
+ else "-"
+ ),
+ "Task Manager": (
+ f"{item['onboarding_task__employee_id__employee_first_name']} {item['onboarding_task__employee_id__employee_last_name']}"
+ if item["onboarding_task__employee_id__employee_first_name"]
+ else "-"
+ ),
+ "Candidates": (
+ item["onboarding_task__candidates__name"]
+ if item["onboarding_task__candidates__name"]
+ else "-"
+ ),
+ "Company": (
+ item["recruitment_id__company_id__company"]
+ if item["recruitment_id__company_id__company"]
+ else "-"
+ ),
+ }
+ for item in data
]
else:
data_list = []
diff --git a/static/build/css/pivottable.min.css b/static/build/css/pivottable.min.css
index 5237d272c..95ce89a18 100644
--- a/static/build/css/pivottable.min.css
+++ b/static/build/css/pivottable.min.css
@@ -1,2 +1 @@
.pvtUi{color:#333}table.pvtTable{font-size:8pt;text-align:left;border-collapse:collapse}table.pvtTable tbody tr th,table.pvtTable thead tr th{background-color:#e6EEEE;border:1px solid #CDCDCD;font-size:8pt;padding:5px}table.pvtTable .pvtColLabel{text-align:center}table.pvtTable .pvtTotalLabel{text-align:right}table.pvtTable tbody tr td{color:#3D3D3D;padding:5px;background-color:#FFF;border:1px solid #CDCDCD;vertical-align:top;text-align:right}.pvtGrandTotal,.pvtTotal{font-weight:700}.pvtVals{text-align:center;white-space:nowrap}.pvtColOrder,.pvtRowOrder{cursor:pointer;width:15px;margin-left:5px;display:inline-block}.pvtAggregator{margin-bottom:5px}.pvtAxisContainer,.pvtVals{border:1px solid gray;background:#EEE;padding:5px;min-width:20px;min-height:20px;user-select:none;-webkit-user-select:none;-moz-user-select:none;-khtml-user-select:none;-ms-user-select:none}.pvtAxisContainer li{padding:8px 6px;list-style-type:none;cursor:move}.pvtAxisContainer li.pvtPlaceholder{-webkit-border-radius:5px;padding:3px 15px;-moz-border-radius:5px;border-radius:5px;border:1px dashed #aaa}.pvtAxisContainer li span.pvtAttr{-webkit-text-size-adjust:100%;background:#F3F3F3;border:1px solid #DEDEDE;padding:2px 5px;white-space:nowrap;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.pvtTriangle{cursor:pointer;color:grey}.pvtHorizList li{display:inline}.pvtVertList{vertical-align:top}.pvtFilteredAttribute{font-style:italic}.pvtFilterBox{z-index:100;width:300px;border:1px solid gray;background-color:#fff;position:absolute;text-align:center}.pvtFilterBox h4{margin:15px}.pvtFilterBox p{margin:10px auto}.pvtFilterBox label{font-weight:400}.pvtFilterBox input[type=checkbox]{margin-right:10px;margin-left:10px}.pvtFilterBox input[type=text]{width:230px}.pvtFilterBox .count{color:gray;font-weight:400;margin-left:3px}.pvtCheckContainer{text-align:left;font-size:14px;white-space:nowrap;overflow-y:scroll;width:100%;max-height:250px;border-top:1px solid #d3d3d3;border-bottom:1px solid #d3d3d3}.pvtCheckContainer p{margin:5px}.pvtRendererArea{padding:5px}
-
diff --git a/static/build/js/pivottable.min.js b/static/build/js/pivottable.min.js
index 0537a21e6..b79a65a08 100644
--- a/static/build/js/pivottable.min.js
+++ b/static/build/js/pivottable.min.js
@@ -1,2 +1,2 @@
(function(){var t,e=[].indexOf||function(t){for(var e=0,n=this.length;e
1?n+a[1]:"",r=/(\d+)(\d{3})/;r.test(o);)o=o.replace(r,"$1"+e+"$2");return o+i},m=function(e){var n;return n={digitsAfterDecimal:2,scaler:1,thousandsSep:",",decimalSep:".",prefix:"",suffix:""},e=t.extend({},n,e),function(t){var n;return isNaN(t)||!isFinite(t)?"":(n=i((e.scaler*t).toFixed(e.digitsAfterDecimal),e.thousandsSep,e.decimalSep),""+e.prefix+n+e.suffix)}},A=m(),x=m({digitsAfterDecimal:0}),S=m({digitsAfterDecimal:1,scaler:100,suffix:"%"}),l={count:function(t){return null==t&&(t=x),function(){return function(e,n,r){return{count:0,push:function(){return this.count++},value:function(){return this.count},format:t}}}},uniques:function(t,n){return null==n&&(n=x),function(r){var a;return a=r[0],function(r,o,i){return{uniq:[],push:function(t){var n;if(n=t[a],e.call(this.uniq,n)<0)return this.uniq.push(t[a])},value:function(){return t(this.uniq)},format:n,numInputs:null!=a?0:1}}}},sum:function(t){return null==t&&(t=A),function(e){var n;return n=e[0],function(e,r,a){return{sum:0,push:function(t){if(!isNaN(parseFloat(t[n])))return this.sum+=parseFloat(t[n])},value:function(){return this.sum},format:t,numInputs:null!=n?0:1}}}},extremes:function(t,e){return null==e&&(e=A),function(n){var r;return r=n[0],function(n,a,o){return{val:null,sorter:h(null!=n?n.sorters:void 0,r),push:function(e){var n,a,o,i;if(i=e[r],"min"!==t&&"max"!==t||(i=parseFloat(i),isNaN(i)||(this.val=Math[t](i,null!=(n=this.val)?n:i))),"first"===t&&this.sorter(i,null!=(a=this.val)?a:i)<=0&&(this.val=i),"last"===t&&this.sorter(i,null!=(o=this.val)?o:i)>=0)return this.val=i},value:function(){return this.val},format:function(t){return isNaN(t)?t:e(t)},numInputs:null!=r?0:1}}}},quantile:function(t,e){return null==e&&(e=A),function(n){var r;return r=n[0],function(n,a,o){return{vals:[],push:function(t){var e;if(e=parseFloat(t[r]),!isNaN(e))return this.vals.push(e)},value:function(){var e;return 0===this.vals.length?null:(this.vals.sort(function(t,e){return t-e}),e=(this.vals.length-1)*t,(this.vals[Math.floor(e)]+this.vals[Math.ceil(e)])/2)},format:e,numInputs:null!=r?0:1}}}},runningStat:function(t,e,n){return null==t&&(t="mean"),null==e&&(e=1),null==n&&(n=A),function(r){var a;return a=r[0],function(r,o,i){return{n:0,m:0,s:0,push:function(t){var e,n;if(n=parseFloat(t[a]),!isNaN(n))return this.n+=1,1===this.n?this.m=n:(e=this.m+(n-this.m)/this.n,this.s=this.s+(n-this.m)*(n-e),this.m=e)},value:function(){if("mean"===t)return 0===this.n?NaN:this.m;if(this.n<=e)return 0;switch(t){case"var":return this.s/(this.n-e);case"stdev":return Math.sqrt(this.s/(this.n-e))}},format:n,numInputs:null!=a?0:1}}}},sumOverSum:function(t){return null==t&&(t=A),function(e){var n,r;return r=e[0],n=e[1],function(e,a,o){return{sumNum:0,sumDenom:0,push:function(t){if(isNaN(parseFloat(t[r]))||(this.sumNum+=parseFloat(t[r])),!isNaN(parseFloat(t[n])))return this.sumDenom+=parseFloat(t[n])},value:function(){return this.sumNum/this.sumDenom},format:t,numInputs:null!=r&&null!=n?0:2}}}},sumOverSumBound80:function(t,e){return null==t&&(t=!0),null==e&&(e=A),function(n){var r,a;return a=n[0],r=n[1],function(n,o,i){return{sumNum:0,sumDenom:0,push:function(t){if(isNaN(parseFloat(t[a]))||(this.sumNum+=parseFloat(t[a])),!isNaN(parseFloat(t[r])))return this.sumDenom+=parseFloat(t[r])},value:function(){var e;return e=t?1:-1,(.821187207574908/this.sumDenom+this.sumNum/this.sumDenom+1.2815515655446004*e*Math.sqrt(.410593603787454/(this.sumDenom*this.sumDenom)+this.sumNum*(1-this.sumNum/this.sumDenom)/(this.sumDenom*this.sumDenom)))/(1+1.642374415149816/this.sumDenom)},format:e,numInputs:null!=a&&null!=r?0:2}}}},fractionOf:function(t,e,r){return null==e&&(e="total"),null==r&&(r=S),function(){var a;return a=1<=arguments.length?n.call(arguments,0):[],function(n,o,i){return{selector:{total:[[],[]],row:[o,[]],col:[[],i]}[e],inner:t.apply(null,a)(n,o,i),push:function(t){return this.inner.push(t)},format:r,value:function(){return this.inner.value()/n.getAggregator.apply(n,this.selector).inner.value()},numInputs:t.apply(null,a)().numInputs}}}}},l.countUnique=function(t){return l.uniques(function(t){return t.length},t)},l.listUnique=function(t){return l.uniques(function(e){return e.sort(f).join(t)},function(t){return t})},l.max=function(t){return l.extremes("max",t)},l.min=function(t){return l.extremes("min",t)},l.first=function(t){return l.extremes("first",t)},l.last=function(t){return l.extremes("last",t)},l.median=function(t){return l.quantile(.5,t)},l.average=function(t){return l.runningStat("mean",1,t)},l["var"]=function(t,e){return l.runningStat("var",t,e)},l.stdev=function(t,e){return l.runningStat("stdev",t,e)},s=function(t){return{Count:t.count(x),"Count Unique Values":t.countUnique(x),"List Unique Values":t.listUnique(", "),Sum:t.sum(A),"Integer Sum":t.sum(x),Average:t.average(A),Median:t.median(A),"Sample Variance":t["var"](1,A),"Sample Standard Deviation":t.stdev(1,A),Minimum:t.min(A),Maximum:t.max(A),First:t.first(A),Last:t.last(A),"Sum over Sum":t.sumOverSum(A),"80% Upper Bound":t.sumOverSumBound80(!0,A),"80% Lower Bound":t.sumOverSumBound80(!1,A),"Sum as Fraction of Total":t.fractionOf(t.sum(),"total",S),"Sum as Fraction of Rows":t.fractionOf(t.sum(),"row",S),"Sum as Fraction of Columns":t.fractionOf(t.sum(),"col",S),"Count as Fraction of Total":t.fractionOf(t.count(),"total",S),"Count as Fraction of Rows":t.fractionOf(t.count(),"row",S),"Count as Fraction of Columns":t.fractionOf(t.count(),"col",S)}}(l),b={Table:function(t,e){return g(t,e)},"Table Barchart":function(e,n){return t(g(e,n)).barchart()},Heatmap:function(e,n){return t(g(e,n)).heatmap("heatmap",n)},"Row Heatmap":function(e,n){return t(g(e,n)).heatmap("rowheatmap",n)},"Col Heatmap":function(e,n){return t(g(e,n)).heatmap("colheatmap",n)}},d={en:{aggregators:s,renderers:b,localeStrings:{renderError:"An error occurred rendering the PivotTable results.",computeError:"An error occurred computing the PivotTable results.",uiRenderError:"An error occurred rendering the PivotTable UI.",selectAll:"Select All",selectNone:"Select None",tooMany:"(too many to list)",filterResults:"Filter values",apply:"Apply",cancel:"Cancel",totals:"Totals",vs:"vs",by:"by"}}},p=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],u=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],N=function(t){return("0"+t).substr(-2,2)},c={bin:function(t,e){return function(n){return n[t]-n[t]%e}},dateFormat:function(t,e,n,r,a){var o;return null==n&&(n=!1),null==r&&(r=p),null==a&&(a=u),o=n?"UTC":"",function(n){var i;return i=new Date(Date.parse(n[t])),isNaN(i)?"":e.replace(/%(.)/g,function(t,e){switch(e){case"y":return i["get"+o+"FullYear"]();case"m":return N(i["get"+o+"Month"]()+1);case"n":return r[i["get"+o+"Month"]()];case"d":return N(i["get"+o+"Date"]());case"w":return a[i["get"+o+"Day"]()];case"x":return i["get"+o+"Day"]();case"H":return N(i["get"+o+"Hours"]());case"M":return N(i["get"+o+"Minutes"]());case"S":return N(i["get"+o+"Seconds"]());default:return"%"+e}})}}},C=/(\d+)|(\D+)/g,v=/\d/,y=/^0/,f=function(t){return function(t,e){var n,r,a,o,i,l;if(null!=e&&null==t)return-1;if(null!=t&&null==e)return 1;if("number"==typeof t&&isNaN(t))return-1;if("number"==typeof e&&isNaN(e))return 1;if(i=+t,l=+e,il)return 1;if("number"==typeof t&&"number"!=typeof e)return-1;if("number"==typeof e&&"number"!=typeof t)return 1;if("number"==typeof t&&"number"==typeof e)return 0;if(isNaN(l)&&!isNaN(i))return-1;if(isNaN(i)&&!isNaN(l))return 1;if(n=String(t),a=String(e),n===a)return 0;if(!v.test(n)||!v.test(a))return n>a?1:-1;for(n=n.match(C),a=a.match(C);n.length&&a.length;)if(r=n.shift(),o=a.shift(),r!==o)return v.test(r)&&v.test(o)?r.replace(y,".0")-o.replace(y,".0"):r>o?1:-1;return n.length-a.length}}(this),w=function(t){var e,n,r,a;r={},n={};for(e in t)a=t[e],r[a]=e,"string"==typeof a&&(n[a.toLowerCase()]=e);return function(t,e){return null!=r[t]&&null!=r[e]?r[t]-r[e]:null!=r[t]?-1:null!=r[e]?1:null!=n[t]&&null!=n[e]?n[t]-n[e]:null!=n[t]?-1:null!=n[e]?1:f(t,e)}},h=function(e,n){var r;if(null!=e)if(t.isFunction(e)){if(r=e(n),t.isFunction(r))return r}else if(null!=e[n])return e[n];return f},o=function(){function e(t,n){var a,o,i,s,u,c,h,d,p,f;null==n&&(n={}),this.getAggregator=r(this.getAggregator,this),this.getRowKeys=r(this.getRowKeys,this),this.getColKeys=r(this.getColKeys,this),this.sortKeys=r(this.sortKeys,this),this.arrSort=r(this.arrSort,this),this.input=t,this.aggregator=null!=(a=n.aggregator)?a:l.count()(),this.aggregatorName=null!=(o=n.aggregatorName)?o:"Count",this.colAttrs=null!=(i=n.cols)?i:[],this.rowAttrs=null!=(s=n.rows)?s:[],this.valAttrs=null!=(u=n.vals)?u:[],this.sorters=null!=(c=n.sorters)?c:{},this.rowOrder=null!=(h=n.rowOrder)?h:"key_a_to_z",this.colOrder=null!=(d=n.colOrder)?d:"key_a_to_z",this.derivedAttributes=null!=(p=n.derivedAttributes)?p:{},this.filter=null!=(f=n.filter)?f:function(){return!0},this.tree={},this.rowKeys=[],this.colKeys=[],this.rowTotals={},this.colTotals={},this.allTotal=this.aggregator(this,[],[]),this.sorted=!1,e.forEachRecord(this.input,this.derivedAttributes,function(t){return function(e){if(t.filter(e))return t.processRecord(e)}}(this))}return e.forEachRecord=function(e,n,r){var o,i,l,s,u,c,h,d,p,f,m,g;if(o=t.isEmptyObject(n)?r:function(t){var e,a,o;for(e in n)o=n[e],t[e]=null!=(a=o(t))?a:t[e];return r(t)},t.isFunction(e))return e(o);if(t.isArray(e)){if(t.isArray(e[0])){f=[];for(l in e)if(a.call(e,l)&&(i=e[l],l>0)){d={},p=e[0];for(s in p)a.call(p,s)&&(u=p[s],d[u]=i[s]);f.push(o(d))}return f}for(m=[],c=0,h=e.length;c tr > th",e).each(function(e){return g.push(t(this).text())}),t("tbody > tr",e).each(function(e){return d={},t("td",this).each(function(e){return d[g[e]]=t(this).text()}),o(d)});throw new Error("unknown input format")},e.prototype.forEachMatchingRecord=function(t,n){return e.forEachRecord(this.input,this.derivedAttributes,function(e){return function(r){var a,o,i;if(e.filter(r)){for(a in t)if(i=t[a],i!==(null!=(o=r[a])?o:"null"))return;return n(r)}}}(this))},e.prototype.arrSort=function(t){var e,n;return n=function(){var n,r,a;for(a=[],n=0,r=t.length;n=l;c=0<=l?++r:--r)t[e-1][c]!==t[e][c]&&(i=!1);if(i)return-1}for(a=0;e+a=s;c=0<=s?++o:--o)t[e][c]!==t[e+a][c]&&(u=!0);if(u)break;a++}return a},A=document.createElement("thead");for(d in i)if(a.call(i,d)){o=i[d],S=document.createElement("tr"),0===parseInt(d)&&0!==m.length&&(w=document.createElement("th"),w.setAttribute("colspan",m.length),w.setAttribute("rowspan",i.length),S.appendChild(w)),w=document.createElement("th"),w.className="pvtAxisLabel",w.textContent=o,S.appendChild(w);for(h in s)a.call(s,h)&&(l=s[h],k=b(s,parseInt(h),parseInt(d)),k!==-1&&(w=document.createElement("th"),w.className="pvtColLabel",w.textContent=l[d],w.setAttribute("colspan",k),parseInt(d)===i.length-1&&0!==m.length&&w.setAttribute("rowspan",2),S.appendChild(w)));0===parseInt(d)&&n.table.rowTotals&&(w=document.createElement("th"),w.className="pvtTotalLabel pvtRowTotalLabel",w.innerHTML=n.localeStrings.totals,w.setAttribute("rowspan",i.length+(0===m.length?0:1)),S.appendChild(w)),A.appendChild(S)}if(0!==m.length){S=document.createElement("tr");for(h in m)a.call(m,h)&&(p=m[h],w=document.createElement("th"),w.className="pvtAxisLabel",w.textContent=p,S.appendChild(w));w=document.createElement("th"),0===i.length&&(w.className="pvtTotalLabel pvtRowTotalLabel",w.innerHTML=n.localeStrings.totals),S.appendChild(w),A.appendChild(S)}f.appendChild(A),C=document.createElement("tbody");for(h in v)if(a.call(v,h)){g=v[h],S=document.createElement("tr");for(d in g)a.call(g,d)&&(N=g[d],k=b(v,parseInt(h),parseInt(d)),k!==-1&&(w=document.createElement("th"),w.className="pvtRowLabel",w.textContent=N,w.setAttribute("rowspan",k),parseInt(d)===m.length-1&&0!==i.length&&w.setAttribute("colspan",2),S.appendChild(w)));for(d in s)a.call(s,d)&&(l=s[d],r=e.getAggregator(g,l),T=r.value(),y=document.createElement("td"),y.className="pvtVal row"+h+" col"+d,y.textContent=r.format(T),y.setAttribute("data-value",T),null!=c&&(y.onclick=c(T,g,l)),S.appendChild(y));(n.table.rowTotals||0===i.length)&&(x=e.getAggregator(g,[]),T=x.value(),y=document.createElement("td"),y.className="pvtTotal rowTotal",y.textContent=x.format(T),y.setAttribute("data-value",T),null!=c&&(y.onclick=c(T,g,[])),y.setAttribute("data-for","row"+h),S.appendChild(y)),C.appendChild(S)}if(n.table.colTotals||0===m.length){S=document.createElement("tr"),(n.table.colTotals||0===m.length)&&(w=document.createElement("th"),w.className="pvtTotalLabel pvtColTotalLabel",w.innerHTML=n.localeStrings.totals,w.setAttribute("colspan",m.length+(0===i.length?0:1)),S.appendChild(w));for(d in s)a.call(s,d)&&(l=s[d],x=e.getAggregator([],l),T=x.value(),y=document.createElement("td"),y.className="pvtTotal colTotal",y.textContent=x.format(T),y.setAttribute("data-value",T),null!=c&&(y.onclick=c(T,[],l)),y.setAttribute("data-for","col"+d),S.appendChild(y));(n.table.rowTotals||0===i.length)&&(x=e.getAggregator([],[]),T=x.value(),y=document.createElement("td"),y.className="pvtGrandTotal",y.textContent=x.format(T),y.setAttribute("data-value",T),null!=c&&(y.onclick=c(T,[],[])),S.appendChild(y)),C.appendChild(S)}return f.appendChild(C),f.setAttribute("data-numrows",v.length),f.setAttribute("data-numcols",s.length),f},t.fn.pivot=function(e,n,r){var a,i,s,u,c,h,p,f;null==r&&(r="en"),null==d[r]&&(r="en"),a={cols:[],rows:[],vals:[],rowOrder:"key_a_to_z",colOrder:"key_a_to_z",dataClass:o,filter:function(){return!0},aggregator:l.count()(),aggregatorName:"Count",sorters:{},derivedAttributes:{},renderer:g},u=t.extend(!0,{},d.en.localeStrings,d[r].localeStrings),s={rendererOptions:{localeStrings:u},localeStrings:u},c=t.extend(!0,{},s,t.extend({},a,n)),p=null;try{h=new c.dataClass(e,c);try{p=c.renderer(h,c.rendererOptions)}catch(m){i=m,"undefined"!=typeof console&&null!==console&&console.error(i.stack),p=t("").html(c.localeStrings.renderError)}}catch(m){i=m,"undefined"!=typeof console&&null!==console&&console.error(i.stack),p=t("").html(c.localeStrings.computeError)}for(f=this[0];f.hasChildNodes();)f.removeChild(f.lastChild);return this.append(p)},t.fn.pivotUI=function(n,r,i,l){var s,u,c,p,m,g,v,b,C,y,w,A,x,S,N,T,k,O,_,F,D,E,M,R,I,L,U,K,q,z,V,j,H,B,P,J,G,W,$,Q,Y,X,Z,tt,et;null==i&&(i=!1),null==l&&(l="en"),null==d[l]&&(l="en"),b={derivedAttributes:{},aggregators:d[l].aggregators,renderers:d[l].renderers,hiddenAttributes:[],hiddenFromAggregators:[],hiddenFromDragDrop:[],menuLimit:500,cols:[],rows:[],vals:[],rowOrder:"key_a_to_z",colOrder:"key_a_to_z",dataClass:o,exclusions:{},inclusions:{},unusedAttrsVertical:85,autoSortUnusedAttrs:!1,onRefresh:null,showUI:!0,filter:function(){return!0},sorters:{}},_=t.extend(!0,{},d.en.localeStrings,d[l].localeStrings),O={rendererOptions:{localeStrings:_},localeStrings:_},y=this.data("pivotUIOptions"),M=null==y||i?t.extend(!0,{},O,t.extend({},b,r)):y;try{m={},F=[],L=0,o.forEachRecord(n,M.derivedAttributes,function(t){var e,n,r,o;if(M.filter(t)){F.push(t);for(e in t)a.call(t,e)&&null==m[e]&&(m[e]={},L>0&&(m[e]["null"]=L));for(e in m)o=null!=(r=t[e])?r:"null",null==(n=m[e])[o]&&(n[o]=0),m[e][o]++;return L++}}),Y=t("",{"class":"pvtUi"}).attr("cellpadding",5),B=t("| ").addClass("pvtUiCell"),H=t(" |