[UPDT] PAYROLL: Comment section updation in reimbursement,leave and bonus encashment request and updation with multiple file uploading comment both card and list view

This commit is contained in:
Horilla
2024-03-28 10:03:31 +05:30
parent 8b52c2b5d9
commit 0dd614db7a
9 changed files with 446 additions and 189 deletions

View File

@@ -3,17 +3,20 @@ forms.py
"""
from datetime import date
from typing import Any
from django import forms
from django.forms import widgets
from django.utils.translation import gettext_lazy as trans
from django.template.loader import render_to_string
from base import thread_local_middleware
from base.forms import Form
from employee.forms import MultipleFileField
from employee.models import Employee
from payroll.context_processors import get_active_employees
from payroll.models.models import (
EncashmentGeneralSettings,
PayrollGeneralSetting,
ReimbursementFile,
ReimbursementrequestComment,
WorkRecord,
)
@@ -176,7 +179,48 @@ class ReimbursementRequestCommentForm(ModelForm):
model = ReimbursementrequestComment
fields = ("comment",)
exclude = ["is_active"]
class reimbursementCommentForm(ModelForm):
"""
Reimbursement request comment model form
"""
verbose_name = "Add Comment"
class Meta:
model = ReimbursementrequestComment
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["files"] = MultipleFileField(label="files")
self.fields["files"].required = False
def as_p(self):
"""
Render the form fields as HTML table rows with Bootstrap styling.
"""
context = {"form": self}
table_html = render_to_string("common_form.html", context)
return table_html
def save(self, commit: bool = ...) -> Any:
multiple_files_ids = []
files = None
if self.files.getlist("files"):
files = self.files.getlist("files")
self.instance.attachemnt = files[0]
multiple_files_ids = []
for attachemnt in files:
file_instance = ReimbursementFile()
file_instance.file = attachemnt
file_instance.save()
multiple_files_ids.append(file_instance.pk)
instance = super().save(commit)
if commit:
instance.files.add(*multiple_files_ids)
return instance, files
class EncashmentGeneralSettingsForm(ModelForm):

View File

@@ -1757,6 +1757,11 @@ except:
pass
class ReimbursementFile(models.Model):
file = models.FileField(upload_to="payroll/request_files")
objects = models.Manager()
class ReimbursementrequestComment(HorillaModel):
"""
ReimbursementRequestComment Model
@@ -1765,6 +1770,12 @@ class ReimbursementrequestComment(HorillaModel):
request_id = models.ForeignKey(Reimbursement, on_delete=models.CASCADE)
employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE)
comment = models.TextField(null=True, verbose_name=_("Comment"), max_length=255)
files = models.ManyToManyField(ReimbursementFile, blank=True)
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created At"),
null=True,
)
def __str__(self) -> str:
return f"{self.comment}"

View File

@@ -1,7 +1,7 @@
{% load i18n %}
<div class="oh-main__titlebar-button-container">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open">
<button class="oh-btn ml-2" @click="open = !open" onclick="event.preventDefault()">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
</button>
<div

View File

@@ -0,0 +1,175 @@
{% load i18n static %}
<style>
#enlargeImageContainer {
position: absolute;
left: -300px;
top: 100px;
height: 200px;
width: 200px;
}
</style>
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">{{ message }}</div>
{% endfor %}
</div>
<script>
setTimeout(() => {
$(".oh-modal__close").click();
}, 1000);
</script>
{% endif %}
<div class="oh-activity-sidebar__header">
<a
style="cursor: pointer;"
onclick="$('.oh-activity-sidebar--show').removeClass('oh-activity-sidebar--show');">
<ion-icon
name="chevron-forward-outline"
class="oh-activity-sidebar__header-icon me-2 oh-activity-sidebar__close"
data-target="#activitySidebar"
></ion-icon>
</a>
<span class="oh-activity-sidebar__title"> {% trans "Comments" %} </span>
</div>
<form method="post"
hx-target="#commentContainer"
hx-post="{% url 'payroll-request-add-comment' request_id %}" id="commentForm">
{% csrf_token %}
<div>
<input type="text" name="comment" id="commentInput" class="oh-input w-100" placeholder="Comment here">
<button type="submit" id="commentButton" class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp" style="display: none;">
{% trans "Comment" %}
</button>
</div>
<div class="oh-inner-sidebar-content__footer"></div>
</form>
{% if comments %}
<ol class="oh-activity-sidebar__qa-list" role="list">
{% for comment in comments %}
<li class="oh-activity-sidebar__qa-item">
<span class="oh-activity-sidebar__q">{{ comment.comment }}
<span class="float-end" title="Delete" hx-get='{% url "payroll-request-delete-comment" comment.id %}' data-target="#activitySidebar" hx-swap="innerHTML" hx-target="#commentContainer">
<ion-icon name="close-outline" style="font-size: 24px" role="img" class="md hydrated" aria-label="close outline"></ion-icon></span>
</span>
<div class="d-flex mt-2 mb-2">
{% for file in comment.files.all %}
<a
href="{{ file.file.url }}"
rel="noopener noreferrer"
target="_blank"
><span
class="oh-file-icon oh-file-icon--pdf"
onmouseover="enlargeImage('{{ file.file.url }}',$(this))"
style="width: 40px; height: 40px"
><img
src="{% static 'images/ui/minus-icon.png' %}"
style="display: block; width: 50%; height: 50%"
hx-get="{% url 'delete-reimbursement-comment-file' %}?ids={{ file.id }}&payroll_id={{ request_id }}"
hx-target="#commentContainer"
onclick="event.stopPropagation();event.preventDefault()" /></span
></a>
{% endfor %}
<form
hx-post="{% url 'payroll-request-view-comment' comment.request_id.id %}?comment_id={{ comment.id }}"
class="add-files-form"
hx-encoding="multipart/form-data"
data-target="#activitySidebar" hx-swap="innerHTML" hx-target="#commentContainer"
>
{% csrf_token %}
<label for="addFile_{{comment.id}}" title="Add Files"
><ion-icon
name="add-outline"
style="font-size: 24px"
role="img"
class="md hydrated"
aria-label="add outline"
></ion-icon
></label>
<input
type="file"
name="files"
class="d-none"
multiple="true"
id="addFile_{{comment.id}}"
onchange="submitForm(this)"
/>
<input
type="submit"
class="d-none add_more_submit"
value="save"
/>
</form>
</div>
<span class="oh-activity-sidebar__a">
{% trans 'by' %}
<img
src="{{ note.note_by.get_avatar }}"
style="width: 1.5em; border-radius: 100%"
/>
{{ comment.employee_id.get_full_name }} @ {{comment.request_id.employee_id.get_full_name }}
{% trans "'s reimbursement request" %}
</span>
<div style="width: 50%">
<div id="enlargeImageContainer" class="enlargeImageContainer"></div>
</div>
</li>
{% endfor %}
</ol>
{% else %}
<div class="oh-timeoff-modal__profile-content">
<div class="oh-404">
<div class="">
<span class="oh-timeoff-title fw-bold" style="display: block"
>{% trans "There are no comments to show." %}</span
>
<img
style="display: block; width: 100px; margin: 20px auto"
src="/static/images/ui/comment.png"
class=""
/>
</div>
</div>
</div>
{% endif %}
<!-- start of comment modal -->
<div
class="oh-modal"
id="shiftcommentModal"
role="dialog"
aria-labelledby="emptagModal"
aria-hidden="true"
>
<div class="oh-modal__dialog" id="shiftRequestCommentForm">
</div>
</div>
<!-- end of comment modal -->
<script>
// Get references to the input field and comment button
const commentInput = document.getElementById('commentInput');
const commentButton = document.getElementById('commentButton');
// Add event listener to the input field
commentInput.addEventListener('input', function() {
// Show the comment button if the input field is not empty, hide it otherwise
if (commentInput.value.trim() !== '') {
commentButton.style.display = 'inline';
} else {
commentButton.style.display = 'none';
}
});
</script>

View File

@@ -110,6 +110,7 @@
>
{% trans "Description" %}
</div>
<div class="oh-sticky-table__th" style="width:115px;">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th"> {% trans "Actions" %} </div>
<div class="oh-sticky-table__th oh-sticky-table__right">{% trans "Confirmation" %}</div>
</div>
@@ -159,6 +160,12 @@
</div>
<div data-cell-index="5" class="oh-sticky-table__td">
{{req.description}}
</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<button type="button" hx-get="{% url 'payroll-request-view-comment' req.id %}" hx-target="#commentContainer"
data-target = '#activitySidebar' title="View Comment" class="oh-btn oh-btn--light oh-activity-sidebar__open w-100" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;" onclick="event.stopPropagation()">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
@@ -394,6 +401,7 @@
>
{% trans "Description" %}
</div>
<div class="oh-sticky-table__th" style="width:115px;">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th"> {% trans "Actions" %} </div>
<div class="oh-sticky-table__th oh-sticky-table__right" style="width:180px">{% trans "Confirmation" %}</div>
</div>
@@ -438,7 +446,7 @@
<div data-cell-index="13" class="oh-sticky-table__td">
<input type="number" required
default="0" min="0" placeholder="Amount"
name="amount" class="w-50"{% if req.status == "approved" %}disabled{% endif %} {% if req.amount %}
name="amount" class="w-75"{% if req.status == "approved" %}disabled{% endif %} {% if req.amount %}
value="{{req.amount}}"{% endif %} />
</div>
<div data-cell-index="14" class="oh-sticky-table__td">
@@ -455,7 +463,14 @@
</div>
<div data-cell-index="18" class="oh-sticky-table__td">
{{req.description}}
</div>
</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<button type="button" hx-get="{% url 'payroll-request-view-comment' req.id %}" hx-target="#commentContainer"
data-target = '#activitySidebar' title="View Comment" class="oh-btn oh-btn--light oh-activity-sidebar__open w-100" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;" onclick="event.stopPropagation()">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
{% if perms.payroll.change_reimbursement or request.user %}
@@ -684,6 +699,7 @@
>
{% trans "Description" %}
</div>
<div class="oh-sticky-table__th" style="width:115px;">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th"> {% trans "Actions" %} </div>
<div class="oh-sticky-table__th oh-sticky-table__right" style="width:180px"> {% trans "Confirmation" %} </div>
</div>
@@ -728,7 +744,7 @@
<div data-cell-index="23" class="oh-sticky-table__td">
<input type="number" required
default="0" min="0" placeholder="Amount"
name="amount" class="w-50"{% if req.status == "approved" %}disabled{% endif %} {% if req.amount %}
name="amount" class="w-75"{% if req.status == "approved" %}disabled{% endif %} {% if req.amount %}
value="{{req.amount}}"{% endif %} />
</div>
<div data-cell-index="24" class="oh-sticky-table__td">
@@ -739,7 +755,13 @@
</div>
<div data-cell-index="26" class="oh-sticky-table__td">
{{req.description}}
</div>
</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<button type="button" hx-get="{% url 'payroll-request-view-comment' req.id %}" hx-target="#commentContainer"
data-target = '#activitySidebar' title="View Comment" class="oh-btn oh-btn--light oh-activity-sidebar__open w-100" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;" onclick="event.stopPropagation()">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
{% if perms.payroll.change_reimbursement or request.user %}
@@ -908,55 +930,16 @@
</div>
<!-- end of tabs -->
<!-- Activty side bar for comment section-->
<!-- start of comment modal -->
<div
class="oh-modal"
id="shiftcommentModal"
role="dialog"
aria-labelledby="emptagModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="createModalTitle">
{% trans "Add Comment" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div
class="oh-modal__dialog-body"
id="shiftRequestCommentForm"
></div>
<div class="oh-activity-sidebar" id="activitySidebar" style="z-index:1000;">
<div class="oh-activity-sidebar__body" id="commentContainer">
</div>
</div>
<!-- end of comment modal -->
<div
class="oh-modal"
style="z-index: 60"
id="shiftRequestDetailModal"
role="dialog"
aria-labelledby="shiftRequestDetailModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="">
{% trans "Details" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div
class="oh-modal__dialog-body oh-modal__dialog-relative"
id="shiftRequestDetailTarget"
></div>
</div>
</div>
<!-- End of Activty side bar for comment section-->
<script>
function reimbursementConfirm(params, target, approve = false) {

View File

@@ -75,6 +75,8 @@
hx-get="{% url 'create-reimbursement' %}?instance_id={{ req.id }}"
hx-target="#objectCreateModalTarget"
class="mr-2"
style="cursor:pointer;"
title='{% trans "Update" %}'
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
><ion-icon
@@ -87,6 +89,7 @@
{% endif %}
{% if perms.payroll.delete_reimbursement %}
<a href="{% url "delete-reimbursement" %}?ids={{req.id}}" onclick="return confirm('Do you want to delete this record?')"
title='{% trans "Delete" %}'
><ion-icon
class="text-danger md hydrated"
name="trash-outline"
@@ -129,25 +132,12 @@
<div class="mb-2">
<i
><a
hx-target="#shiftRequestCommentForm"
hx-get="{% url 'payroll-request-add-comment' req.id %}"
class="text-primary"
hx-swap="innerHTML"
data-toggle="oh-modal-toggle"
data-target="#shiftcommentModal"
style="cursor: pointer"
>{% trans 'Add Comments' %}</a
>
&nbsp,
<a
hx-target="#shiftRequestDetailTarget"
hx-get="{% url 'payroll-request-view-comment' req.id %}"
class="text-primary"
data-toggle="oh-modal-toggle"
data-target="#shiftRequestDetailModal"
hx-target="#commentContainer"
data-target = '#activitySidebar'
class="text-primary fw-bold oh-activity-sidebar__open w-100"
style="cursor: pointer"
>{% trans 'View Comments' %}</a
>{% trans 'Comments' %}</a
></i
>
</div>
@@ -371,6 +361,8 @@
hx-get="{% url 'create-reimbursement' %}?instance_id={{ req.id }}"
hx-target="#objectCreateModalTarget"
class="mr-2"
style="cursor:pointer;"
title='{% trans "Update" %}'
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
><ion-icon
@@ -383,6 +375,7 @@
{% endif %}
{% if perms.payroll.delete_reimbursement %}
<a href="{% url "delete-reimbursement" %}?ids={{req.id}}" onclick="return confirm('Do you want to delete this record?')"
title='{% trans "Delete" %}'
><ion-icon
class="text-danger md hydrated"
name="trash-outline"
@@ -424,25 +417,12 @@
<div class="mb-2">
<i
><a
hx-target="#shiftRequestCommentForm"
hx-get="{% url 'payroll-request-add-comment' req.id %}"
class="text-primary"
hx-swap="innerHTML"
data-toggle="oh-modal-toggle"
data-target="#shiftcommentModal"
style="cursor: pointer"
>{% trans 'Add Comments' %}</a
>
&nbsp,
<a
hx-target="#shiftRequestDetailTarget"
hx-target="#commentContainer"
hx-get="{% url 'payroll-request-view-comment' req.id %}"
class="text-primary"
data-toggle="oh-modal-toggle"
data-target="#shiftRequestDetailModal"
class="text-primary fw-bold oh-activity-sidebar__open w-100"
data-target = '#activitySidebar'
style="cursor: pointer"
>{% trans 'View Comments' %}</a
>{% trans 'Comments' %}</a
></i
>
</div>
@@ -664,6 +644,8 @@
hx-get="{% url 'create-reimbursement' %}?instance_id={{ req.id }}"
hx-target="#objectCreateModalTarget"
class="mr-2"
style="cursor:pointer;"
title='{% trans "Update" %}'
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
><ion-icon
@@ -676,7 +658,7 @@
{% endif %}
{% if perms.payroll.delete_reimbursement %}
<a href="{% url "delete-reimbursement" %}?ids={{req.id}}" onclick="return confirm('Do you want to delete this record?')"
><ion-icon
title='{% trans "Delete" %}'><ion-icon
class="text-danger md hydrated"
name="trash-outline"
role="img"
@@ -717,25 +699,12 @@
<div class="mb-2">
<i
><a
hx-target="#shiftRequestCommentForm"
hx-get="{% url 'payroll-request-add-comment' req.id %}"
class="text-primary"
hx-swap="innerHTML"
data-toggle="oh-modal-toggle"
data-target="#shiftcommentModal"
style="cursor: pointer"
>{% trans 'Add Comments' %}</a
>
&nbsp,
<a
hx-target="#shiftRequestDetailTarget"
hx-target="#commentContainer"
hx-get="{% url 'payroll-request-view-comment' req.id %}"
class="text-primary"
data-toggle="oh-modal-toggle"
data-target="#shiftRequestDetailModal"
class="text-primary fw-bold oh-activity-sidebar__open w-100"
data-target = '#activitySidebar'
style="cursor: pointer"
>{% trans 'View Comments' %}</a
>{% trans 'Comments' %}</a
></i
>
</div>
@@ -936,55 +905,17 @@
</div>
</div>
<!-- start of comment modal -->
<div
class="oh-modal"
id="shiftcommentModal"
role="dialog"
aria-labelledby="emptagModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="createModalTitle">
{% trans "Add Comment" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div
class="oh-modal__dialog-body"
id="shiftRequestCommentForm"
></div>
</div>
</div>
<!-- end of comment modal -->
<!-- Activty side bar for comment section-->
<div
class="oh-modal"
style="z-index: 60"
id="shiftRequestDetailModal"
role="dialog"
aria-labelledby="shiftRequestDetailModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="">
{% trans "Details" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div
class="oh-modal__dialog-body oh-modal__dialog-relative"
id="shiftRequestDetailTarget"
></div>
<div class="oh-activity-sidebar" id="activitySidebar" style="z-index:1000;">
<div class="oh-activity-sidebar__body" id="commentContainer">
</div>
</div>
<!-- End of Activty side bar for comment section-->
<script>
function reimbursementConfirm(params, target, approve = false) {
Swal.fire({

View File

@@ -54,6 +54,14 @@
<div class="oh-modal__dialog-body" id="reimbursementAttachementModalBody"></div>
</div>
</div>
<div class="oh-activity-sidebar" id="activitySidebar" style="z-index:1000;">
<div class="oh-activity-sidebar__body" id="commentContainer">
</div>
</div>
<script>
function reimbursementConfirm(params, target, approve = false) {
Swal.fire({
@@ -79,5 +87,42 @@
}
});
}
</script>
// This lines is used to set default selected stage for exits lines
function enlargeImage(src,$element) {
$(".enlargeImageContainer").empty()
var enlargeImageContainer = $element.parents().closest("li").find(".enlargeImageContainer")
enlargeImageContainer.empty()
style = 'width:100%; height:90%; box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.2); background:white'
var enlargedImage = $('<iframe>').attr({ src: src, style: style })
var name = $('<span>').text(src.split('/').pop().replace(/_/g, ' '))
enlargeImageContainer.append(enlargedImage)
enlargeImageContainer.append(name)
setTimeout(function () {
enlargeImageContainer.show()
const iframe = document.querySelector('iframe').contentWindow
var iframe_document = iframe.document
iframe_image = iframe_document.getElementsByTagName('img')[0]
$(iframe_image).attr('style', 'width:100%; height:100%;')
}, 100)
}
function hideEnlargeImage() {
var enlargeImageContainer = $('.enlargeImageContainer')
enlargeImageContainer.empty()
}
$(document).on('click', function (event) {
if (!$(event.target).closest('#enlargeImageContainer').length) {
hideEnlargeImage()
}
})
function submitForm(elem) {
$(elem).siblings(".add_more_submit").click();
}
</script>
{% endblock %}

View File

@@ -157,6 +157,11 @@ urlpatterns = [
views.delete_payrollrequest_comment,
name="payroll-request-delete-comment",
),
path(
"delete-reimbursement-comment-file/",
views.delete_reimbursement_comment_file,
name="delete-reimbursement-comment-file",
),
path(
"initial-notice-period",
views.initial_notice_period,

View File

@@ -29,6 +29,7 @@ from payroll.models.models import (
PayrollGeneralSetting,
Payslip,
Reimbursement,
ReimbursementFile,
ReimbursementrequestComment,
WorkRecord,
Contract,
@@ -1487,30 +1488,70 @@ def create_payrollrequest_comment(request, payroll_id):
form.instance.employee_id = emp
form.instance.request_id = payroll
form.save()
comments = ReimbursementrequestComment.objects.filter(
request_id=payroll_id
).order_by("-created_at")
no_comments = False
if not comments.exists():
no_comments = True
form = ReimbursementRequestCommentForm(
initial={"employee_id": emp.id, "request_id": payroll_id}
)
messages.success(request, _("Comment added successfully!"))
if request.user.employee_get.id == payroll.employee_id.id:
rec = (
payroll.employee_id.employee_work_info.reporting_manager_id.employee_user_id
)
notify.send(
request.user.employee_get,
recipient=rec,
verb=f"{payroll.employee_id}'s reimbursement request has received a comment.",
verb_ar=f"تلقى طلب استرداد نفقات {payroll.employee_id} تعليقًا.",
verb_de=f"{payroll.employee_id}s Rückerstattungsantrag hat einen Kommentar erhalten.",
verb_es=f"La solicitud de reembolso de gastos de {payroll.employee_id} ha recibido un comentario.",
verb_fr=f"La demande de remboursement de frais de {payroll.employee_id} a reçu un commentaire.",
redirect="/payroll/view-reimbursement",
icon="chatbox-ellipses",
)
elif (
request.user.employee_get.id
== payroll.employee_id.employee_work_info.reporting_manager_id.id
if (
payroll.employee_id.employee_work_info.reporting_manager_id
is not None
):
if request.user.employee_get.id == payroll.employee_id.id:
rec = (
payroll.employee_id.employee_work_info.reporting_manager_id.employee_user_id
)
notify.send(
request.user.employee_get,
recipient=rec,
verb=f"{payroll.employee_id}'s reimbursement request has received a comment.",
verb_ar=f"تلقى طلب استرداد نفقات {payroll.employee_id} تعليقًا.",
verb_de=f"{payroll.employee_id}s Rückerstattungsantrag hat einen Kommentar erhalten.",
verb_es=f"La solicitud de reembolso de gastos de {payroll.employee_id} ha recibido un comentario.",
verb_fr=f"La demande de remboursement de frais de {payroll.employee_id} a reçu un commentaire.",
redirect="/payroll/view-reimbursement",
icon="chatbox-ellipses",
)
elif (
request.user.employee_get.id
== payroll.employee_id.employee_work_info.reporting_manager_id.id
):
rec = payroll.employee_id.employee_user_id
notify.send(
request.user.employee_get,
recipient=rec,
verb="Your reimbursement request has received a comment.",
verb_ar="تلقى طلب استرداد نفقاتك تعليقًا.",
verb_de="Ihr Rückerstattungsantrag hat einen Kommentar erhalten.",
verb_es="Tu solicitud de reembolso ha recibido un comentario.",
verb_fr="Votre demande de remboursement a reçu un commentaire.",
redirect="/payroll/view-reimbursement",
icon="chatbox-ellipses",
)
else:
rec = [
payroll.employee_id.employee_user_id,
payroll.employee_id.employee_work_info.reporting_manager_id.employee_user_id,
]
notify.send(
request.user.employee_get,
recipient=rec,
verb=f"{payroll.employee_id}'s reimbursement request has received a comment.",
verb_ar=f"تلقى طلب استرداد نفقات {payroll.employee_id} تعليقًا.",
verb_de=f"{payroll.employee_id}s Rückerstattungsantrag hat einen Kommentar erhalten.",
verb_es=f"La solicitud de reembolso de gastos de {payroll.employee_id} ha recibido un comentario.",
verb_fr=f"La demande de remboursement de frais de {payroll.employee_id} a reçu un commentaire.",
redirect="/payroll/view-reimbursement",
icon="chatbox-ellipses",
)
else:
rec = payroll.employee_id.employee_user_id
notify.send(
request.user.employee_get,
@@ -1523,27 +1564,15 @@ def create_payrollrequest_comment(request, payroll_id):
redirect="/payroll/view-reimbursement",
icon="chatbox-ellipses",
)
else:
rec = [
payroll.employee_id.employee_user_id,
payroll.employee_id.employee_work_info.reporting_manager_id.employee_user_id,
]
notify.send(
request.user.employee_get,
recipient=rec,
verb=f"{payroll.employee_id}'s reimbursement request has received a comment.",
verb_ar=f"تلقى طلب استرداد نفقات {payroll.employee_id} تعليقًا.",
verb_de=f"{payroll.employee_id}s Rückerstattungsantrag hat einen Kommentar erhalten.",
verb_es=f"La solicitud de reembolso de gastos de {payroll.employee_id} ha recibido un comentario.",
verb_fr=f"La demande de remboursement de frais de {payroll.employee_id} a reçu un commentaire.",
redirect="/payroll/view-reimbursement",
icon="chatbox-ellipses",
)
return HttpResponse("<script>window.location.reload()</script>")
return render(
request,
"payroll/reimbursement/reimbursement_comment.html",
{"comments": comments, "no_comments": no_comments, "request_id": payroll_id,},
)
return render(
request,
"payroll/reimbursement/reimbursement_request_comment_form.html",
"payroll/reimbursement/reimbursement_comment.html",
{"form": form, "request_id": payroll_id},
)
@@ -1556,14 +1585,26 @@ def view_payrollrequest_comment(request, payroll_id):
comments = ReimbursementrequestComment.objects.filter(
request_id=payroll_id
).order_by("-created_at")
no_comments = False
if not comments.exists():
no_comments = True
if request.FILES:
files = request.FILES.getlist("files")
comment_id = request.GET["comment_id"]
comment = ReimbursementrequestComment.objects.get(id=comment_id)
attachments = []
for file in files:
file_instance = ReimbursementFile()
file_instance.file = file
file_instance.save()
attachments.append(file_instance)
comment.files.add(*attachments)
return render(
request,
"payroll/reimbursement/comment_view.html",
{"comments": comments, "no_comments": no_comments},
"payroll/reimbursement/reimbursement_comment.html",
{"comments": comments, "no_comments": no_comments, "request_id": payroll_id,},
)
@@ -1572,12 +1613,34 @@ def delete_payrollrequest_comment(request, comment_id):
"""
This method is used to delete Reimbursement request comments
"""
ReimbursementrequestComment.objects.get(id=comment_id).delete()
comment=ReimbursementrequestComment.objects.get(id=comment_id)
reimbursementrequest = comment.request_id.id
comment.delete()
messages.success(request, _("Comment deleted successfully!"))
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
return redirect('payroll-request-view-comment', payroll_id = reimbursementrequest)
@login_required
def delete_reimbursement_comment_file(request):
"""
Used to delete attachment
"""
ids = request.GET.getlist("ids")
ReimbursementFile.objects.filter(id__in=ids).delete()
payroll_id = request.GET["payroll_id"]
comments = ReimbursementrequestComment.objects.filter(request_id=payroll_id).order_by(
"-created_at"
)
return render(
request,
"payroll/reimbursement/reimbursement_comment.html",
{
"comments": comments,
"request_id": payroll_id,
},
)
@login_required
@permission_required("payroll.add_payrollgeneralsetting")
def initial_notice_period(request):