[ADD] PMS: Added bulk feedback create feature
This commit is contained in:
97
pms/cbvs.py
97
pms/cbvs.py
@@ -8,16 +8,18 @@ from django.utils.decorators import method_decorator
|
|||||||
from django.utils.translation import gettext_lazy as _trans
|
from django.utils.translation import gettext_lazy as _trans
|
||||||
|
|
||||||
from base.methods import filter_own_and_subordinate_recordes, is_reportingmanager
|
from base.methods import filter_own_and_subordinate_recordes, is_reportingmanager
|
||||||
from employee.forms import EmployeeForm
|
from employee.models import Employee
|
||||||
from horilla import horilla_middlewares
|
from horilla import horilla_middlewares
|
||||||
from horilla.decorators import login_required, permission_required
|
from horilla.decorators import login_required, owner_can_enter, permission_required
|
||||||
from horilla_views.generic.cbv import views
|
from horilla_views.generic.cbv import views
|
||||||
from pms import models
|
from pms import models
|
||||||
from pms.filters import BonusPointSettingFilter, EmployeeBonusPointFilter
|
from pms.filters import BonusPointSettingFilter, EmployeeBonusPointFilter
|
||||||
from pms.forms import (
|
from pms.forms import (
|
||||||
BonusPointSettingForm,
|
BonusPointSettingForm,
|
||||||
|
BulkFeedbackForm,
|
||||||
EmployeeBonusPointForm,
|
EmployeeBonusPointForm,
|
||||||
EmployeeFeedbackForm,
|
EmployeeFeedbackForm,
|
||||||
|
FeedbackForm,
|
||||||
)
|
)
|
||||||
from pms.methods import check_duplication
|
from pms.methods import check_duplication
|
||||||
|
|
||||||
@@ -275,10 +277,7 @@ class EmployeeBonusPointListView(views.HorillaListView):
|
|||||||
####################### Feedback ########################################
|
####################### Feedback ########################################
|
||||||
|
|
||||||
|
|
||||||
# @method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
# @method_decorator(
|
|
||||||
# permission_required("pms.change_feedback"), name="dispatch"
|
|
||||||
# )
|
|
||||||
class FeedbackEmployeeFormView(views.HorillaFormView):
|
class FeedbackEmployeeFormView(views.HorillaFormView):
|
||||||
"""
|
"""
|
||||||
Feedback other employee form View
|
Feedback other employee form View
|
||||||
@@ -301,7 +300,7 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
|
|||||||
)
|
)
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
def form_valid(self, form: EmployeeForm) -> views.HttpResponse:
|
def form_valid(self, form: EmployeeFeedbackForm) -> views.HttpResponse:
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
message = "Feedback request sent."
|
message = "Feedback request sent."
|
||||||
other_employees = check_duplication(
|
other_employees = check_duplication(
|
||||||
@@ -312,3 +311,87 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
|
|||||||
messages.success(self.request, _trans(message))
|
messages.success(self.request, _trans(message))
|
||||||
return self.HttpResponse("<script>window.location.reload()</script>")
|
return self.HttpResponse("<script>window.location.reload()</script>")
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@method_decorator(permission_required("pms.add_feedback"), name="dispatch")
|
||||||
|
class BulkFeedbackFormView(views.HorillaFormView):
|
||||||
|
"""
|
||||||
|
Feedback other employee form View
|
||||||
|
"""
|
||||||
|
|
||||||
|
form_class = BulkFeedbackForm
|
||||||
|
model = models.Feedback
|
||||||
|
view_id = "BulkFeedbackForm"
|
||||||
|
new_display_title = _trans("Bulk Feedback request ")
|
||||||
|
template_name = "feedback/bulk_feedback_form.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_invalid(self, form: Any) -> HttpResponse:
|
||||||
|
if not form.is_valid():
|
||||||
|
errors = form.errors.as_data()
|
||||||
|
return render(
|
||||||
|
self.request, self.template_name, {"form": form, "errors": errors}
|
||||||
|
)
|
||||||
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
def form_valid(self, form: BulkFeedbackForm) -> views.HttpResponse:
|
||||||
|
if form.is_valid():
|
||||||
|
message = "Bulk Feedback request sent."
|
||||||
|
cleaned_data = form.cleaned_data
|
||||||
|
employees = cleaned_data["employee_ids"]
|
||||||
|
for employee in employees:
|
||||||
|
reporting_manager = employee.get_reporting_manager()
|
||||||
|
manager_id = (
|
||||||
|
reporting_manager if cleaned_data["include_manager"] else None
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
"review_cycle": f"{cleaned_data['title']}-{employee} feedback",
|
||||||
|
"employee_id": employee,
|
||||||
|
"manager_id": manager_id,
|
||||||
|
"question_template_id": cleaned_data["question_template_id"],
|
||||||
|
"start_date": cleaned_data["start_date"],
|
||||||
|
"end_date": cleaned_data["end_date"],
|
||||||
|
"cyclic_feedback": cleaned_data["cyclic_feedback"],
|
||||||
|
"cyclic_feedback_days_count": cleaned_data[
|
||||||
|
"cyclic_feedback_days_count"
|
||||||
|
],
|
||||||
|
"cyclic_feedback_period": cleaned_data["cyclic_feedback_period"],
|
||||||
|
"status": cleaned_data["status"],
|
||||||
|
}
|
||||||
|
feedback_form = FeedbackForm(data)
|
||||||
|
if feedback_form.is_valid():
|
||||||
|
feedback = feedback_form.save()
|
||||||
|
if cleaned_data["include_keyresult"]:
|
||||||
|
keyresults = models.EmployeeKeyResult.objects.filter(
|
||||||
|
employee_objective_id__employee_id=employee.id
|
||||||
|
)
|
||||||
|
feedback.employee_key_results_id.add(*keyresults)
|
||||||
|
if cleaned_data["include_colleagues"]:
|
||||||
|
department = employee.get_department()
|
||||||
|
# employee ids to exclude from collegue list
|
||||||
|
exclude_ids = [employee.id]
|
||||||
|
if reporting_manager:
|
||||||
|
exclude_ids.append(reporting_manager.id)
|
||||||
|
# Get employees in the same department as the employee
|
||||||
|
colleagues = Employee.objects.filter(
|
||||||
|
is_active=True, employee_work_info__department_id=department
|
||||||
|
).exclude(id__in=exclude_ids)
|
||||||
|
feedback.colleague_id.add(*colleagues)
|
||||||
|
|
||||||
|
if cleaned_data["include_subordinates"]:
|
||||||
|
subordinates = Employee.objects.filter(
|
||||||
|
is_active=True,
|
||||||
|
employee_work_info__reporting_manager_id=employee,
|
||||||
|
)
|
||||||
|
feedback.subordinate_id.add(*subordinates)
|
||||||
|
other_employees = check_duplication(
|
||||||
|
feedback, cleaned_data["other_employees"]
|
||||||
|
)
|
||||||
|
feedback.others_id.add(*other_employees)
|
||||||
|
messages.success(self.request, _trans(message))
|
||||||
|
return self.HttpResponse("<script>window.location.reload()</script>")
|
||||||
|
return super().form_valid(form)
|
||||||
|
|||||||
100
pms/forms.py
100
pms/forms.py
@@ -1201,3 +1201,103 @@ class EmployeeFeedbackForm(HorillaModelForm):
|
|||||||
cleaned_data["others_id"] = employee_data
|
cleaned_data["others_id"] = employee_data
|
||||||
|
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
class BulkFeedbackForm(HorillaModelForm):
|
||||||
|
"""Form for creating feedback in bulk"""
|
||||||
|
|
||||||
|
title = forms.CharField(required=True, label=_("Title"))
|
||||||
|
employee_ids = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=Employee.objects.filter(is_active=True), required=True
|
||||||
|
)
|
||||||
|
other_employees = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=Employee.objects.filter(is_active=True),
|
||||||
|
required=False,
|
||||||
|
label=_("Other employees"),
|
||||||
|
help_text=_("Employees need to sent feedback request."),
|
||||||
|
)
|
||||||
|
include_manager = forms.BooleanField(
|
||||||
|
initial=True,
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
include_subordinates = forms.BooleanField(
|
||||||
|
initial=True, required=False, label=_("Include all subordinates")
|
||||||
|
)
|
||||||
|
include_colleagues = forms.BooleanField(
|
||||||
|
initial=True, required=False, label=_("Include all colleagues")
|
||||||
|
)
|
||||||
|
include_keyresult = forms.BooleanField(
|
||||||
|
initial=True,
|
||||||
|
required=False,
|
||||||
|
label=_("Include all keyresults"),
|
||||||
|
help_text=_("Include all keyresults assigned to the employee."),
|
||||||
|
)
|
||||||
|
period = forms.ModelChoiceField(
|
||||||
|
queryset=Period.objects.all(),
|
||||||
|
label=_("Period"),
|
||||||
|
required=False,
|
||||||
|
widget=forms.Select(
|
||||||
|
attrs={
|
||||||
|
"onchange": "periodChange($(this))",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Feedback
|
||||||
|
fields = [
|
||||||
|
"title",
|
||||||
|
"employee_ids",
|
||||||
|
"status",
|
||||||
|
"other_employees",
|
||||||
|
"include_manager",
|
||||||
|
"include_subordinates",
|
||||||
|
"include_colleagues",
|
||||||
|
"include_keyresult",
|
||||||
|
"question_template_id",
|
||||||
|
"cyclic_feedback",
|
||||||
|
"cyclic_feedback_period",
|
||||||
|
"cyclic_feedback_days_count",
|
||||||
|
"period",
|
||||||
|
"start_date",
|
||||||
|
"end_date",
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
"start_date": forms.DateInput(
|
||||||
|
attrs={"type": "date", "class": "oh-input w-100"}
|
||||||
|
),
|
||||||
|
"end_date": forms.DateInput(
|
||||||
|
attrs={"type": "date", "class": "oh-input w-100"}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields["employee_ids"] = HorillaMultiSelectField(
|
||||||
|
queryset=Employee.objects.filter(employee_work_info__isnull=False),
|
||||||
|
widget=HorillaMultiSelectWidget(
|
||||||
|
filter_route_name="employee-widget-filter",
|
||||||
|
filter_class=EmployeeFilter,
|
||||||
|
filter_instance_contex_name="f",
|
||||||
|
filter_template_path="employee_filters.html",
|
||||||
|
form=self,
|
||||||
|
instance=self.instance,
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
label=_("Employees"),
|
||||||
|
)
|
||||||
|
self.fields["status"].initial = "Not Started"
|
||||||
|
self.fields["cyclic_feedback"].widget.attrs["onchange"] = "cyclicFeedback()"
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
if isinstance(self.fields["employee_ids"], HorillaMultiSelectField):
|
||||||
|
self.errors.pop("employee_ids", None)
|
||||||
|
|
||||||
|
employee_data = self.fields["employee_ids"].queryset.filter(
|
||||||
|
id__in=self.data.getlist("employee_ids")
|
||||||
|
)
|
||||||
|
|
||||||
|
cleaned_data["employee_ids"] = employee_data
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ var unarchiveMessages = {
|
|||||||
fr: "Voulez-vous vraiment désarchiver tous les retours sélectionnés?",
|
fr: "Voulez-vous vraiment désarchiver tous les retours sélectionnés?",
|
||||||
};
|
};
|
||||||
|
|
||||||
var deleteMessages = {
|
var deleteFeedbackMessages = {
|
||||||
ar: "هل ترغب حقاً في حذف كل التعليقات المحددة؟",
|
ar: "هل ترغب حقاً في حذف كل التعليقات المحددة؟",
|
||||||
de: "Möchten Sie wirklich alle ausgewählten Rückmeldungen löschen?",
|
de: "Möchten Sie wirklich alle ausgewählten Rückmeldungen löschen?",
|
||||||
es: "¿Realmente quieres eliminar todas las retroalimentaciones seleccionadas?",
|
es: "¿Realmente quieres eliminar todas las retroalimentaciones seleccionadas?",
|
||||||
@@ -219,7 +219,7 @@ $("#deleteFeedback").click(function (e) {
|
|||||||
var languageCode = null;
|
var languageCode = null;
|
||||||
getCurrentLanguageCode(function (code) {
|
getCurrentLanguageCode(function (code) {
|
||||||
languageCode = code;
|
languageCode = code;
|
||||||
var confirmMessage = deleteMessages[languageCode];
|
var confirmMessage = deleteFeedbackMessages[languageCode];
|
||||||
var textMessage = norowMessages[languageCode];
|
var textMessage = norowMessages[languageCode];
|
||||||
var checkedRows = $(".feedback-checkbox").filter(":checked");
|
var checkedRows = $(".feedback-checkbox").filter(":checked");
|
||||||
if (checkedRows.length === 0) {
|
if (checkedRows.length === 0) {
|
||||||
|
|||||||
50
pms/templates/feedback/bulk_feedback_form.html
Normal file
50
pms/templates/feedback/bulk_feedback_form.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{% load static %}
|
||||||
|
{% include "generic/horilla_form.html" %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#id_cyclic_feedback_period_parent_div').addClass('d-none')
|
||||||
|
$('#id_cyclic_feedback_days_count_parent_div').addClass('d-none')
|
||||||
|
})
|
||||||
|
// this function is used to generate csrf token
|
||||||
|
function getCookie(name) {
|
||||||
|
let cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== "") {
|
||||||
|
const cookies = document.cookie.split(";");
|
||||||
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
const cookie = cookies[i].trim();
|
||||||
|
// Does this cookie string begin with the name we want?
|
||||||
|
if (cookie.substring(0, name.length + 1) === (name + "=")) {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
function periodChange(element){
|
||||||
|
period_id = element.val()
|
||||||
|
$.ajax({
|
||||||
|
url: '/pms/period-change',
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: JSON.stringify(period_id),
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
"X-CSRFToken": getCookie("csrftoken"),
|
||||||
|
},
|
||||||
|
success: (data) => {
|
||||||
|
// Adding data to start and end date
|
||||||
|
$('#BulkFeedbackForm #id_start_date').val(data.start_date)
|
||||||
|
$('#BulkFeedbackForm #id_end_date').val(data.end_date);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
console.log('Error', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function cyclicFeedback(){
|
||||||
|
$('#id_cyclic_feedback_period_parent_div').toggleClass('d-none')
|
||||||
|
$('#id_cyclic_feedback_days_count_parent_div').toggleClass('d-none')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -21,7 +21,37 @@
|
|||||||
<div class="oh-main__titlebar oh-main__titlebar--right">
|
<div class="oh-main__titlebar oh-main__titlebar--right">
|
||||||
|
|
||||||
<div class="oh-main__titlebar-button-container">
|
<div class="oh-main__titlebar-button-container">
|
||||||
|
{% if perms.pms.add_feedback %}
|
||||||
|
<div class="oh-btn-group ml-2">
|
||||||
|
<div class="oh-dropdown" x-data="{open: false}">
|
||||||
|
<button
|
||||||
|
onclick="event.preventDefault()"
|
||||||
|
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
|
||||||
|
@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
|
||||||
|
data-toggle="oh-modal-toggle"
|
||||||
|
data-target="#objectCreateModal"
|
||||||
|
hx-get="{% url "bulk-feedback-create" %}"
|
||||||
|
hx-target="#objectCreateModalTarget"
|
||||||
|
class="oh-dropdown__link"
|
||||||
|
id="bulkfeedback"
|
||||||
|
>{% trans "Bulk Feedback" %}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<!-- checking user permission for objective creation -->
|
<!-- checking user permission for objective creation -->
|
||||||
{% if perms.pms.add_feedback or request.user|filtersubordinates %}
|
{% if perms.pms.add_feedback or request.user|filtersubordinates %}
|
||||||
<div class="oh-btn-group ml-2">
|
<div class="oh-btn-group ml-2">
|
||||||
|
|||||||
@@ -146,6 +146,19 @@
|
|||||||
style="display: none;"
|
style="display: none;"
|
||||||
>
|
>
|
||||||
<ul class="oh-dropdown__items">
|
<ul class="oh-dropdown__items">
|
||||||
|
{% if perms.pms.add_feedback %}
|
||||||
|
<li class="oh-dropdown__item">
|
||||||
|
<a
|
||||||
|
data-toggle="oh-modal-toggle"
|
||||||
|
data-target="#objectCreateModal"
|
||||||
|
hx-get="{% url "bulk-feedback-create" %}"
|
||||||
|
hx-target="#objectCreateModalTarget"
|
||||||
|
class="oh-dropdown__link"
|
||||||
|
id="bulkfeedback"
|
||||||
|
>{% trans "Bulk Feedback" %}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<li class="oh-dropdown__item">
|
<li class="oh-dropdown__item">
|
||||||
<a href="#" class="oh-dropdown__link " id="archiveFeedback"
|
<a href="#" class="oh-dropdown__link " id="archiveFeedback"
|
||||||
@@ -231,9 +244,9 @@
|
|||||||
<script src="{% static '/base/filter.js' %}"></script>
|
<script src="{% static '/base/filter.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
$(".fil").click(function() {
|
$(".fil").click(function() {
|
||||||
$("#filterCount").text("(1)");
|
$("#filterCount").text("(1)");
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
10
pms/urls.py
10
pms/urls.py
@@ -125,11 +125,11 @@ urlpatterns = [
|
|||||||
"feedback-list-search", views.feedback_list_search, name="feedback-list-search"
|
"feedback-list-search", views.feedback_list_search, name="feedback-list-search"
|
||||||
),
|
),
|
||||||
path("feedback-creation", views.feedback_creation, name="feedback-creation"),
|
path("feedback-creation", views.feedback_creation, name="feedback-creation"),
|
||||||
# path(
|
path(
|
||||||
# "feedback-creation-ajax",
|
"bulk-feedback-create",
|
||||||
# views.feedback_creation_ajax,
|
cbvs.BulkFeedbackFormView.as_view(),
|
||||||
# name="feedback-creation-ajax",
|
name="bulk-feedback-create",
|
||||||
# ),
|
),
|
||||||
path("feedback-update/<int:id>", views.feedback_update, name="feedback-update"),
|
path("feedback-update/<int:id>", views.feedback_update, name="feedback-update"),
|
||||||
path("feedback-delete/<int:id>", views.feedback_delete, name="feedback-delete"),
|
path("feedback-delete/<int:id>", views.feedback_delete, name="feedback-delete"),
|
||||||
path("feedback-archive/<int:id>", views.feedback_archive, name="feedback-archive"),
|
path("feedback-archive/<int:id>", views.feedback_archive, name="feedback-archive"),
|
||||||
|
|||||||
Reference in New Issue
Block a user