Pylint updates
This commit is contained in:
@@ -2,17 +2,18 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class ReportConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'report'
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "report"
|
||||
|
||||
def ready(self) -> None:
|
||||
ready = super().ready()
|
||||
from django.urls import include, path
|
||||
from horilla.urls import urlpatterns
|
||||
|
||||
from horilla.horilla_settings import APPS
|
||||
from horilla.urls import urlpatterns
|
||||
|
||||
urlpatterns.append(
|
||||
path("report/", include("report.urls")),
|
||||
)
|
||||
|
||||
return ready
|
||||
return ready
|
||||
|
||||
@@ -1,90 +1,119 @@
|
||||
from django.utils.translation import gettext_lazy as trans
|
||||
from django.urls import reverse_lazy
|
||||
from django.apps import apps
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as trans
|
||||
|
||||
MENU = trans("Reports")
|
||||
IMG_SRC = "images/ui/report.svg"
|
||||
ACCESSIBILITY = "report.sidebar.menu_accessibility"
|
||||
|
||||
|
||||
SUBMENUS = [
|
||||
|
||||
]
|
||||
SUBMENUS = []
|
||||
|
||||
# Dynamically adding submenu if the app is installed
|
||||
if apps.is_installed('recruitment'):
|
||||
SUBMENUS.append({
|
||||
"menu": "Recruitment",
|
||||
"redirect": reverse_lazy("recruitment-report"),
|
||||
"accessibility": "report.sidebar.recruitment_accessibility",
|
||||
})
|
||||
if apps.is_installed("recruitment"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Recruitment",
|
||||
"redirect": reverse_lazy("recruitment-report"),
|
||||
"accessibility": "report.sidebar.recruitment_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed('employee'):
|
||||
SUBMENUS.append({
|
||||
"menu":"Employee",
|
||||
"redirect":reverse_lazy("employee-report"),
|
||||
"accessibility": "report.sidebar.employee_accessibility",
|
||||
})
|
||||
if apps.is_installed("employee"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Employee",
|
||||
"redirect": reverse_lazy("employee-report"),
|
||||
"accessibility": "report.sidebar.employee_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed('attendance'):
|
||||
SUBMENUS.append({
|
||||
"menu":"Attendance",
|
||||
"redirect":reverse_lazy("attendance-report"),
|
||||
"accessibility": "report.sidebar.attendance_accessibility",
|
||||
})
|
||||
if apps.is_installed("attendance"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Attendance",
|
||||
"redirect": reverse_lazy("attendance-report"),
|
||||
"accessibility": "report.sidebar.attendance_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed('leave'):
|
||||
SUBMENUS.append({
|
||||
"menu":"Leave",
|
||||
"redirect":reverse_lazy("leave-report"),
|
||||
"accessibility": "report.sidebar.leave_accessibility",
|
||||
})
|
||||
if apps.is_installed("leave"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Leave",
|
||||
"redirect": reverse_lazy("leave-report"),
|
||||
"accessibility": "report.sidebar.leave_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed('payroll'):
|
||||
SUBMENUS.append({
|
||||
"menu":"Payroll",
|
||||
"redirect":reverse_lazy("payroll-report"),
|
||||
"accessibility": "report.sidebar.payroll_accessibility",
|
||||
})
|
||||
if apps.is_installed("payroll"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Payroll",
|
||||
"redirect": reverse_lazy("payroll-report"),
|
||||
"accessibility": "report.sidebar.payroll_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed('asset'):
|
||||
SUBMENUS.append({
|
||||
"menu":"Asset",
|
||||
"redirect":reverse_lazy("asset-report"),
|
||||
"accessibility": "report.sidebar.asset_accessibility",
|
||||
})
|
||||
if apps.is_installed("asset"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Asset",
|
||||
"redirect": reverse_lazy("asset-report"),
|
||||
"accessibility": "report.sidebar.asset_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed("pms"):
|
||||
SUBMENUS.append(
|
||||
{
|
||||
"menu": "Performance",
|
||||
"redirect": reverse_lazy("pms-report"),
|
||||
"accessibility": "report.sidebar.pms_accessibility",
|
||||
}
|
||||
)
|
||||
|
||||
if apps.is_installed('pms'):
|
||||
SUBMENUS.append({
|
||||
"menu":"Performance",
|
||||
"redirect":reverse_lazy("pms-report"),
|
||||
"accessibility": "report.sidebar.pms_accessibility",
|
||||
})
|
||||
|
||||
def menu_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("recruitment.view_recruitment") or request.user.has_perm("employee.view_employee") or request.user.has_perm("pms.view_objective") or request.user.has_perm("attendance.view_attendance") or request.user.has_perm("leave.view_leaverequest") or request.user.has_perm("payroll.view_payslip") or request.user.has_perm("asset.view_asset")
|
||||
|
||||
return (
|
||||
request.user.is_superuser
|
||||
or request.user.has_perm("recruitment.view_recruitment")
|
||||
or request.user.has_perm("employee.view_employee")
|
||||
or request.user.has_perm("pms.view_objective")
|
||||
or request.user.has_perm("attendance.view_attendance")
|
||||
or request.user.has_perm("leave.view_leaverequest")
|
||||
or request.user.has_perm("payroll.view_payslip")
|
||||
or request.user.has_perm("asset.view_asset")
|
||||
)
|
||||
|
||||
|
||||
def recruitment_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("recruitment.view_recruitment")
|
||||
return request.user.is_superuser or request.user.has_perm(
|
||||
"recruitment.view_recruitment"
|
||||
)
|
||||
|
||||
|
||||
def employee_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("employee.view_employee")
|
||||
|
||||
|
||||
def attendance_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("attendance.view_attendance")
|
||||
return request.user.is_superuser or request.user.has_perm(
|
||||
"attendance.view_attendance"
|
||||
)
|
||||
|
||||
|
||||
def leave_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("leave.view_leaverequest")
|
||||
|
||||
|
||||
def payroll_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("payroll.view_payslip")
|
||||
|
||||
|
||||
def asset_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("asset.view_asset")
|
||||
|
||||
|
||||
def pms_accessibility(request, submenu, user_perms, *args, **kwargs):
|
||||
return request.user.is_superuser or request.user.has_perm("pms.view_objective")
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<h1 class="oh-main__titlebar-title fw-bold">
|
||||
{% trans "Asset Reports" %}
|
||||
</h1>
|
||||
|
||||
|
||||
|
||||
<div style="display:inline-flex;">
|
||||
<!-- Filter section -->
|
||||
@@ -51,7 +51,7 @@
|
||||
<label class="oh-label" for="{{asset_filter_form.asset_tracking_id.id_for_label}}">{% trans "Tracking Id" %}</label>
|
||||
{{asset_filter_form.asset_tracking_id}}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
<div class="oh-input-group">
|
||||
@@ -136,7 +136,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 {
|
||||
@@ -165,7 +165,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 {
|
||||
@@ -198,7 +198,7 @@
|
||||
alert("No table found to export.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Pivot Data");
|
||||
const baseRow = 5;
|
||||
@@ -215,7 +215,7 @@
|
||||
city: "{{ company.city|escapejs }}",
|
||||
zip: "{{ company.zip|escapejs }}"
|
||||
};
|
||||
|
||||
|
||||
function getBase64FromUrl(url) {
|
||||
return fetch(url)
|
||||
.then(response => response.blob())
|
||||
@@ -226,7 +226,7 @@
|
||||
reader.readAsDataURL(blob);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
const logoUrl = "{{ protocol }}://{{ host }}{{ company.icon.url }}";
|
||||
await getBase64FromUrl(logoUrl).then((base64) => {
|
||||
const base64Data = base64.split(',')[1];
|
||||
@@ -234,13 +234,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);
|
||||
@@ -258,46 +258,46 @@
|
||||
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;
|
||||
|
||||
|
||||
// Table rendering
|
||||
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) => {
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -328,7 +328,7 @@
|
||||
right: { style: 'thin' }
|
||||
};
|
||||
excelCell.alignment = { horizontal: "center", vertical: "middle" };
|
||||
|
||||
|
||||
if (rowspan > 1 || colspan > 1) {
|
||||
worksheet.mergeCells(
|
||||
currentRow + rowIndex,
|
||||
@@ -336,7 +336,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;
|
||||
@@ -345,15 +345,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
|
||||
|
||||
|
||||
worksheet.columns.forEach(column => {
|
||||
let maxLength = 2;
|
||||
column.eachCell({ includeEmpty: true }, cell => {
|
||||
@@ -362,13 +362,13 @@
|
||||
});
|
||||
column.width = maxLength + 3;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<h1 class="oh-main__titlebar-title fw-bold">
|
||||
{% trans "Attendance Reports" %}
|
||||
</h1>
|
||||
|
||||
|
||||
|
||||
<div style="display:inline-flex;">
|
||||
<!-- Filter section -->
|
||||
@@ -213,7 +213,7 @@
|
||||
$.pivotUtilities.renderers,
|
||||
plotlyRenderers // Adding Plotly renderers
|
||||
),
|
||||
|
||||
|
||||
// Hide specific fields from selection dropdown
|
||||
onRefresh: function (config) {
|
||||
let currentRenderer = config.rendererName;
|
||||
@@ -284,7 +284,7 @@
|
||||
$.pivotUtilities.renderers,
|
||||
plotlyRenderers // Adding Plotly renderers
|
||||
),
|
||||
|
||||
|
||||
// Hide specific fields from selection dropdown
|
||||
onRefresh: function (config) {
|
||||
let currentRenderer = config.rendererName;
|
||||
@@ -319,7 +319,7 @@
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// When the filter form is submitted, prevent default action and load filtered data
|
||||
@@ -341,7 +341,7 @@
|
||||
alert("No table found to export.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Pivot Data");
|
||||
const baseRow = 5;
|
||||
@@ -358,7 +358,7 @@
|
||||
city: "{{ company.city|escapejs }}",
|
||||
zip: "{{ company.zip|escapejs }}"
|
||||
};
|
||||
|
||||
|
||||
function getBase64FromUrl(url) {
|
||||
return fetch(url)
|
||||
.then(response => response.blob())
|
||||
@@ -369,7 +369,7 @@
|
||||
reader.readAsDataURL(blob);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
const logoUrl = "{{ protocol }}://{{ host }}{{ company.icon.url }}";
|
||||
await getBase64FromUrl(logoUrl).then((base64) => {
|
||||
const base64Data = base64.split(',')[1];
|
||||
@@ -377,13 +377,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);
|
||||
@@ -401,46 +401,46 @@
|
||||
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;
|
||||
|
||||
|
||||
// Table rendering
|
||||
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) => {
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
right: { style: 'thin' }
|
||||
};
|
||||
excelCell.alignment = { horizontal: "center", vertical: "middle" };
|
||||
|
||||
|
||||
if (rowspan > 1 || colspan > 1) {
|
||||
worksheet.mergeCells(
|
||||
currentRow + rowIndex,
|
||||
@@ -479,7 +479,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;
|
||||
@@ -488,15 +488,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
|
||||
|
||||
|
||||
worksheet.columns.forEach(column => {
|
||||
let maxLength = 2;
|
||||
column.eachCell({ includeEmpty: true }, cell => {
|
||||
@@ -505,13 +505,13 @@
|
||||
});
|
||||
column.width = maxLength + 3;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<label class="oh-label" for="{{f.form.country.id_for_label}}">{% trans "Country" %}</label>
|
||||
{{f.form.country}}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
<div class="oh-input-group">
|
||||
@@ -162,7 +162,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 {
|
||||
@@ -196,7 +196,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 {
|
||||
@@ -208,7 +208,7 @@
|
||||
plotlyRenderers // Adding Plotly renderers
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// When the filter form is submitted, prevent default action and load filtered data
|
||||
@@ -230,7 +230,7 @@
|
||||
alert("No table found to export.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Pivot Data");
|
||||
const baseRow = 5;
|
||||
@@ -247,7 +247,7 @@
|
||||
city: "{{ company.city|escapejs }}",
|
||||
zip: "{{ company.zip|escapejs }}"
|
||||
};
|
||||
|
||||
|
||||
function getBase64FromUrl(url) {
|
||||
return fetch(url)
|
||||
.then(response => response.blob())
|
||||
@@ -258,7 +258,7 @@
|
||||
reader.readAsDataURL(blob);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
const logoUrl = "{{ protocol }}://{{ host }}{{ company.icon.url }}";
|
||||
await getBase64FromUrl(logoUrl).then((base64) => {
|
||||
const base64Data = base64.split(',')[1];
|
||||
@@ -266,13 +266,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);
|
||||
@@ -290,46 +290,46 @@
|
||||
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;
|
||||
|
||||
|
||||
// Table rendering
|
||||
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) => {
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -360,7 +360,7 @@
|
||||
right: { style: 'thin' }
|
||||
};
|
||||
excelCell.alignment = { horizontal: "center", vertical: "middle" };
|
||||
|
||||
|
||||
if (rowspan > 1 || colspan > 1) {
|
||||
worksheet.mergeCells(
|
||||
currentRow + rowIndex,
|
||||
@@ -368,7 +368,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;
|
||||
@@ -377,15 +377,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
|
||||
|
||||
|
||||
worksheet.columns.forEach(column => {
|
||||
let maxLength = 2;
|
||||
column.eachCell({ includeEmpty: true }, cell => {
|
||||
@@ -394,13 +394,13 @@
|
||||
});
|
||||
column.width = maxLength + 3;
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
{% trans "Leave Reports" %}
|
||||
</h1>
|
||||
|
||||
|
||||
|
||||
<div style="display:inline-flex;">
|
||||
<!-- Filter section -->
|
||||
<form id="filterForm" onsubmit="event.preventDefault(); loadFilteredPivotData();" class="me-3" style="margin-top: 10px;">
|
||||
@@ -78,7 +78,7 @@
|
||||
{{form.from_date}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
<div class="oh-input-group">
|
||||
<label class="oh-label" for="{{ form.to_date.id_for_label }}">{% trans "To Date" %}</label>
|
||||
@@ -292,17 +292,17 @@
|
||||
containerId = "pivot-availableleave";
|
||||
rowsConfig = ["Name","Department", "Leave Type","Assigned Date","Total Leave Days","Available Days","Carryforward Days"]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Show relevant container
|
||||
$("#" + containerId).show();
|
||||
|
||||
// Fetch data dynamically based on model
|
||||
$.getJSON(url, function (data) {
|
||||
|
||||
|
||||
// Add Plotly renderers correctly
|
||||
let plotlyRenderers = $.pivotUtilities.plotly_renderers;
|
||||
|
||||
|
||||
// Initialize pivot table with Plotly enabled
|
||||
$("#" + containerId).pivotUI(data, {
|
||||
rows: rowsConfig,
|
||||
@@ -331,12 +331,12 @@
|
||||
window.loadFilteredPivotData =function loadFilteredPivotData() {
|
||||
const selectedModel = $("#model-select").val();
|
||||
const formData = $("#filterForm").serialize();
|
||||
|
||||
|
||||
$(".pivot-wrapper").hide();
|
||||
|
||||
|
||||
let containerId = "";
|
||||
let rowsConfig = [];
|
||||
|
||||
|
||||
if (selectedModel === "leave_request"){
|
||||
containerId = "pivot-leave";
|
||||
rowsConfig = ["Name","Department","Shift", "Leave Type","Requested Days","Start Date","End Date","Status"]
|
||||
@@ -344,13 +344,13 @@
|
||||
containerId = "pivot-availableleave";
|
||||
rowsConfig = ["Name","Department", "Leave Type","Assigned Date","Total Leave Days","Available Days","Carryforward Days"]
|
||||
}
|
||||
|
||||
|
||||
$("#" + containerId).show();
|
||||
|
||||
|
||||
$.getJSON(`leave-pivot?model=${selectedModel}&${formData}`, function (data) {
|
||||
|
||||
const plotlyRenderers = $.pivotUtilities.plotly_renderers;
|
||||
|
||||
|
||||
$("#" + containerId).pivotUI(data, {
|
||||
rows: rowsConfig,
|
||||
cols: [],
|
||||
@@ -364,21 +364,21 @@
|
||||
} else {
|
||||
$("#export-btn").hide();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Initial load with all models
|
||||
loadPivotData("leave_request");
|
||||
|
||||
|
||||
// Model selection change event
|
||||
$("#model-select").on("change", function () {
|
||||
let selectedModel = $(this).val();
|
||||
loadPivotData(selectedModel); // Reload pivot data with selected model
|
||||
});
|
||||
|
||||
|
||||
// Export to Excel on button click
|
||||
$("#export-btn").on("click", function () {
|
||||
let visiblePivot = $(".pivot-wrapper:visible .pvtTable").closest(".pivot-wrapper");
|
||||
@@ -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,51 +458,51 @@
|
||||
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) => {
|
||||
|
||||
|
||||
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,
|
||||
@@ -512,7 +512,7 @@
|
||||
'FF000000'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
excelCell.fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
@@ -530,7 +530,7 @@
|
||||
right: { style: 'thin' }
|
||||
};
|
||||
excelCell.alignment = { horizontal: "center", vertical: "middle" };
|
||||
|
||||
|
||||
// Merge
|
||||
if (rowspan > 1 || colspan > 1) {
|
||||
worksheet.mergeCells(
|
||||
@@ -539,7 +539,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;
|
||||
@@ -548,15 +548,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;
|
||||
@@ -566,13 +566,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;
|
||||
@@ -580,7 +580,7 @@
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
@@ -208,7 +208,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -242,7 +242,7 @@
|
||||
<select id="model-select" class="oh-select oh-select--sm ml-2 mb-2" style="width: 200px;">
|
||||
<option value="payslip">Payslip</option>
|
||||
<option value="allowance">Allowance & Deduction</option>
|
||||
</select>
|
||||
</select>
|
||||
|
||||
<!-- Pivot Container -->
|
||||
<div id="pivot-container" class="mb-5" style="width: 100%; overflow-x: auto;">
|
||||
@@ -260,11 +260,11 @@
|
||||
|
||||
// Hide all containers first
|
||||
$(".pivot-wrapper").hide();
|
||||
|
||||
|
||||
// Determine current container and row config
|
||||
let containerId = "";
|
||||
let rowsConfig = [];
|
||||
|
||||
|
||||
if (model === "payslip") {
|
||||
containerId = "pivot-payslip";
|
||||
rowsConfig = ["Employee","Basic Salary","Gross Pay","Net Pay","Status"];
|
||||
@@ -272,7 +272,7 @@
|
||||
containerId = "pivot-allowance";
|
||||
rowsConfig = ["Employee","Allowance & Deduction","Allowance & Deduction Title","Allowance & Deduction Amount"];
|
||||
}
|
||||
|
||||
|
||||
// Show relevant container
|
||||
$("#" + containerId).show();
|
||||
|
||||
@@ -280,20 +280,20 @@
|
||||
$.getJSON(url, function (data) {
|
||||
// Add Plotly renderers correctly
|
||||
let plotlyRenderers = $.pivotUtilities.plotly_renderers;
|
||||
|
||||
|
||||
// Initialize pivot table with Plotly enabled
|
||||
$("#" + containerId).pivotUI(data, {
|
||||
|
||||
|
||||
rows: rowsConfig,
|
||||
cols: [], // Default columns
|
||||
aggregatorName: "Count", // Default aggregator
|
||||
rendererName: "Table", // Default view as Table
|
||||
|
||||
|
||||
renderers: $.extend(
|
||||
$.pivotUtilities.renderers,
|
||||
plotlyRenderers // Adding Plotly renderers
|
||||
),
|
||||
|
||||
|
||||
onRefresh: function (config) {
|
||||
let currentRenderer = config.rendererName;
|
||||
if (
|
||||
@@ -307,10 +307,10 @@
|
||||
} else {
|
||||
$("#export-btn").hide(); // Hide button for charts
|
||||
}
|
||||
|
||||
|
||||
// ✅ Hide fields from the dropdown but keep them visible in the table
|
||||
let hiddenFields = ["Allowance Amount", "Deduction Amount"];
|
||||
|
||||
|
||||
setTimeout(function () {
|
||||
$(".pvtAttrDropdown option").each(function () {
|
||||
if (hiddenFields.includes($(this).text())) {
|
||||
@@ -324,12 +324,12 @@
|
||||
window.loadFilteredPivotData =function loadFilteredPivotData() {
|
||||
const selectedModel = $("#model-select").val();
|
||||
const formData = $("#filterForm").serialize();
|
||||
|
||||
|
||||
$(".pivot-wrapper").hide();
|
||||
|
||||
|
||||
let containerId = "";
|
||||
let rowsConfig = [];
|
||||
|
||||
|
||||
if (model === "payslip") {
|
||||
containerId = "pivot-payslip";
|
||||
rowsConfig = ["Employee","Basic Salary","Gross Pay","Net Pay","Status"];
|
||||
@@ -337,13 +337,13 @@
|
||||
containerId = "pivot-allowance";
|
||||
rowsConfig = ["Employee","Allowance & Deduction","Allowance & Deduction Title","Allowance & Deduction Amount"];
|
||||
}
|
||||
|
||||
|
||||
$("#" + containerId).show();
|
||||
|
||||
|
||||
$.getJSON(`payroll-pivot?model=${selectedModel}&${formData}`, function (data) {
|
||||
|
||||
|
||||
const plotlyRenderers = $.pivotUtilities.plotly_renderers;
|
||||
|
||||
|
||||
$("#" + containerId).pivotUI(data, {
|
||||
rows: rowsConfig,
|
||||
cols: [],
|
||||
@@ -357,14 +357,14 @@
|
||||
} else {
|
||||
$("#export-btn").hide();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Export to Excel on button click
|
||||
$("#export-btn").on("click", function () {
|
||||
let visiblePivot = $(".pivot-wrapper:visible .pvtTable").closest(".pivot-wrapper");
|
||||
@@ -382,14 +382,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 = {
|
||||
@@ -400,7 +400,7 @@
|
||||
city: "{{ company.city|escapejs }}",
|
||||
zip: "{{ company.zip|escapejs }}"
|
||||
};
|
||||
|
||||
|
||||
function getBase64FromUrl(url) {
|
||||
return fetch(url)
|
||||
.then(response => response.blob())
|
||||
@@ -411,7 +411,7 @@
|
||||
reader.readAsDataURL(blob);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
const logoUrl = "{{ protocol }}://{{ host }}{{ company.icon.url }}";
|
||||
await getBase64FromUrl(logoUrl).then((base64) => {
|
||||
const base64Data = base64.split(',')[1];
|
||||
@@ -419,13 +419,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);
|
||||
@@ -443,51 +443,51 @@
|
||||
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) => {
|
||||
|
||||
|
||||
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,
|
||||
@@ -497,7 +497,7 @@
|
||||
'FF000000'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
excelCell.fill = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
@@ -515,7 +515,7 @@
|
||||
right: { style: 'thin' }
|
||||
};
|
||||
excelCell.alignment = { horizontal: "center", vertical: "middle" };
|
||||
|
||||
|
||||
// Merge
|
||||
if (rowspan > 1 || colspan > 1) {
|
||||
worksheet.mergeCells(
|
||||
@@ -524,7 +524,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;
|
||||
@@ -533,15 +533,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;
|
||||
@@ -551,13 +551,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;
|
||||
@@ -574,8 +574,8 @@
|
||||
loadPivotData(selectedModel); // Reload pivot data with selected model
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
<option value="employeeobjective">Employee Objective</option>
|
||||
<option value="feedback">Feedback</option>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<!-- Pivot Container -->
|
||||
<div id="pivot-container" class="mb-5" style="width: 100%; overflow-x: auto;">
|
||||
@@ -216,11 +216,11 @@
|
||||
function loadPivotData(model) {
|
||||
// Hide all containers first
|
||||
$(".pivot-wrapper").hide();
|
||||
|
||||
|
||||
// Determine current container and row config
|
||||
let containerId = "";
|
||||
let rowsConfig = [];
|
||||
|
||||
|
||||
if (model === "feedback") {
|
||||
containerId = "pivot-feedback";
|
||||
rowsConfig = ["Title","Employee","Manager","Answerable Employees","Answered Employees","Questions","Answer"];
|
||||
@@ -231,14 +231,14 @@
|
||||
containerId = "pivot-employeeobjective";
|
||||
rowsConfig = ["Employee", "Objective","Employee Keyresult","Keyresult Target Value","Keyresult Current Value"];
|
||||
}
|
||||
|
||||
|
||||
// Show relevant container
|
||||
$("#" + containerId).show();
|
||||
|
||||
|
||||
// Fetch and render data in its own container
|
||||
$.getJSON(`pms-pivot?model=${model}`, function (data) {
|
||||
let plotlyRenderers = $.pivotUtilities.plotly_renderers;
|
||||
|
||||
|
||||
$("#" + containerId).pivotUI(data, {
|
||||
rows: rowsConfig,
|
||||
cols: [],
|
||||
@@ -252,9 +252,9 @@
|
||||
} else {
|
||||
$("#export-btn").hide();
|
||||
}
|
||||
|
||||
|
||||
$(".pvtTotal, .pvtTotalLabel, .pvtGrandTotal, .pvtAggregator").hide();
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
$(".pvtAttrDropdown option").each(function () {
|
||||
if (hiddenFields.includes($(this).text())) {
|
||||
@@ -271,12 +271,12 @@
|
||||
window.loadFilteredPivotData =function loadFilteredPivotData() {
|
||||
const selectedModel = $("#model-select").val();
|
||||
const formData = $("#filterForm").serialize();
|
||||
|
||||
|
||||
$(".pivot-wrapper").hide();
|
||||
|
||||
|
||||
let containerId = "";
|
||||
let rowsConfig = [];
|
||||
|
||||
|
||||
if (selectedModel === "feedback") {
|
||||
containerId = "pivot-feedback";
|
||||
rowsConfig = ["Title","Employee", "Manager", "Answerable Employees", "Answered Employees", "Questions", "Answer"];
|
||||
@@ -287,15 +287,15 @@
|
||||
containerId = "pivot-employeeobjective";
|
||||
rowsConfig = ["Employee", "Objective", "Employee Keyresult", "Keyresult Target Value", "Keyresult Current Value"];
|
||||
}
|
||||
|
||||
|
||||
$("#" + containerId).show();
|
||||
|
||||
|
||||
$.getJSON(`pms-pivot?model=${selectedModel}&${formData}`, function (data) {
|
||||
|
||||
console.log("Filtered data:", data);
|
||||
|
||||
const plotlyRenderers = $.pivotUtilities.plotly_renderers;
|
||||
|
||||
|
||||
$("#" + containerId).pivotUI(data, {
|
||||
rows: rowsConfig,
|
||||
cols: [],
|
||||
@@ -309,24 +309,24 @@
|
||||
} else {
|
||||
$("#export-btn").hide();
|
||||
}
|
||||
|
||||
|
||||
$(".pvtTotal, .pvtTotalLabel, .pvtGrandTotal, .pvtAggregator").hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Listen to dropdown
|
||||
$("#model-select").on("change", function () {
|
||||
let selectedModel = $(this).val();
|
||||
loadPivotData(selectedModel);
|
||||
});
|
||||
|
||||
|
||||
// Initial load
|
||||
loadPivotData("objective");
|
||||
|
||||
|
||||
|
||||
|
||||
// Export to Excel on button click
|
||||
$("#export-btn").on("click", function () {
|
||||
let visiblePivot = $(".pivot-wrapper:visible .pvtTable").closest(".pivot-wrapper");
|
||||
@@ -343,14 +343,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 = {
|
||||
@@ -361,7 +361,7 @@
|
||||
city: "{{ company.city|escapejs }}",
|
||||
zip: "{{ company.zip|escapejs }}"
|
||||
};
|
||||
|
||||
|
||||
function getBase64FromUrl(url) {
|
||||
return fetch(url)
|
||||
.then(response => response.blob())
|
||||
@@ -372,7 +372,7 @@
|
||||
reader.readAsDataURL(blob);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
const logoUrl = "{{ protocol }}://{{ host }}{{ company.icon.url }}";
|
||||
await getBase64FromUrl(logoUrl).then((base64) => {
|
||||
const base64Data = base64.split(',')[1];
|
||||
@@ -380,13 +380,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);
|
||||
@@ -404,24 +404,24 @@
|
||||
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
|
||||
// ------------------------
|
||||
@@ -431,9 +431,9 @@
|
||||
row.classList.contains("pvtTotal") ||
|
||||
row.classList.contains("pvtGrandTotal")
|
||||
) return;
|
||||
|
||||
|
||||
let colIndex = baseCol;
|
||||
|
||||
|
||||
Array.from(row.cells).forEach((cell) => {
|
||||
if (
|
||||
cell.classList.contains("pvtTotal") ||
|
||||
@@ -441,18 +441,18 @@
|
||||
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;
|
||||
|
||||
|
||||
excelCell.font = {
|
||||
bold: rowIndex === 0,
|
||||
size: rowIndex === 0 ? 12 : 11,
|
||||
@@ -470,7 +470,7 @@
|
||||
right: { style: 'thin' }
|
||||
};
|
||||
excelCell.alignment = { horizontal: "center", vertical: "middle" };
|
||||
|
||||
|
||||
// Merge
|
||||
if (rowspan > 1 || colspan > 1) {
|
||||
worksheet.mergeCells(
|
||||
@@ -479,7 +479,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;
|
||||
@@ -488,14 +488,14 @@
|
||||
} else {
|
||||
cellMap[`${currentRow + rowIndex}-${colIndex}`] = true;
|
||||
}
|
||||
|
||||
|
||||
colIndex++;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Header row height
|
||||
worksheet.getRow(currentRow).height = 30;
|
||||
|
||||
|
||||
// Auto-adjust column widths
|
||||
worksheet.columns.forEach(column => {
|
||||
let maxLength = 5;
|
||||
@@ -505,21 +505,21 @@
|
||||
});
|
||||
column.width = maxLength + 0;
|
||||
});
|
||||
|
||||
|
||||
// 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;
|
||||
link.click();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<label class="oh-label">{% trans 'Job Position' %}</label>
|
||||
{{ f.form.job_position_id }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="oh-input-group">
|
||||
<label class="oh-label">{% trans 'Is Hired' %}?</label>
|
||||
{{ f.form.hired }}
|
||||
@@ -77,12 +77,12 @@
|
||||
<label class="oh-label">{% trans 'Email' %}</label>
|
||||
{{ f.form.email }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="oh-input-group">
|
||||
<label class="oh-label">{% trans 'Gender' %}</label>
|
||||
{{ f.form.gender }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="oh-input-group">
|
||||
<label class="oh-label">{% trans 'State' %}</label>
|
||||
<select name="state" class="oh-select-2 w-100 country" id="state">
|
||||
@@ -98,7 +98,7 @@
|
||||
<label class="oh-label">{% trans 'Stage Type' %}</label>
|
||||
{{ f.form.stage_id__stage_type }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="oh-input-group">
|
||||
<label class="oh-label">{% trans 'Is Canceled' %}?</label>
|
||||
{{ f.form.canceled }}
|
||||
@@ -172,7 +172,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="oh-accordion">
|
||||
<div class="oh-accordion-header">{% trans "Onboarding" %}</div>
|
||||
<div class="oh-accordion-body">
|
||||
@@ -195,7 +195,7 @@
|
||||
{{ fo.form.recruitment_id__company_id }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-sm-12 col-md-12 col-lg-6">
|
||||
<div class="oh-input-group">
|
||||
<label class="oh-label">{% trans 'Candidates' %}</label>
|
||||
@@ -246,7 +246,7 @@
|
||||
<option value="recruitment">{% trans "Recruitment" %}</option>
|
||||
<option value="onboarding">{% trans "Onboarding" %}</option>
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<!-- Pivot Container -->
|
||||
<div id="pivot-container" class="mb-5" style="width: 100%; overflow-x: auto;">
|
||||
@@ -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 @@
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
Reference in New Issue
Block a user