[UPDT] OFFBOARDING: Notification and message while adding employee and changing status and stage

This commit is contained in:
Horilla
2024-01-24 12:27:28 +05:30
parent 5a9a066e74
commit bc3d486f09
4 changed files with 190 additions and 101 deletions

View File

@@ -165,7 +165,8 @@ class TaskForm(ModelForm):
verbose_name = "Offboarding Task"
tasks_to = forms.ModelMultipleChoiceField(
queryset=OffboardingEmployee.objects.all()
queryset=OffboardingEmployee.objects.all(),
required=False,
)
class Meta:
@@ -179,6 +180,8 @@ class TaskForm(ModelForm):
super().__init__(*args, **kwargs)
self.fields["stage_id"].empty_label = "All Stages in Offboarding"
self.fields["managers"].empty_label = None
queryset = OffboardingEmployee.objects.filter(stage_id__offboarding_id=OffboardingStage.objects.filter(id=self.initial.get("stage_id")).first().offboarding_id)
self.fields["tasks_to"].queryset = queryset
def as_p(self):
"""
@@ -192,7 +195,6 @@ class TaskForm(ModelForm):
super().save(commit)
if commit:
employees = self.cleaned_data["tasks_to"]
print(employees)
for employee in employees:
assinged_task = EmployeeTask.objects.get_or_create(
employee_id=employee,

View File

@@ -6,6 +6,9 @@ from django.dispatch import receiver
from base import thread_local_middleware
from employee.models import Employee
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
from notifications.signals import notify
from django.contrib.auth.models import User
from base.thread_local_middleware import _thread_locals
# Create your models here.
@@ -22,6 +25,9 @@ class Offboarding(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=10, default="ongoing", choices=statuses)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.title
class OffboardingStage(models.Model):
@@ -150,6 +156,21 @@ class EmployeeTask(models.Model):
class Meta:
unique_together = ["employee_id", "task_id"]
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
request = getattr(_thread_locals, "request", None)
notify.send(
request.user.employee_get,
recipient=self.employee_id.employee_id.employee_user_id,
verb=f'Offboarding task "{self.task_id.title}" has been assiged',
verb_ar=f"",
verb_de=f"",
verb_es=f"",
verb_fr=f"",
redirect="offboarding/offboarding-pipeline",
icon="information",
)
class ExitReason(models.Model):

View File

@@ -1,82 +1,92 @@
{% load i18n offboarding_filter %}
{% for employee in stage.offboardingemployee_set.all %}
<div class="oh-sticky-table__tr oh-multiple-table-sort__movable" data-employee="{{employee.employee_id.get_full_name}}" data-employee-id="{{ employee.id }}">
<div class="oh-sticky-table__sd">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{ employee.employee_id.get_avatar }}" class="oh-profile__image" />
</div>
<span class="oh-profile__name oh-text--dark">{{ employee.employee_id.get_full_name }}</span>
</div>
</div>
<div class="oh-sticky-table__td">
{% trans 'In' %} {{ employee.notice_period }}
{{ employee.get_unit_display }}
</div>
<div class="oh-sticky-table__td">{{ employee.notice_period_starts }}</div>
<div class="oh-sticky-table__td">{{ employee.notice_period_ends }}</div>
<div class="oh-sticky-table__td">
<form hx-get="{% url "offboarding-change-stage" %}?employee_ids={{employee.id}}"
hx-target="#offboardingBody{{offboarding.id}}">
{{ stage_forms|stages:stage }}
<input type="submit" hidden>
</form>
</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
<button type="button" hx-get="{% url 'send-mail-employee' employee.employee_id.id %}"
title="{% trans 'Send Mail' %}" hx-target="#offboardingModalBody" class="oh-btn oh-btn--light"
data-toggle="oh-modal-toggle" data-target="#offboardingModal"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon
name="mail-open-outline"></ion-icon></button>
<button type="button" title="{% trans 'Notes' %}" class="oh-btn oh-btn--light oh-activity-sidebar__open"
data-target="#activitySidebar" hx-get="{% url 'view-offboarding-note' %}?employee_id={{ employee.id }}"
hx-target="#noteContainer" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon
name="newspaper-outline"></ion-icon>
</button>
{% if not employee.employee_id.is_active %}
<a type="button" href="{% url 'employee-archive' employee.employee_id.id %}" title="{% trans 'Un Archive' %}"
class="oh-btn oh-btn--light tex-primary"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon name="archive"></ion-icon></a>
{% else %}
<a type="button" hx-target="#offboardingModalBody" data-toggle="oh-modal-toggle" data-target="#offboardingModal" hx-get="{% url "add-employee" %}?instance_id={{employee.id}}&stage_id={{stage.id}}" title="{% trans 'Edit' %}"
class="oh-btn oh-btn--light tex-primary"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon name="create-outline"></ion-icon></a>
{% endif %}
{% if perms.offboarding.delete_offboardingemployee %}
<a type="button" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;" title="{% trans 'Delete' %}" onclick="return confirm('Do you want to delete this offboarding user?')" class="oh-btn oh-btn--light" href="{% url "delete-offboarding-employee" %}"><ion-icon
name="trash-outline"></ion-icon>
</a>
{% endif %}
</div>
</div>
{% for task in stage.offboardingtask_set.all %}
<div class="oh-sticky-table__td">
{% if task|have_task:employee %}
{% for assinged_tasks in employee|get_assigned_task:task %}
<select hx-get="{% url "update-task-status" %}?stage_id={{stage.id}}&employee_ids={{employee.id}}&task_id={{assinged_tasks.task_id.id}}"
hx-target="#offboardingBody{{offboarding.id}}" name="task_status" id="task_status{{assinged_tasks.id}}"
class="oh-select-custom w-100">
{% for assinged_task in assinged_tasks.statuses %}
{% if assinged_tasks.status == assinged_task.0 %}
<option value="{{ assinged_task.0 }}" selected>{{ assinged_task.1 }}</option>
{% else %}
<option value="{{ assinged_task.0 }}">{{ assinged_task.1 }}</option>
{% endif %}
{% endfor %}
</select>
{% endfor %}
{% else %}
<button hx-get="{% url "offboarding-assign-task" %}?employee_ids={{employee.id}}&task_id={{task.id}}"
hx-target="#offboardingBody{{offboarding.id}}" class="oh-checkpoint-badge text-info"
data-toggle="oh-modal-toggle">{% trans 'Assign' %}</button>
{% endif %}
</div>
{% endfor %}
<div class="oh-sticky-table__td"></div>
{% if response_message %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--success">
{{response_message}}
</div>
</div>
{% endif %}
{% for employee in stage.offboardingemployee_set.all %}
{% if perms.offboarding.view_offboarding or request.user.employee_get|is_in_managers:offboarding or request.user.employee_get|is_in_managers:stage or request.user.employee_get|is_in_managers:employee %}
<div class="oh-sticky-table__tr oh-multiple-table-sort__movable" data-employee="{{employee.employee_id.get_full_name}}" data-employee-id="{{ employee.id }}">
<div class="oh-sticky-table__sd">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{ employee.employee_id.get_avatar }}" class="oh-profile__image" />
</div>
<span class="oh-profile__name oh-text--dark">{{ employee.employee_id.get_full_name }}</span>
</div>
</div>
<div class="oh-sticky-table__td">
{% trans 'In' %} {{ employee.notice_period }}
{{ employee.get_unit_display }}
</div>
<div class="oh-sticky-table__td">{{ employee.notice_period_starts }}</div>
<div class="oh-sticky-table__td">{{ employee.notice_period_ends }}</div>
{% if request.user.employee_get|is_any_stage_manager or perms.offboarding.change_offboarding or perms.offboarding.change_offboardingemployee %}
<div class="oh-sticky-table__td">
<form hx-get="{% url "offboarding-change-stage" %}?employee_ids={{employee.id}}"
hx-target="#offboardingBody{{offboarding.id}}">
{{ stage_forms|stages:stage }}
<input type="submit" hidden>
</form>
</div>
{% endif %}
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
<button type="button" hx-get="{% url 'send-mail-employee' employee.employee_id.id %}"
title="{% trans 'Send Mail' %}" hx-target="#offboardingModalBody" class="oh-btn oh-btn--light"
data-toggle="oh-modal-toggle" data-target="#offboardingModal"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon
name="mail-open-outline"></ion-icon></button>
<button type="button" title="{% trans 'Notes' %}" class="oh-btn oh-btn--light oh-activity-sidebar__open"
data-target="#activitySidebar" hx-get="{% url 'view-offboarding-note' %}?employee_id={{ employee.id }}"
hx-target="#noteContainer" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon
name="newspaper-outline"></ion-icon>
</button>
{% if not employee.employee_id.is_active %}
<a type="button" href="{% url 'employee-archive' employee.employee_id.id %}" title="{% trans 'Un Archive' %}"
class="oh-btn oh-btn--light tex-primary"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon name="archive"></ion-icon></a>
{% else %}
<a type="button" hx-target="#offboardingModalBody" data-toggle="oh-modal-toggle" data-target="#offboardingModal" hx-get="{% url "add-employee" %}?instance_id={{employee.id}}&stage_id={{stage.id}}" title="{% trans 'Edit' %}"
class="oh-btn oh-btn--light tex-primary"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;"><ion-icon name="create-outline"></ion-icon></a>
{% endif %}
{% if perms.offboarding.delete_offboardingemployee %}
<a type="button" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;" title="{% trans 'Delete' %}" onclick="return confirm('Do you want to delete this offboarding user?')" class="oh-btn oh-btn--light" href="{% url "delete-offboarding-employee" %}?employee_ids={{employee.id}}"><ion-icon
name="trash-outline"></ion-icon>
</a>
{% endif %}
</div>
</div>
{% for task in stage.offboardingtask_set.all %}
<div class="oh-sticky-table__td">
{% if task|have_task:employee %}
{% for assinged_tasks in employee|get_assigned_task:task %}
<select hx-get="{% url "update-task-status" %}?stage_id={{stage.id}}&employee_ids={{employee.id}}&task_id={{assinged_tasks.task_id.id}}"
hx-target="#offboardingBody{{offboarding.id}}" name="task_status" id="task_status{{assinged_tasks.id}}"
class="oh-select-custom w-100">
{% for assinged_task in assinged_tasks.statuses %}
{% if assinged_tasks.status == assinged_task.0 %}
<option value="{{ assinged_task.0 }}" selected>{{ assinged_task.1 }}</option>
{% else %}
<option value="{{ assinged_task.0 }}">{{ assinged_task.1 }}</option>
{% endif %}
{% endfor %}
</select>
{% endfor %}
{% else %}
<button hx-get="{% url "offboarding-assign-task" %}?employee_ids={{employee.id}}&task_id={{task.id}}"
hx-target="#offboardingBody{{offboarding.id}}" class="oh-checkpoint-badge text-info"
data-toggle="oh-modal-toggle">{% trans 'Assign' %}</button>
{% endif %}
</div>
{% endfor %}
<div class="oh-sticky-table__td"></div>
</div>
{% endif %}
{% endfor %}
<script>
$("[data-archive-stage=true]").find(".oh-sticky-table__tr[data-employee-id]").hide();

View File

@@ -1,9 +1,14 @@
from django.http import HttpResponse, JsonResponse
from django.shortcuts import redirect, render
from django.contrib import messages
from django.contrib.auth.models import User
from employee.models import Employee
from horilla.decorators import login_required, permission_required
from offboarding.decorators import any_manager_can_enter, offboarding_manager_can_enter, offboarding_or_stage_manager_can_enter
from offboarding.decorators import (
any_manager_can_enter,
offboarding_manager_can_enter,
offboarding_or_stage_manager_can_enter,
)
from offboarding.forms import (
NoteForm,
OffboardingEmployeeForm,
@@ -21,13 +26,17 @@ from offboarding.models import (
OffboardingStageMultipleFile,
OffboardingTask,
)
from notifications.signals import notify
from django.utils.translation import gettext_lazy as _
# Create your views here.
@login_required
@any_manager_can_enter("offboarding.view_offboarding")
@any_manager_can_enter(
"offboarding.view_offboarding", offboarding_employee_can_enter=True
)
def pipeline(request):
"""
Offboarding pipleine view
@@ -58,7 +67,7 @@ def create_offboarding(request):
form = OffboardingForm(request.POST, instance=instance)
if form.is_valid():
form.save()
messages.success(request, "Offboarding saved")
messages.success(request, _("Offboarding saved"))
return HttpResponse("<script>window.location.reload()</script>")
return render(
@@ -78,7 +87,7 @@ def delete_offboarding(request):
"""
ids = request.GET.getlits("id")
Offboarding.objects.filter(id__in=ids).delete()
messages.success(request, "Offboarding deleted")
messages.success(request, _("Offboarding deleted"))
return redirect(pipeline)
@@ -103,7 +112,7 @@ def create_stage(request):
instance.offboarding_id = offboarding
instance.save()
instance.managers.set(form.data.getlist("managers"))
messages.success(request, "Stage saved")
messages.success(request, _("Stage saved"))
return HttpResponse("<script>window.location.reload()</script>")
return render(request, "offboarding/stage/form.html", {"form": form})
@@ -123,15 +132,24 @@ def add_employee(request):
form = OffboardingEmployeeForm(initial={"stage_id": stage}, instance=instance)
form.instance.stage_id = stage
if request.method == "POST":
form = OffboardingEmployeeForm(
request.POST,
instance=instance
)
form = OffboardingEmployeeForm(request.POST, instance=instance)
if form.is_valid():
instance = form.save(commit=False)
instance.stage_id = stage
instance.save()
messages.success(request, "Employee added to the stage")
messages.success(request, _("Employee added to the stage"))
if not instance_id:
notify.send(
request.user.employee_get,
recipient=instance.employee_id.employee_user_id,
verb=f"You have been added to the {stage} of {stage.offboarding_id}",
verb_ar=f"",
verb_de=f"",
verb_es=f"",
verb_fr=f"",
redirect="offboarding/offboarding-pipeline",
icon="information",
)
return HttpResponse("<script>window.location.reload()</script>")
return render(request, "offboarding/employee/form.html", {"form": form})
@@ -143,8 +161,20 @@ def delete_employee(request):
This method is used to delete the offboarding employee
"""
employee_ids = request.GET.getlist("employee_ids")
instances = OffboardingEmployee.objects.filter(id__in=employee_ids)
OffboardingEmployee.objects.filter(id__in=employee_ids).delete()
messages.success(request, "Offboarding employee deleted")
messages.success(request, _("Offboarding employee deleted"))
notify.send(
request.user.employee_get,
recipient=User.objects.filter(id__in=instances.values_list("employee_id__employee_user_id",flat=True)),
verb=f"You have been removed from the offboarding",
verb_ar=f"",
verb_de=f"",
verb_es=f"",
verb_fr=f"",
redirect="offboarding/offboarding-pipeline",
icon="information",
)
return redirect(pipeline)
@@ -156,7 +186,7 @@ def delete_stage(request):
"""
ids = request.GET.getlist("ids")
OffboardingStage.objects.filter(id__in=ids).delete()
messages.success(request, "Stage deleted")
messages.success(request, _("Stage deleted"))
return redirect(pipeline)
@@ -183,15 +213,26 @@ def change_stage(request):
stage_forms[str(stage.offboarding_id.id)] = StageSelectForm(
offboarding=stage.offboarding_id
)
notify.send(
request.user.employee_get,
recipient=User.objects.filter(id__in=employees.values_list("employee_id__employee_user_id",flat=True)),
verb=f"Offboarding stage has been changed",
verb_ar=f"",
verb_de=f"",
verb_es=f"",
verb_fr=f"",
redirect="offboarding/offboarding-pipeline",
icon="information",
)
return render(
request,
"offboarding/stage/offboarding_body.html",
{"offboarding": stage.offboarding_id, "stage_forms": stage_forms},
{"offboarding": stage.offboarding_id, "stage_forms": stage_forms,"response_message":_("stage changed successfully.")},
)
@login_required
@any_manager_can_enter("offboarding.view_offboardingnote")
@any_manager_can_enter("offboarding.view_offboardingnote",offboarding_employee_can_enter=True)
def view_notes(request):
"""
This method is used to render all the notes of the employee
@@ -288,17 +329,21 @@ def add_task(request):
instance=instance,
)
if request.method == "POST":
form = TaskForm(request.POST, instance=instance)
form = TaskForm(request.POST, instance=instance,initial={
"stage_id": stage_id,
})
if form.is_valid():
form.save()
messages.success(request, "Task Added")
return HttpResponse("<script>window.location.reload()</script>")
return render(request, "offboarding/task/form.html", {"form": form})
return render(request, "offboarding/task/form.html", {"form": form,})
@login_required
@any_manager_can_enter("offboarding.change_employeetask")
def update_task_status(request):
@any_manager_can_enter(
"offboarding.change_employeetask", offboarding_employee_can_enter=True
)
def update_task_status(request,*args, **kwargs):
"""
This method is used to update the assigned tasks status
"""
@@ -306,10 +351,21 @@ def update_task_status(request):
employee_ids = request.GET.getlist("employee_ids")
task_id = request.GET["task_id"]
status = request.GET["task_status"]
EmployeeTask.objects.filter(
employee_task = EmployeeTask.objects.filter(
employee_id__id__in=employee_ids, task_id__id=task_id
).update(status=status)
)
employee_task.update(status=status)
notify.send(
request.user.employee_get,
recipient=User.objects.filter(id__in=employee_task.values_list("task_id__managers__employee_user_id",flat=True)),
verb=f"Offboarding Task status has been updated",
verb_ar=f"",
verb_de=f"",
verb_es=f"",
verb_fr=f"",
redirect="offboarding/offboarding-pipeline",
icon="information",
)
stage = OffboardingStage.objects.get(id=stage_id)
stage_forms = {}
stage_forms[str(stage.offboarding_id.id)] = StageSelectForm(
@@ -318,7 +374,7 @@ def update_task_status(request):
return render(
request,
"offboarding/stage/offboarding_body.html",
{"offboarding": stage.offboarding_id, "stage_forms": stage_forms},
{"offboarding": stage.offboarding_id, "stage_forms": stage_forms,"response_message": _("Task status changed successfully.")},
)