[ADD] EMPLOYEE/ATTENDANCE: Late come/ early out penalty implementation
This commit is contained in:
@@ -10,6 +10,7 @@ from .models import (
|
||||
AttendanceOverTime,
|
||||
AttendanceLateComeEarlyOut,
|
||||
AttendanceValidationCondition,
|
||||
PenaltyAccount,
|
||||
)
|
||||
|
||||
# Register your models here.
|
||||
@@ -18,3 +19,4 @@ admin.site.register(AttendanceActivity)
|
||||
admin.site.register(AttendanceOverTime)
|
||||
admin.site.register(AttendanceLateComeEarlyOut)
|
||||
admin.site.register(AttendanceValidationCondition)
|
||||
admin.site.register(PenaltyAccount)
|
||||
|
||||
@@ -17,6 +17,7 @@ from attendance.models import (
|
||||
AttendanceOverTime,
|
||||
AttendanceLateComeEarlyOut,
|
||||
AttendanceActivity,
|
||||
PenaltyAccount,
|
||||
strtime_seconds,
|
||||
)
|
||||
from base.filters import FilterSet
|
||||
@@ -242,6 +243,16 @@ class LateComeEarlyOutFilter(FilterSet):
|
||||
self.form.fields[field].widget.attrs["id"] = f"{uuid.uuid4()}"
|
||||
|
||||
|
||||
class PenaltyFilter(FilterSet):
|
||||
"""
|
||||
PenaltyFilter
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = PenaltyAccount
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class AttendanceActivityFilter(FilterSet):
|
||||
"""
|
||||
Filter set class for AttendanceActivity model
|
||||
@@ -332,6 +343,7 @@ class AttendanceFilters(FilterSet):
|
||||
Args:
|
||||
FilterSet (class): custom filter set class to apply styling
|
||||
"""
|
||||
|
||||
id = django_filters.NumberFilter(field_name="id")
|
||||
search = django_filters.CharFilter(method=filter_by_name)
|
||||
employee = django_filters.CharFilter(field_name="employee_id__id")
|
||||
|
||||
@@ -42,6 +42,7 @@ from attendance.models import (
|
||||
AttendanceActivity,
|
||||
AttendanceLateComeEarlyOut,
|
||||
AttendanceValidationCondition,
|
||||
PenaltyAccount,
|
||||
strtime_seconds,
|
||||
)
|
||||
from django.utils.html import format_html
|
||||
@@ -683,6 +684,17 @@ class AttendanceExportForm(forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class PenaltyAccountForm(ModelForm):
|
||||
"""
|
||||
PenaltyAccountForm
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = PenaltyAccount
|
||||
fields = "__all__"
|
||||
exclude = ["late_early_id"]
|
||||
|
||||
|
||||
class LateComeEarlyOutExportForm(forms.Form):
|
||||
model_fields = AttendanceLateComeEarlyOut._meta.get_fields()
|
||||
field_choices_1 = [
|
||||
|
||||
@@ -12,12 +12,21 @@ from datetime import datetime, date, timedelta
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db.models.signals import post_save
|
||||
import pandas as pd
|
||||
from base.models import Company, EmployeeShift, EmployeeShiftDay, WorkType
|
||||
from base.horilla_company_manager import HorillaCompanyManager
|
||||
from employee.models import Employee
|
||||
from leave.models import WEEK_DAYS, WEEKS, CompanyLeave, Holiday, LeaveRequest
|
||||
from leave.models import (
|
||||
WEEK_DAYS,
|
||||
WEEKS,
|
||||
CompanyLeave,
|
||||
Holiday,
|
||||
LeaveRequest,
|
||||
LeaveType,
|
||||
)
|
||||
from attendance.methods.differentiate import get_diff_dict
|
||||
|
||||
# Create your models here.
|
||||
@@ -256,7 +265,7 @@ class Attendance(models.Model):
|
||||
keys = diffs.keys()
|
||||
return keys
|
||||
|
||||
def get_last_clock_out(self,null_activity=False):
|
||||
def get_last_clock_out(self, null_activity=False):
|
||||
"""
|
||||
This method is used to get the last attendance activity if exists
|
||||
"""
|
||||
@@ -712,6 +721,13 @@ class AttendanceLateComeEarlyOut(models.Model):
|
||||
objects = HorillaCompanyManager(
|
||||
related_company_field="employee_id__employee_work_info__company_id"
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True,null=True)
|
||||
|
||||
def get_penalties_count(self):
|
||||
"""
|
||||
This method is used to return the total penalties in the late early instance
|
||||
"""
|
||||
return self.penaltyaccount_set.count()
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
super().save(*args, **kwargs)
|
||||
@@ -754,3 +770,96 @@ class AttendanceValidationCondition(models.Model):
|
||||
super().clean()
|
||||
if not self.id and AttendanceValidationCondition.objects.exists():
|
||||
raise ValidationError(_("You cannot add more conditions."))
|
||||
|
||||
|
||||
months = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
]
|
||||
|
||||
|
||||
class PenaltyAccount(models.Model):
|
||||
"""
|
||||
LateComeEarlyOutPenaltyAccount
|
||||
"""
|
||||
|
||||
employee_id = models.ForeignKey(
|
||||
Employee,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="penalty_set",
|
||||
editable=False,
|
||||
verbose_name="Employee",
|
||||
null=True,
|
||||
)
|
||||
late_early_id = models.ForeignKey(
|
||||
AttendanceLateComeEarlyOut,
|
||||
on_delete=models.CASCADE,
|
||||
null=True,
|
||||
)
|
||||
leave_type_id = models.ForeignKey(
|
||||
LeaveType,
|
||||
on_delete=models.DO_NOTHING,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name="Leave type",
|
||||
)
|
||||
minus_leaves = models.FloatField(default=0.0, null=True)
|
||||
deduct_from_carry_forward = models.BooleanField(default=False)
|
||||
penalty_amount = models.FloatField(default=0.0, null=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True,null=True)
|
||||
|
||||
def clean(self) -> None:
|
||||
super().clean()
|
||||
if (
|
||||
self.minus_leaves or self.deduct_from_carry_forward
|
||||
) and not self.leave_type_id:
|
||||
raise ValidationError({"leave_type_id": "Leave type is required"})
|
||||
return
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
|
||||
|
||||
@receiver(post_save, sender=PenaltyAccount)
|
||||
def create_initial_stage(sender, instance, created, **kwargs):
|
||||
"""
|
||||
This is post save method, used to create initial stage for the recruitment
|
||||
"""
|
||||
|
||||
if created:
|
||||
penalty_amount = instance.penalty_amount
|
||||
if penalty_amount:
|
||||
from payroll.models.models import Deduction
|
||||
|
||||
penalty = Deduction()
|
||||
penalty.title = f"{instance.late_early_id.get_type_display()} penalty"
|
||||
penalty.one_time_date = instance.late_early_id.attendance_id.attendance_date
|
||||
penalty.include_active_employees = False
|
||||
penalty.is_fixed = True
|
||||
penalty.amount = instance.penalty_amount
|
||||
penalty.only_show_under_employee = True
|
||||
penalty.save()
|
||||
penalty.specific_employees.add(instance.employee_id)
|
||||
|
||||
if instance.leave_type_id and instance.minus_leaves:
|
||||
available = instance.employee_id.available_leave.filter(
|
||||
leave_type_id=instance.leave_type_id
|
||||
).first()
|
||||
unit = round(instance.minus_leaves * 2) / 2
|
||||
if not instance.deduct_from_carry_forward:
|
||||
available.available_days = max(0, (available.total_leave_days - unit))
|
||||
else:
|
||||
available.carryforward_days = max(
|
||||
0, (available.carryforward_days - unit)
|
||||
)
|
||||
available.save()
|
||||
|
||||
@@ -1,85 +1,45 @@
|
||||
{% extends 'settings.html' %} {% load i18n %} {% block settings %}
|
||||
|
||||
<div class="oh-inner-sidebar-content mb-4">
|
||||
<div class="oh-inner-sidebar-content__header d-flex justify-content-between align-items-center">
|
||||
<h2 class="oh-inner-sidebar-content__title">{% trans "Employee Type" %}</h2>
|
||||
{% if not condition %}
|
||||
<button
|
||||
class="oh-btn oh-btn--secondary oh-btn--shadow"
|
||||
hx-target="#updateForm"
|
||||
type="button"
|
||||
hx-get="{% url 'attendance-settings-create'%}"
|
||||
class="oh-btn oh-btn--info"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#updateModal"
|
||||
>
|
||||
<ion-icon name="add-outline" class="me-1"></ion-icon>
|
||||
{% trans "Create" %}
|
||||
<div class="oh-inner-sidebar-content">
|
||||
<div class="oh-inner-sidebar-content__header d-flex justify-content-between align-items-center">
|
||||
<h2 class="oh-inner-sidebar-content__title">{% trans 'Condition' %}</h2>
|
||||
{% if not condition %}
|
||||
<button class="oh-btn oh-btn--secondary oh-btn--shadow" hx-target="#updateForm" type="button" hx-get="{% url 'attendance-settings-create' %}" class="oh-btn oh-btn--info" data-toggle="oh-modal-toggle" data-target="#updateModal">
|
||||
<ion-icon name="add-outline" class="me-1"></ion-icon>
|
||||
{% trans 'Create' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if condition %}
|
||||
<div class="oh-sticky-table">
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__thead">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__th">
|
||||
{% trans "Auto Validate Till" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">
|
||||
{% trans "Min Hour To Approve OT" %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">{% trans "OT Cut-Off/Day" %}</div>
|
||||
<div class="oh-sticky-table__th"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-sticky-table__tbody">
|
||||
{% if condition != None %}
|
||||
<div class="oh-sticky-table__tr" draggable="true">
|
||||
<div class="oh-sticky-table__td">
|
||||
{{condition.validation_at_work}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
{{condition.minimum_overtime_to_approve}}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">{{condition.overtime_cutoff}}</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
<a
|
||||
hx-get="{% url 'attendance-settings-update' condition.id %}"
|
||||
hx-target="#updateForm"
|
||||
type="button"
|
||||
class="oh-btn oh-btn--info"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#updateModal"
|
||||
>
|
||||
{% trans "Edit" %}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif%}
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="oh-modal"
|
||||
id="updateModal"
|
||||
role="dialog"
|
||||
aria-labelledby="updateModal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="oh-modal__dialog">
|
||||
<div class="oh-modal__dialog-header">
|
||||
|
||||
<button class="oh-modal__close" aria-label="Close">
|
||||
<ion-icon name="close-outline"></ion-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oh-modal__dialog-body" id="updateForm"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock settings %}
|
||||
{% if condition %}
|
||||
<div class="oh-sticky-table">
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__thead">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__th">
|
||||
{% trans 'Auto Validate Till' %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">
|
||||
{% trans 'Min Hour To Approve OT' %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">
|
||||
{% trans 'OT Cut-Off/Day' %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__th"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-sticky-table__tbody">
|
||||
{% if condition != None %}
|
||||
<div class="oh-sticky-table__tr" draggable="true">
|
||||
<div class="oh-sticky-table__td">{{ condition.validation_at_work }}</div>
|
||||
<div class="oh-sticky-table__td">{{ condition.minimum_overtime_to_approve }}</div>
|
||||
<div class="oh-sticky-table__td">{{ condition.overtime_cutoff }}</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
<a hx-get="{% url 'attendance-settings-update' condition.id %}" hx-target="#updateForm" type="button" class="oh-btn oh-btn--info" data-toggle="oh-modal-toggle" data-target="#updateModal">{% trans 'Edit' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% load i18n %}
|
||||
{% include 'filter_tags.html' %}
|
||||
{% load basefilters %}
|
||||
<div div class="oh-sticky-table" >
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__thead">
|
||||
@@ -18,6 +19,8 @@
|
||||
<div class='oh-sticky-table__th' scope="col" hx-target='#report-container' hx-get="{% url 'late-come-early-out-search' %}?{{pd}}&sortby=attendance_id__attendance_clock_out_date">{% trans "Out Date" %}</div>
|
||||
<div class='oh-sticky-table__th' scope="col" hx-target='#report-container' hx-get="{% url 'late-come-early-out-search' %}?{{pd}}&sortby=">{% trans "Min Hour" %}</div>
|
||||
<div class='oh-sticky-table__th' scope="col" hx-target='#report-container' hx-get="{% url 'late-come-early-out-search' %}?{{pd}}&sortby=attendance_id__at_work_second">{% trans "At Work" %}</div>
|
||||
<div class='oh-sticky-table__th' scope="col"></div>
|
||||
<div class='oh-sticky-table__th' scope="col" style="width: 150px;"></div>
|
||||
<div class='oh-sticky-table__th' scope="col" style="width: 80px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,12 +57,28 @@
|
||||
<div class='oh-sticky-table__td timeformat_changer'>{{late_in_early_out.attendance_id.attendance_clock_in}}</div>
|
||||
<div class='oh-sticky-table__td dateformat_changer'>{{late_in_early_out.attendance_id.attendance_clock_in_date}}</div>
|
||||
<div class='oh-sticky-table__td timeformat_changer'>{{late_in_early_out.attendance_id.attendance_clock_out}}</div>
|
||||
<div class='oh-sticky-table__td dateformat_changer'>{{late_in_early_out.attendance_id.attendance_clock_out_date}}</div>
|
||||
<div class='oh-sticky-table__td dateformat_changer'>{{laXte_in_early_out.attendance_id.attendance_clock_out_date}}</div>
|
||||
<div class='oh-sticky-table__td'>{{late_in_early_out.attendance_id.minimum_hour}}</div>
|
||||
<div class='oh-sticky-table__td'>{{late_in_early_out.attendance_id.attendance_worked_hour}}</div>
|
||||
<div class='oh-sticky-table__td'>
|
||||
{% if late_in_early_out.get_penalties_count %}
|
||||
<div class="" data-target="#penaltyViewModal" data-toggle="oh-modal-toggle" hx-get="{% url "view-penalties" %}?late_early_id={{late_in_early_out.id}}" hx-target="#penaltyViewModalBody" align="center" style="background-color: rgba(229, 79, 56, 0.036); border: 2px solid rgb(229, 79, 56); border-radius: 18px; padding: 10px; font-weight: bold; width: 85%;">Penalties :{{late_in_early_out.get_penalties_count}}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
<div class="oh-btn-group">
|
||||
|
||||
{% if request.user|is_reportingmanager or perms.attendance.chanage_penaltyaccount %}
|
||||
<button
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#penaltyModal"
|
||||
hx-target="#penaltyModalBody"
|
||||
hx-get="{% url "cut-penalty" late_in_early_out.id %}"
|
||||
type='submit'
|
||||
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
|
||||
title="{% trans 'Penalty' %}">
|
||||
<ion-icon name="information-circle-outline"></ion-icon>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if perms.attendance.delete_attendancelatecomeearlyout %}
|
||||
<form action="{% url 'late-come-early-out-delete' late_in_early_out.id %}"
|
||||
onclick="event.stopPropagation()" onsubmit="return confirm('{% trans "Are you sure want to delete this attendance?" %}')"
|
||||
|
||||
@@ -2,25 +2,42 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
|
||||
{% include 'attendance/late_come_early_out/nav.html' %}
|
||||
<div class="oh-checkpoint-badge mb-2" id="selectedLatecome" data-ids="[]" data-clicked="" style="display:none;" >
|
||||
{% trans "Selected Attendance" %}
|
||||
</div>
|
||||
<div class="oh-wrapper">
|
||||
{% include 'attendance/late_come_early_out/nav.html' %}
|
||||
<div class="oh-checkpoint-badge mb-2" id="selectedLatecome" data-ids="[]" data-clicked="" style="display:none;">
|
||||
{% trans 'Selected Attendance' %}
|
||||
</div>
|
||||
<div class="oh-wrapper">
|
||||
<div class="oh-checkpoint-badge text-success mb-2" id="selectAllLatecome" style="cursor: pointer;">
|
||||
{% trans "Select All Attendance" %}
|
||||
{% trans 'Select All Attendance' %}
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge text-secondary mb-2" id="unselectAllLatecome" style="cursor: pointer;">
|
||||
{% trans "Unselect All Attendance" %}
|
||||
{% trans 'Unselect All Attendance' %}
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge text-info mb-2" id="exportLatecome" style="cursor: pointer;">
|
||||
{% trans "Export Attendance" %}
|
||||
{% trans 'Export Attendance' %}
|
||||
</div>
|
||||
<div class="oh-checkpoint-badge text-danger mb-2" id="selectedShowLatecome" >
|
||||
<div class="oh-checkpoint-badge text-danger mb-2" id="selectedShowLatecome"></div>
|
||||
<div id="report-container">
|
||||
{% include 'attendance/late_come_early_out/report_list.html' %}
|
||||
</div>
|
||||
<div id='report-container'>
|
||||
{% include 'attendance/late_come_early_out/report_list.html' %}
|
||||
</div>
|
||||
|
||||
<div class="oh-modal" id="penaltyModal" role="dialog" aria-hidden="true">
|
||||
<div class="oh-modal__dialog" style="max-width: 550px">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<button type="button" class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
|
||||
</div>
|
||||
|
||||
<div class="oh-modal__dialog-body" id="penaltyModalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-modal" id="penaltyViewModal" role="dialog" aria-hidden="true">
|
||||
<div class="oh-modal__dialog" style="max-width: 1050px">
|
||||
<div class="oh-modal__dialog-header">
|
||||
<button type="button" class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
|
||||
</div>
|
||||
|
||||
<div class="oh-modal__dialog-body" id="penaltyViewModalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
53
attendance/templates/attendance/penalty/form.html
Normal file
53
attendance/templates/attendance/penalty/form.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<div class="oh-timeoff-modal__profile-content">
|
||||
<div class="oh-profile mb-2">
|
||||
<div class="oh-profile__avatar">
|
||||
<img src="{{ instance.employee_id.get_avatar }}" class="oh-profile__image me-2" alt="Mary Magdalene" />
|
||||
</div>
|
||||
<div class="oh-timeoff-modal__profile-info">
|
||||
<span class="oh-timeoff-modal__user fw-bold">{{ instance.employee_id.get_full_name }}</span>
|
||||
<span class="oh-timeoff-modal__user m-0" style="font-size: 18px; color: #4d4a4a">
|
||||
{{ instance.employee_id.get_department }} /
|
||||
{{ instance.employee_id.get_job_position }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form hx-post="{% url 'cut-penalty' instance.id %}" hx-target="#penaltyModalBody">
|
||||
{{ form.as_p }}
|
||||
|
||||
<div class="oh-sticky-table__table oh-table--sortable">
|
||||
<div class="oh-sticky-table__thead">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__th">Leave Type</div>
|
||||
<div class="oh-sticky-table__th">Available Days</div>
|
||||
<div class="oh-sticky-table__th">Carry Forward Days</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-sticky-table__tbody">
|
||||
{% for acc in available %}
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__th">{{ acc.leave_type_id }}</div>
|
||||
<div class="oh-sticky-table__th">{{ acc.available_days }}</div>
|
||||
<div class="oh-sticky-table__th">{{ acc.carryforward_days }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<ol class="mt-3">
|
||||
<li>
|
||||
<i>Leave type is optional when 'minus leave' is 0</i>
|
||||
</li>
|
||||
<li>
|
||||
<i>Penalty amount will affect payslip on the date</i>
|
||||
</li>
|
||||
<li>
|
||||
<i>By default minus leave will cut/deduct from available leaves</i>
|
||||
</li>
|
||||
<li>
|
||||
<i>By enabling 'Deduct from carry forward' leave will cut/deduct from carry forward days</i>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="d-flex flex-row-reverse">
|
||||
<button type="submit" class="oh-btn oh-btn--secondary mt-2 mr-0 pl-4 pr-5 oh-btn--w-100-resp">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
30
attendance/templates/attendance/penalty/penalty_view.html
Normal file
30
attendance/templates/attendance/penalty/penalty_view.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<div class="oh-sticky-table__table mt-3">
|
||||
<div class="oh-sticky-table__thead">
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__th">Leave Type</div>
|
||||
<div class="oh-sticky-table__th">Minus Days</div>
|
||||
<div class="oh-sticky-table__th">
|
||||
Deducted From <span title="Carry Forward Days">CFD</span>
|
||||
</div>
|
||||
<div class="oh-sticky-table__th">Penalty amount</div>
|
||||
<div class="oh-sticky-table__th">Created Date</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-sticky-table__tbody">
|
||||
{% for acc in records %}
|
||||
<div class="oh-sticky-table__tr">
|
||||
<div class="oh-sticky-table__td">{{ acc.leave_type_id }}</div>
|
||||
<div class="oh-sticky-table__td">{{ acc.minus_leaves }}</div>
|
||||
<div class="oh-sticky-table__td">
|
||||
{% if acc.deduct_from_carry_forward %}
|
||||
Yes
|
||||
{% else %}
|
||||
No
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">{{ acc.penalty_amount }}</div>
|
||||
<div class="oh-sticky-table__td">{{ acc.created_at }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,6 +8,7 @@ from django.urls import path
|
||||
import attendance.views.clock_in_out
|
||||
|
||||
import attendance.views.dashboard
|
||||
import attendance.views.penalty
|
||||
import attendance.views.search
|
||||
import attendance.views.requests
|
||||
from .views import views
|
||||
@@ -286,5 +287,11 @@ urlpatterns = [
|
||||
views.latecome_attendance_select_filter,
|
||||
name="latecome-attendance-select-filter",
|
||||
),
|
||||
path("pending-hours/",attendance.views.dashboard.pending_hours,name="pending-hours")
|
||||
path(
|
||||
"pending-hours/", attendance.views.dashboard.pending_hours, name="pending-hours"
|
||||
),
|
||||
path(
|
||||
"cut-penalty/<int:instance_id>/", attendance.views.penalty.cut_available_leave, name="cut-penalty"
|
||||
),
|
||||
path("view-penalties",attendance.views.penalty.view_penalties,name="view-penalties")
|
||||
]
|
||||
|
||||
56
attendance/views/penalty.py
Normal file
56
attendance/views/penalty.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
attendance/views/penalty.py
|
||||
|
||||
This module is used to write late come early out penatly methods
|
||||
"""
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib import messages
|
||||
from attendance.forms import PenaltyAccountForm
|
||||
from attendance.filters import PenaltyFilter
|
||||
from attendance.models import AttendanceLateComeEarlyOut, PenaltyAccount
|
||||
from leave.models import AvailableLeave
|
||||
from employee.models import Employee
|
||||
from horilla.decorators import login_required, manager_can_enter
|
||||
|
||||
|
||||
@login_required
|
||||
@manager_can_enter("leave.change_availableleave")
|
||||
def cut_available_leave(request, instance_id):
|
||||
"""
|
||||
This method is used to create the penalties
|
||||
"""
|
||||
instance = AttendanceLateComeEarlyOut.objects.get(id=instance_id)
|
||||
form = PenaltyAccountForm()
|
||||
available = AvailableLeave.objects.filter(employee_id=instance.employee_id)
|
||||
if request.method == "POST":
|
||||
form = PenaltyAccountForm(request.POST)
|
||||
if form.is_valid():
|
||||
penalty_instance = form.instance
|
||||
penalty = PenaltyAccount()
|
||||
penalty.late_early_id = instance
|
||||
penalty.employee_id = instance.employee_id
|
||||
penalty.leave_type_id = penalty_instance.leave_type_id
|
||||
penalty.minus_leaves = penalty_instance.minus_leaves
|
||||
penalty.penalty_amount = penalty_instance.penalty_amount
|
||||
penalty.save()
|
||||
messages.success(request, "Penalty/Fine added")
|
||||
return HttpResponse("<script>window.location.reload()</script>")
|
||||
return render(
|
||||
request,
|
||||
"attendance/penalty/form.html",
|
||||
{"available": available, "form": form, "instance": instance},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@manager_can_enter("attendance.view_penalty")
|
||||
def view_penalties(request):
|
||||
"""
|
||||
This method is used to filter or view the penalties
|
||||
"""
|
||||
records = PenaltyFilter(request.GET).qs
|
||||
print("-------------")
|
||||
print(records)
|
||||
print("-------------")
|
||||
return render(request, "attendance/penalty/penalty_view.html", {"records": records})
|
||||
@@ -180,24 +180,16 @@
|
||||
>{% trans "Allowance & Deduction" %}</a
|
||||
>
|
||||
</li>
|
||||
{% comment %} <li class="oh-general__tab">
|
||||
<a
|
||||
href="#"
|
||||
class="oh-general__tab-link"
|
||||
data-action="general-tab"
|
||||
data-target="#team"
|
||||
>{% trans "Team" %}</a
|
||||
>
|
||||
</li>
|
||||
<li class="oh-general__tab">
|
||||
<a
|
||||
href="#"
|
||||
class="oh-general__tab-link"
|
||||
data-action="general-tab"
|
||||
data-target="#timesheet"
|
||||
>{% trans "Timesheet" %}</a
|
||||
data-target="#penaltyTarget"
|
||||
id="penalty"
|
||||
class="oh-general__tab-link"
|
||||
role="button"
|
||||
>{% trans "Penalty Account" %}</a
|
||||
>
|
||||
</li> {% endcomment %}
|
||||
</li>
|
||||
<li class="oh-general__tab">
|
||||
<a
|
||||
hx-get={% url 'profile-asset-tab' employee.id %}
|
||||
@@ -229,12 +221,17 @@
|
||||
id="personal_target"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="oh-general__tab-target oh-profile__info-tab mb-4 d-none"
|
||||
id="payroll"
|
||||
class="oh-general__tab-target oh-profile__info-tab mb-4"
|
||||
id="penaltyTarget"
|
||||
>
|
||||
{% include 'tabs/payroll-tab.html' %}
|
||||
{% include 'tabs/penalty_account.html' %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-general__tab-target oh-profile__info-tab mb-4 d-none"
|
||||
id="payroll"
|
||||
>
|
||||
{% include 'tabs/payroll-tab.html' %}
|
||||
</div>
|
||||
<div
|
||||
class="oh-general__tab-target oh-profile__info-tab mb-4 d-none"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{% load basefilters %}
|
||||
{% comment %} {% include 'employee_nav.html' %} {% endcomment %}
|
||||
|
||||
|
||||
<style>
|
||||
.enlarge-image-container {
|
||||
display: none;
|
||||
|
||||
2
employee/templates/tabs/penalty_account.html
Normal file
2
employee/templates/tabs/penalty_account.html
Normal file
@@ -0,0 +1,2 @@
|
||||
{% load i18n %}
|
||||
<div hx-get="{% url "view-penalties" %}?employee_id={{employee.id}}" hx-trigger="load"></div>
|
||||
Reference in New Issue
Block a user