[UPDT] LEAVE : Multi company functionalites updations in all models and fixes

This commit is contained in:
Horilla
2024-11-15 10:59:52 +05:30
parent 0b244f215d
commit ce629c6d1b
6 changed files with 31 additions and 752 deletions

View File

@@ -693,11 +693,12 @@ class LeaveRequest(HorillaModel):
conditions = None
if work_info.exists():
department_id = self.employee_id.employee_work_info.department_id
emp_comp_id = self.employee_id.employee_work_info.company_id
requested_days = self.requested_days
applicable_condition = False
if department_id != None:
if department_id != None and emp_comp_id != None:
conditions = MultipleApprovalCondition.objects.filter(
department=department_id
department=department_id, company_id=emp_comp_id
).order_by("condition_value")
if conditions != None:
for condition in conditions:
@@ -1075,6 +1076,14 @@ class RestrictLeave(HorillaModel):
description = models.TextField(
null=True, verbose_name=_("Description"), max_length=255
)
company_id = models.ForeignKey(
Company,
null=True,
blank=True,
on_delete=models.CASCADE,
verbose_name=_("Company"),
)
objects = HorillaCompanyManager(related_company_field="company_id")
def __str__(self) -> str:
return f"{self.title}"

View File

@@ -1,422 +0,0 @@
var rowMessages = {
ar: " تم الاختيار",
de: " Ausgewählt",
es: " Seleccionado",
en: " Selected",
fr: " Sélectionné",
};
var deleteHolidayMessages = {
ar: "هل تريد حقًا حذف جميع العطل المحددة؟",
de: "Möchten Sie wirklich alle ausgewählten Feiertage löschen?",
es: "¿Realmente quieres eliminar todas las vacaciones seleccionadas?",
en: "Do you really want to delete all the selected holidays?",
fr: "Voulez-vous vraiment supprimer toutes les vacances sélectionnées?",
};
var no_rows_deleteMessages = {
ar: "لم تتم تحديد صفوف لحذف العطلات.",
de: "Es wurden keine Zeilen zum Löschen von Feiertagen ausgewählt.",
es: "No se han seleccionado filas para eliminar las vacaciones.",
en: "No rows are selected for deleting holidays.",
fr: "Aucune ligne n'a été sélectionnée pour supprimer les vacances.",
};
var downloadMessages = {
ar: "هل ترغب في تنزيل القالب؟",
de: "Möchten Sie die Vorlage herunterladen?",
es: "¿Quieres descargar la plantilla?",
en: "Do you want to download the template?",
fr: "Voulez-vous télécharger le modèle ?",
};
function createHolidayHxValue() {
var pd = $(".oh-pagination").attr("data-pd");
var hxValue = JSON.stringify(pd);
$("#holidayCreateButton").attr("hx-vals", `{"pd":${hxValue}}`);
}
tickHolidayCheckboxes();
function makeHolidayListUnique(list) {
return Array.from(new Set(list));
}
function getCurrentLanguageCode(callback) {
var languageCode = $("#main-section-data").attr("data-lang");
var allowedLanguageCodes = ["ar", "de", "es", "en", "fr"];
if (allowedLanguageCodes.includes(languageCode)) {
callback(languageCode);
} else {
$.ajax({
type: "GET",
url: "/employee/get-language-code/",
success: function (response) {
var ajaxLanguageCode = response.language_code;
$("#main-section-data").attr("data-lang", ajaxLanguageCode);
callback(
allowedLanguageCodes.includes(ajaxLanguageCode)
? ajaxLanguageCode
: "en"
);
},
error: function () {
callback("en");
},
});
}
}
function tickHolidayCheckboxes() {
var ids = JSON.parse($("#selectedHolidays").attr("data-ids") || "[]");
uniqueIds = makeHolidayListUnique(ids);
toggleHighlight(uniqueIds);
click = $("#selectedHolidays").attr("data-clicked");
if (click === "1") {
$(".all-holidays").prop("checked", true);
}
uniqueIds.forEach(function (id) {
$("#" + id).prop("checked", true);
});
var selectedCount = uniqueIds.length;
getCurrentLanguageCode(function (code) {
languageCode = code;
var message = rowMessages[languageCode];
if (selectedCount > 0) {
$("#unselectAllHolidays").css("display", "inline-flex");
$("#exportHolidays").css("display", "inline-flex");
$("#selectedShowHolidays").css("display", "inline-flex");
$("#selectedShowHolidays").text(selectedCount + " -" + message);
} else {
$("#unselectAllHolidays").css("display", "none ");
$("#selectedShowHolidays").css("display", "none");
$("#exportHolidays").css("display", "none");
}
});
}
function addingHolidayIds() {
var ids = JSON.parse($("#selectedHolidays").attr("data-ids") || "[]");
var selectedCount = 0;
$(".all-holidays-row").each(function () {
if ($(this).is(":checked")) {
ids.push(this.id);
} else {
var index = ids.indexOf(this.id);
if (index > -1) {
ids.splice(index, 1);
$(".all-holidays").prop("checked", false);
}
}
});
ids = makeHolidayListUnique(ids);
toggleHighlight(ids);
selectedCount = ids.length;
getCurrentLanguageCode(function (code) {
languageCode = code;
var message = rowMessages[languageCode];
$("#selectedHolidays").attr("data-ids", JSON.stringify(ids));
if (selectedCount === 0) {
$("#selectedShowHolidays").css("display", "none");
$("#exportHolidays").css("display", "none");
$('#unselectAllHolidays').css("display", "none");
} else {
$("#unselectAllHolidays").css("display", "inline-flex");
$("#exportHolidays").css("display", "inline-flex");
$("#selectedShowHolidays").css("display", "inline-flex");
$("#selectedShowHolidays").text(selectedCount + " - " + message);
}
});
}
function selectAllHolidays() {
$("#selectedHolidays").attr("data-clicked", 1);
$("#selectedShowHolidays").removeAttr("style");
var savedFilters = JSON.parse(localStorage.getItem("savedFilters"));
if (savedFilters && savedFilters["filterData"] !== null) {
var filter = savedFilters["filterData"];
$.ajax({
url: "/leave/holiday-select-filter",
data: { page: "all", filter: JSON.stringify(filter) },
type: "GET",
dataType: "json",
success: function (response) {
var employeeIds = response.employee_ids;
if (Array.isArray(employeeIds)) {
// Continue
} else {
console.error("employee_ids is not an array:", employeeIds);
}
for (var i = 0; i < employeeIds.length; i++) {
var empId = employeeIds[i];
$("#" + empId).prop("checked", true);
}
$("#selectedHolidays").attr("data-ids", JSON.stringify(employeeIds));
count = makeHolidayListUnique(employeeIds);
tickHolidayCheckboxes(count);
},
error: function (xhr, status, error) {
console.error("Error:", error);
},
});
} else {
$.ajax({
url: "/leave/holiday-select",
data: { page: "all" },
type: "GET",
dataType: "json",
success: function (response) {
var employeeIds = response.employee_ids;
if (Array.isArray(employeeIds)) {
// Continue
} else {
console.error("employee_ids is not an array:", employeeIds);
}
for (var i = 0; i < employeeIds.length; i++) {
var empId = employeeIds[i];
$("#" + empId).prop("checked", true);
}
var previousIds = $("#selectedHolidays").attr("data-ids");
$("#selectedHolidays").attr(
"data-ids",
JSON.stringify(
Array.from(new Set([...employeeIds, ...JSON.parse(previousIds)]))
)
);
count = makeHolidayListUnique(employeeIds);
tickHolidayCheckboxes(count);
},
error: function (xhr, status, error) {
console.error("Error:", error);
},
});
}
}
function unselectAllHolidays() {
$("#selectedHolidays").attr("data-clicked", 0);
$.ajax({
url: "/leave/holiday-select",
data: { page: "all", filter: "{}" },
type: "GET",
dataType: "json",
success: function (response) {
var employeeIds = response.employee_ids;
if (Array.isArray(employeeIds)) {
// Continue
} else {
console.error("employee_ids is not an array:", employeeIds);
}
for (var i = 0; i < employeeIds.length; i++) {
var empId = employeeIds[i];
$("#" + empId).prop("checked", false);
$(".all-holidays").prop("checked", false);
}
var ids = JSON.parse($("#selectedHolidays").attr("data-ids") || "[]");
var uniqueIds = makeListUnique(ids);
toggleHighlight(uniqueIds);
$("#selectedHolidays").attr("data-ids", JSON.stringify([]));
count = [];
tickHolidayCheckboxes(count);
},
error: function (xhr, status, error) {
console.error("Error:", error);
},
});
}
function exportHolidays() {
var currentDate = new Date().toISOString().slice(0, 10);
var language_code = null;
getCurrentLanguageCode(function (code) {
language_code = code;
var confirmMessage = excelMessages[language_code];
ids = [];
ids = JSON.parse($("#selectedHolidays").attr("data-ids"));
Swal.fire({
text: confirmMessage,
icon: "question",
showCancelButton: true,
confirmButtonColor: "#008000",
cancelButtonColor: "#d33",
confirmButtonText: "Confirm",
}).then(function (result) {
if (result.isConfirmed) {
$.ajax({
type: "GET",
url: "/leave/holiday-info-export",
data: {
ids: JSON.stringify(ids),
},
dataType: "binary",
xhrFields: {
responseType: "blob",
},
success: function (response) {
const file = new Blob([response], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = URL.createObjectURL(file);
const link = document.createElement("a");
link.href = url;
link.download = "holiday_leaves" + currentDate + ".xlsx";
document.body.appendChild(link);
link.click();
},
error: function (xhr, textStatus, errorThrown) {
console.error("Error downloading file:", errorThrown);
},
});
}
});
});
}
$("#bulkHolidaysDelete").click(function (e) {
e.preventDefault();
var languageCode = null;
getCurrentLanguageCode(function (code) {
languageCode = code;
var confirmMessage = deleteHolidayMessages[languageCode];
var textMessage = no_rows_deleteMessages[languageCode];
ids = [];
ids.push($("#selectedHolidays").attr("data-ids"));
ids = JSON.parse($("#selectedHolidays").attr("data-ids"));
if (ids.length === 0) {
Swal.fire({
text: textMessage,
icon: "warning",
confirmButtonText: "Close",
});
} else {
Swal.fire({
text: confirmMessage,
icon: "error",
showCancelButton: true,
confirmButtonColor: "#008000",
cancelButtonColor: "#d33",
confirmButtonText: "Confirm",
}).then(function (result) {
if (result.isConfirmed) {
ids = [];
ids.push($("#selectedHolidays").attr("data-ids"));
ids = JSON.parse($("#selectedHolidays").attr("data-ids"));
$.ajax({
type: "POST",
url: "/leave/holidays-bulk-delete",
data: {
csrfmiddlewaretoken: getCookie("csrftoken"),
ids: JSON.stringify(ids),
},
success: function (response, textStatus, jqXHR) {
if (jqXHR.status === 200) {
location.reload();
} else {
}
},
});
}
});
}
});
});
$("#holidaysInfoImport").click(function (e) {
e.preventDefault();
var languageCode = null;
getCurrentLanguageCode(function (code) {
languageCode = code;
var confirmMessage = downloadMessages[languageCode];
Swal.fire({
text: confirmMessage,
icon: "question",
showCancelButton: true,
confirmButtonColor: "#008000",
cancelButtonColor: "#d33",
confirmButtonText: "Confirm",
}).then(function (result) {
if (result.isConfirmed) {
$.ajax({
type: "GET",
url: "holidays-excel-template",
dataType: "binary",
xhrFields: {
responseType: "blob",
},
success: function (response) {
const file = new Blob([response], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = URL.createObjectURL(file);
const link = document.createElement("a");
link.href = url;
link.download = "holiday_excel.xlsx";
document.body.appendChild(link);
link.click();
},
error: function (xhr, textStatus, errorThrown) {
console.error("Error downloading file:", errorThrown);
},
});
}
});
});
});
$("#holidaysImportForm").submit(function (e) {
e.preventDefault();
// Create a FormData object to send the file
$("#uploadContainer").css("display", "none");
$("#uploading").css("display", "block");
var formData = new FormData(this);
fetch("/leave/holidays-info-import", {
method: "POST",
dataType: "binary",
body: formData,
processData: false,
contentType: false,
headers: {
// Include the CSRF token in the headers
"X-CSRFToken": "{{ csrf_token }}",
},
xhrFields: {
responseType: "blob",
},
})
.then((response) => {
if (response.ok) {
return response.blob(); // Use response.blob() to get the binary data
} else {
// Handle errors, e.g., show an error message
}
})
.then((blob) => {
if (blob) {
// Create a Blob from the binary data
const file = new Blob([blob], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = URL.createObjectURL(file);
const link = document.createElement("a");
link.href = url;
link.download = "HolidayImportError.xlsx";
document.body.appendChild(link);
link.click();
window.location.href = "/leave/holiday-view";
} else {
window.location.reload();
}
})
.catch((error) => {});
});

View File

@@ -1,328 +0,0 @@
{% extends 'index.html' %} {% block content %} {% load static %} {% load i18n %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{ message.tags }}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div
class="oh-modal"
id="assignLeaveTypeImport"
role="dialog"
aria-labelledby="assignLeaveTypeImport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="assignLeaveTypeImportLavel">
{% trans "Import Assigned Leaves" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div
class="oh-modal__dialog-body p-0 pt-2"
id="assignLeaveTypeImportModalBody"
>
<form
action="{%url 'assign-leave-type-info-import' %}"
enctype="multipart/form-data"
method="post"
id="assignLeaveTypeImportForm"
>
{% csrf_token %}
<div
class="oh-modal__dialog-body mr-5"
id="uploading"
style="display: none"
>
<div class="loader-container">
<div class="loader"></div>
<div class="loader-text">{% trans "Uploading..." %}</div>
</div>
</div>
<div id="uploadContainer">
<label class="oh-dropdown__import-label" for="uploadFile">
<ion-icon
name="cloud-upload"
class="oh-dropdown__import-form-icon"
></ion-icon>
<span class="oh-dropdown__import-form-title"
>{% trans "Upload a File" %}</span
>
<span class="oh-dropdown__import-form-text"
>{% trans "Drag and drop files here" %}</span
>
</label>
<input type="file" name="assign_leave_type_import" id="" required />
</div>
<button
type="submit"
class="oh-btn oh-btn--small oh-btn--secondary w-100 mt-3"
>
{% trans "Upload" %}
</button>
</form>
</div>
</div>
</div>
</div>
<div
class="oh-modal"
id="assignedLeavesExport"
role="dialog"
aria-labelledby="assignedLeavesExport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="assignedLeavesExportLavel">
{% trans "Export Assigned Leaves" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div
class="oh-modal__dialog-body p-0 pt-2"
id="assignedLeavesExportModalBody"
>
<form
action="{%url 'assigned-leaves-info-export' %}"
method="get"
onsubmit="event.stopPropagation();$(this).parents().find('.oh-modal--show').last().toggleClass('oh-modal--show');"
id="assignedLeavesExportForm"
>
{% csrf_token %}
{% include "leave/leave_assign/assigned_leaves_export_filter.html" %}
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100">
{% trans "Export" %}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">
{% trans "All Assigned Leaves" %}
</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon"
></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
{% if available_leaves %}
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
placeholder="{% trans 'Search' %}"
name="search"
hx-get="{% url 'assign-filter' %}"
hx-trigger="keyup"
hx-target="#assignedLeaves"
/>
</div>
<div class="oh-main__titlebar-button-container">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
<div id="filterCount"></div>
</button>
{% include "leave/leave_assign/assigned_leaves_filter.html" %}
</div>
{% endif %}
<div class="oh-dropdown ml-2" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right"
x-show="open"
style="display: none"
>
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link"
id="assign-leave-type-info-import"
data-toggle="oh-modal-toggle"
data-target="#assignLeaveTypeImport"
>{% trans "Import" %}</a
>
</li>
{% if available_leaves %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link"
id="assign-leave-type-export"
data-toggle="oh-modal-toggle"
data-target="#assignedLeavesExport"
>{% trans "Export" %}</a
>
</li>
<li class="oh-dropdown__item">
<a
href="#"
id="bulkAssignedLeavesDelete"
class="oh-dropdown__link oh-dropdown__link--danger"
>{% trans "Delete" %}</a
>
</li>
{% endif %}
</ul>
</div>
</div>
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#editModal2"
hx-get="{% url 'assign' %}"
hx-target="#assignLeaveForm"
>
{% trans "Assign" %}
</button>
</div>
</div>
</div>
</div>
</section>
<div class="oh-wrapper" id="assignedLeaves">
{% if available_leaves %}
{% include 'leave/leave_assign/assigned_leave.html'%}
{% else %}
<div
style="
height: 70vh;
display: flex;
align-items: center;
justify-content: center;
"
class=""
>
<div style="" class="oh-404">
<img
style="display: block; width: 150px; height: 150px; margin: 10px auto"
src="{% static 'images/ui/leave_types.png' %}"
class="mb-4"
alt=""
/>
<h3 style="font-size: 20px" class="oh-404__subtitle">
{% trans "There are no leave assigned at the moment." %}
</h3>
</div>
</div>
{% endif %}
</div>
<!-- update available leave -->
<div
class="oh-modal"
id="editModal1"
role="dialog"
aria-labelledby="editDialogModal"
aria-hidden="true"
>
<div class="oh-modal__dialog oh-modal__dialog--timeoff">
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="editDialogDialog"
>{% trans "Update Available Leave" %}</span
>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div id="leaveForm"></div>
</div>
</div>
<!-- Assign leave -->
<div
class="oh-modal"
id="editModal2"
role="dialog"
aria-labelledby="editDialogModal"
aria-hidden="true"
>
<div class="oh-modal__dialog oh-modal__dialog--timeoff">
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="editDialogDialog"
>{% trans "Assign Leave" %}</span
>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div id="assignLeaveForm"></div>
</div>
</div>
<script>
$(document).on('htmx:load', '#assignedLeaves', function () {
// Create a new script element
var scriptElement = document.createElement("script");
// Set the source URL of the script file to be loaded
scriptElement.src = "{% static 'build/js/web.frontend.min.js' %}";
// Append the script element to the head of the HTML document
document.head.appendChild(scriptElement);
});
$(document).on('htmx:load', '#assignLeaveForm', function () {
{% include 'select2.js' %}
$('#leaveTypeAssign #id_leave_type_id').select2();
$('#employeeAssign #id_employee_id').select2();
});
$('#delete-link').on('click', function (event) {
event.preventDefault(); // prevent the default behavior of the link
const link = $(this);
const confirmation = confirm("{% trans 'Are you sure you want to delete?' %}");
if (confirmation) {
window.location.href = link.attr('href'); // execute the href if confirmed
}
});
</script>
<script src="{% static '/base/filter.js' %}"></script>
<script src="{% static '/leave_assign/action.js' %}"></script>
{% endblock %}

View File

@@ -63,6 +63,7 @@
{% trans "Leave Types" %}
</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Company" %}</div>
{% if perms.leave.change_restrictleave or perms.leave.delete_restrictleave %}
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
{% endif %}
@@ -114,6 +115,9 @@
<div class="oh-sticky-table__td">
{{day.description}}
</div>
<div class="oh-sticky-table__td">
{{day.company_id}}
</div>
{% if perms.leave.change_restrictleave or perms.leave.delete_restrictleave %}
<div class="oh-sticky-table__td">
<div class="oh-btn-group">

View File

@@ -88,6 +88,14 @@
<label class="oh-label d-block" for="{{ form.description.id_for_label }}">{% trans "Description" %}</label>
{{form.description}} {{form.description.errors}}
<div class="row">
<div class="col-12 col-sm-12 col-md-6 col-lg-6">
<div class="oh-input__group">
<label class="oh-label d-block" for="{{ form.company_id.id_for_label }}">{% trans "Company" %}</label>
{{form.company_id}} {{form.company_id.errors}}
</div>
</div>
</div>
<div class="oh-modal__dialog-footer p-0 pt-2">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}

View File

@@ -87,6 +87,14 @@
<label class="oh-label d-block" for="{{ form.description.id_for_label }}">{% trans "Description" %}</label>
{{form.description}} {{form.description.errors}}
<div class="row">
<div class="col-12 col-sm-12 col-md-6 col-lg-6">
<div class="oh-input__group">
<label class="oh-label d-block" for="{{ form.company_id.id_for_label }}">{% trans "Company" %}</label>
{{form.company_id}} {{form.company_id.errors}}
</div>
</div>
</div>
<div class="oh-modal__dialog-footer p-0 pt-4">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}