[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 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.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 pms import models
|
||||
from pms.filters import BonusPointSettingFilter, EmployeeBonusPointFilter
|
||||
from pms.forms import (
|
||||
BonusPointSettingForm,
|
||||
BulkFeedbackForm,
|
||||
EmployeeBonusPointForm,
|
||||
EmployeeFeedbackForm,
|
||||
FeedbackForm,
|
||||
)
|
||||
from pms.methods import check_duplication
|
||||
|
||||
@@ -275,10 +277,7 @@ class EmployeeBonusPointListView(views.HorillaListView):
|
||||
####################### Feedback ########################################
|
||||
|
||||
|
||||
# @method_decorator(login_required, name="dispatch")
|
||||
# @method_decorator(
|
||||
# permission_required("pms.change_feedback"), name="dispatch"
|
||||
# )
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class FeedbackEmployeeFormView(views.HorillaFormView):
|
||||
"""
|
||||
Feedback other employee form View
|
||||
@@ -301,7 +300,7 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
|
||||
)
|
||||
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():
|
||||
message = "Feedback request sent."
|
||||
other_employees = check_duplication(
|
||||
@@ -312,3 +311,87 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
|
||||
messages.success(self.request, _trans(message))
|
||||
return self.HttpResponse("<script>window.location.reload()</script>")
|
||||
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
|
||||
|
||||
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?",
|
||||
};
|
||||
|
||||
var deleteMessages = {
|
||||
var deleteFeedbackMessages = {
|
||||
ar: "هل ترغب حقاً في حذف كل التعليقات المحددة؟",
|
||||
de: "Möchten Sie wirklich alle ausgewählten Rückmeldungen löschen?",
|
||||
es: "¿Realmente quieres eliminar todas las retroalimentaciones seleccionadas?",
|
||||
@@ -219,7 +219,7 @@ $("#deleteFeedback").click(function (e) {
|
||||
var languageCode = null;
|
||||
getCurrentLanguageCode(function (code) {
|
||||
languageCode = code;
|
||||
var confirmMessage = deleteMessages[languageCode];
|
||||
var confirmMessage = deleteFeedbackMessages[languageCode];
|
||||
var textMessage = norowMessages[languageCode];
|
||||
var checkedRows = $(".feedback-checkbox").filter(":checked");
|
||||
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-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 -->
|
||||
{% if perms.pms.add_feedback or request.user|filtersubordinates %}
|
||||
<div class="oh-btn-group ml-2">
|
||||
|
||||
@@ -146,6 +146,19 @@
|
||||
style="display: none;"
|
||||
>
|
||||
<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">
|
||||
<a href="#" class="oh-dropdown__link " id="archiveFeedback"
|
||||
@@ -231,9 +244,9 @@
|
||||
<script src="{% static '/base/filter.js' %}"></script>
|
||||
<script>
|
||||
|
||||
$(".fil").click(function() {
|
||||
$("#filterCount").text("(1)");
|
||||
});
|
||||
$(".fil").click(function() {
|
||||
$("#filterCount").text("(1)");
|
||||
});
|
||||
|
||||
</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"
|
||||
),
|
||||
path("feedback-creation", views.feedback_creation, name="feedback-creation"),
|
||||
# path(
|
||||
# "feedback-creation-ajax",
|
||||
# views.feedback_creation_ajax,
|
||||
# name="feedback-creation-ajax",
|
||||
# ),
|
||||
path(
|
||||
"bulk-feedback-create",
|
||||
cbvs.BulkFeedbackFormView.as_view(),
|
||||
name="bulk-feedback-create",
|
||||
),
|
||||
path("feedback-update/<int:id>", views.feedback_update, name="feedback-update"),
|
||||
path("feedback-delete/<int:id>", views.feedback_delete, name="feedback-delete"),
|
||||
path("feedback-archive/<int:id>", views.feedback_archive, name="feedback-archive"),
|
||||
|
||||
Reference in New Issue
Block a user