add early exit

This commit is contained in:
MahanCh
2025-04-09 18:38:15 +03:30
parent d03f19a51e
commit c561a138d6
4 changed files with 634 additions and 26 deletions

View File

@@ -9,4 +9,6 @@ public class RollCallTimeViewModel
public TimeSpan TotalHours { get; set; }
public DateTime StartDateGr { get; set; }
public DateTime EndDateGr { get; set; }
public string EntryTimeDifferences { get; set; }
public string ExitTimeDifferences { get; set; }
}

View File

@@ -528,9 +528,9 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
if (employeeRollCallStatuses == null || employeeRollCallStatuses.EmployeesStatus == null || !employeeRollCallStatuses.EmployeesStatus.Any())
return new();
//this list will have all the months which employee was active in, remember this doesn't have statuses,
//just the list of months which user had activity in
var activeMonths = new List<PersianDateTime>();
//this list will have all the months which employee was active in, remember this doesn't have statuses,
//just the list of months which user had activity in
var activeMonths = new List<PersianDateTime>();
//filling the list
foreach (var status in employeeRollCallStatuses.EmployeesStatus)
@@ -562,10 +562,10 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
//get the months that include these dates
activeMonthsList = activeMonthsList.Where(x => x.Year >= startSearch.Year && x.Month >= startSearch.Month && x.Year <= endSearch.Year && x.Month <= endSearch.Month).ToList();
}
//if exact datetime is given start and end will be the same date
//if exact datetime is given start and end will be the same date
if (exactDateTime.HasValue)
{
startSearch = new PersianDateTime(exactDateTime.Value);
startSearch = new PersianDateTime(exactDateTime.Value);
endSearch = startSearch;
activeMonthsList = activeMonthsList.Where(x => x.Year >= startSearch.Year && x.Month >= startSearch.Month && x.Year <= endSearch.Year && x.Month <= endSearch.Month).ToList();
}
@@ -575,8 +575,8 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
{
var persianDateIndex = new PersianDateTime(dateIndex.Value);
//gets first of month, if the day is 21st of month, first of month is 21-21 +1 = 1 or 21 + (-(21-1)) = 1
var dateIndexFirstOfMonth = persianDateIndex.AddDays(-(persianDateIndex.Day - 1));
//gets first of month, if the day is 21st of month, first of month is 21-21 +1 = 1 or 21 + (-(21-1)) = 1
var dateIndexFirstOfMonth = persianDateIndex.AddDays(-(persianDateIndex.Day - 1));
activeMonthsList = activeMonthsList.Where(x => dateIndexFirstOfMonth >= x).ToList();
}
@@ -594,7 +594,7 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
x.StartDate < nextMonthFirstDay);
//get leaves in the specified month
//get leaves in the specified month
var leavesQuery =
_context.LeaveList.Where(x => (x.LeaveType == "استعلاجی" || (x.LeaveType == "استحقاقی" && x.PaidLeaveType == "روزانه")) &&
x.IsAccepted &&
@@ -653,7 +653,9 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
StartDate = y.StartDate.Value.ToString("HH:mm"),
EndDate = y.EndDate!.Value.ToString("HH:mm"),
StartDateGr = y.StartDate.Value,
EndDateGr = y.EndDate.Value
EndDateGr = y.EndDate.Value,
EntryTimeDifferences = CalculateEntryTimeDifferences(y.EarlyEntryDuration, y.LateEntryDuration),
ExitTimeDifferences = CalculateExitTimeDifferences(y.EarlyExitDuration, y.LateExitDuration)
}),
TotalWorkingHoursSpan = new TimeSpan(rollCallsList.Where(y => x.Date == y.ShiftDate.Date)
.Sum(y => (y.EndDate!.Value - y.StartDate.Value).Ticks)),
@@ -662,26 +664,26 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
};
});
//filling TotalWorkingHours string from TimeSpans we filled in the last step and other info
result = result.Select(x => new RollCallViewModel()
//filling TotalWorkingHours string from TimeSpans we filled in the last step and other info
result = result.Select(x => new RollCallViewModel()
{
EmployeeFullName = employeeName.EmployeeFullName,
EmployeeId = employeeId,
PersonnelCode = personnelCode.ToString(),
DateGr = x.DateGr,
DateFa = x.DateFa,
DayOfWeekFa = x.DateGr.DayOfWeek.DayOfWeeKToPersian(),
IsHoliday = _holidayItemApplication.IsHoliday(x.DateGr),
IsAbsent = !x.RollCallTimesList.Any(),
RollCallTimesList = x.RollCallTimesList.OrderBy(r=>r.StartDateGr),
RollCallTimesList = x.RollCallTimesList.OrderBy(r => r.StartDateGr),
HasLeave = x.HasLeave,
TotalWorkingHoursSpan = x.TotalWorkingHoursSpan,
TotalWorkingHours = $"{(int)x.TotalWorkingHoursSpan.TotalHours}:{x.TotalWorkingHoursSpan.Minutes.ToString("00")}"
}).ToList();
//total working hours in the whole month or duration
var totalWorkingHours = new TimeSpan(result.Sum(x => x.TotalWorkingHoursSpan.Ticks));
var totalWorkingHours = new TimeSpan(result.Sum(x => x.TotalWorkingHoursSpan.Ticks));
//if there are any other months available that the selector
return new EmployeeRollCallsByMonthViewModel()
@@ -847,10 +849,12 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
EmployeeFullName = x.EmployeeFullName,
EmployeeId = x.EmployeeId,
Reason = leave == null ? "" : $"{leave.LeaveType}-{leave.PaidLeaveType}",
RollCallTimesList = employeeRollCallsForDate.OrderBy(r=>r.StartDate).Select(y => new RollCallTimeViewModel()
RollCallTimesList = employeeRollCallsForDate.OrderBy(r => r.StartDate).Select(y => new RollCallTimeViewModel()
{
EndDate = y.EndDate!.Value.ToString("HH:mm"),
StartDate = y.StartDate!.Value.ToString("HH:mm"),
EntryTimeDifferences = CalculateEntryTimeDifferences(y.EarlyEntryDuration, y.LateEntryDuration),
ExitTimeDifferences = CalculateExitTimeDifferences(y.EarlyExitDuration, y.LateExitDuration)
}),
HasLeave = leave != null,
IsAbsent = !employeeRollCallsForDate.Any(),
@@ -1081,10 +1085,13 @@ public class RollCallRepository : RepositoryBase<long, RollCall>, IRollCallRepos
EmployeeFullName = nameReferences.FirstOrDefault(y => x.Key == y.EmployeeId).EmployeeFullName,
EmployeeId = x.FirstOrDefault()!.EmployeeId,
TotalWorkingHoursSpan = new TimeSpan(x.Where(y => y.EndDate != null).Sum(y => (y.EndDate!.Value - y.StartDate!.Value).Ticks)),
RollCallTimesList = x.Select(y => new RollCallTimeViewModel()
RollCallTimesList = x.OrderBy(r => r.StartDate).Select(y => new RollCallTimeViewModel()
{
StartDate = y.StartDate!.Value.ToString("HH:mm"),
EndDate = y.EndDate?.ToString("HH:mm")
EndDate = y.EndDate?.ToString("HH:mm"),
EntryTimeDifferences = CalculateEntryTimeDifferences(y.EarlyEntryDuration, y.LateEntryDuration),
ExitTimeDifferences = CalculateExitTimeDifferences(y.EarlyExitDuration, y.LateExitDuration)
}).ToList()

View File

@@ -2,19 +2,16 @@
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using Microsoft.AspNetCore.Razor.Language.Intermediate
@using WorkFlow.Application.Contracts.WorkFlow
@using Version = _0_Framework.Application.Version
@inject _0_Framework.Application.IAuthHelper AuthHelper;
@inject IWorkFlowApplication WorkFlowApplication;
@{
string clientVersion = _0_Framework.Application.Version.StyleVersion;
var currentAccount = AuthHelper.CurrentAccountInfo();
long workshopId = currentAccount.WorkshopList.First(x => x.Slug == currentAccount.WorkshopSlug).Id;
int countWorkFlow = 0;/* await WorkFlowApplication.GetCountAllWorkFlows(workshopId); */
int countWorkFlow = 0;/* await WorkFlowApplication.GetCountAllWorkFlows(workshopId); */
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { { "countWorkFlow", countWorkFlow } };
}
@@ -45,6 +42,7 @@
<link href="~/AssetsClient/libs/font-awesome/css/font-awesome.min.css?ver=@clientVersion" rel="stylesheet" />
<link href="~/AssetsClient/css/material-design-iconic-font.min.css?ver=@clientVersion" rel="stylesheet" />
<script src="~/AssetsAdminNew/libs/wavesurfer/wavesurfer.min.js"></script>
<link href="~/AdminTheme/assets/sweet-alert/sweet-alert.min.css" rel="stylesheet">
<link href="~/AssetsClient/libs/select2/css/select2.min.css" rel="stylesheet" />
@@ -60,8 +58,8 @@
<div id="overlay" class="d-lg-none"></div>
<!-- End Dark Overlay -->
<!-- ========== Right Sidebar Start ========== -->
<partial name="_Menu" view-data="viewData"/>
<partial name="_headerAndFooter" view-data="viewData"/>
<partial name="_Menu" view-data="viewData" />
<partial name="_headerAndFooter" view-data="viewData" />
<!-- ============================================================== -->
<!-- Start right Content here -->
@@ -84,6 +82,7 @@
<script src="~/AssetsClient/js/jquery-3.7.1.min.js"></script>
<script src="~/AssetsClient/js/bootstrap.bundle.min.js"></script>
<script src="~/assetsclient/js/services/ajax-service.js?ver=@clientVersion"></script>
<script src="~/AssetsClient/libs/intro.js/intro.js"></script>
<script src="~/AssetsClient/libs/select2/js/select2.js"></script>
<script src="~/AssetsClient/libs/select2/js/i18n/fa.js"></script>
@@ -404,9 +403,9 @@
</script>
<script src="~/assetsclient/js/tickets_action.js?ver=@Version.StyleVersion"></script>
<script src="~/assetsclient/js/tickets_action.js?ver=@clientVersion"></script>
@RenderSection("Script", false)
<script>
// window.addEventListener('load', function () {
// console.clear();

View File

@@ -0,0 +1,600 @@
/* Sidebar Navigation */
.sidebar-menu {
display: flex;
flex-direction: column;
height: 100vh;
}
.sidebar-navigation {
background: linear-gradient(180deg, #30C2C2 0%, #077171 100%);
border-radius: 35px 0px 0px 35px;
padding: 0;
width: 280px;
height: 100%;
position: fixed;
margin-right: -300px;
background-color: #100901;
/*overflow: auto;*/
z-index: 1011 !important;
overflow: hidden;
transition: ease .3s;
}
/* submenu */
.toggle-submenu {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
position: relative;
}
.has-submenu {
position: relative;
}
.arrow-icon {
display: flex;
align-items: center;
transition: transform 0.3s ease;
}
.arrow-icon svg {
transition: transform 0.3s ease;
transform: rotate(0deg);
}
.toggle-submenu.open .arrow-icon svg {
transform: rotate(-90deg);
}
.submenu {
position: relative;
display: none;
padding-right: 2rem;
}
.submenu:before {
content: "";
position: absolute;
width: 3px;
height: 95%;
background: #69C3C3;
right: 18px;
top: 5px;
border-radius: 10px;
}
.submenu li {
position: relative;
}
.submenu li:before {
content: "";
position: absolute;
width: 9px;
height: 9px;
background: #69C3C3;
right: -20px;
top: 50%;
border-radius: 50px;
transform: translate(0px, -50%);
}
.sidebar-navigation > .sidebar-menu > ul li > .submenu li {
padding: 6px 10px !important;
}
.submenu li.active:before {
background: #ffffff;
}
.submenu.open {
display: block;
}
/* submenu */
.menuContainer {
height: 75vh;
overflow-y: auto;
overflow-x: hidden;
padding: 0 0.6rem;
flex-grow: 1;
scrollbar-gutter: stable;
direction: ltr;
/*writing-mode: horizontal-tb;*/
/* scrollbar-width: thin;
scrollbar-color: rgba(0, 0, 0, 0.3) transparent;*/
user-select: none;
}
.menuContainer::-webkit-scrollbar {
width: 6px;
direction: ltr !important;
}
.menuContainer::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.3);
border-radius: 10px;
}
.menuContainer::-webkit-scrollbar-track {
background: transparent;
}
.menuContainer .scrollbar-track {
background: transparent !important;
border-radius: 10px;
}
.menuContainer .scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.3) !important;
border-radius: 10px !important;
}
.menuContainer .scrollbar-track-x {
display: none !important;
}
.menuContainer .scrollbar-thumb:hover {
background-color: rgba(0, 0, 0, 0.5) !important;
}
.active-sidebar-navigation {
margin-right: 0;
}
.sidebar-navigation .logo {
padding: 10px;
width: 125px;
text-align: center;
transition: ease .4s;
}
.sidebar-navigation > .sidebar-menu > ul li {
border-radius: 22px;
padding: 6px;
margin: 5px 2px;
/* padding: 8px 0;
margin: 6px auto; */
border: 0.5px solid transparent;
transition: ease .4s;
}
.sidebar-navigation > .sidebar-menu > ul li:hover {
border: 0.5px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.15);
}
.sidebar-navigation > .sidebar-menu > ul li.active {
border: 0.5px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.15);
}
.sidebar-navigation > .sidebar-menu > ul li a {
position: relative;
color: #ffffff;
text-align: right;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
display: flex;
align-items: center;
cursor: pointer;
transition: ease .4s;
}
.sidebar-navigation > .sidebar-menu > ul li a span {
transition: ease .4s;
}
.sidebar-navigation > .sidebar-menu > ul li a span.alert-number {
background-color: #dd2a2a;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40px;
color: #fff;
font-weight: 600;
padding: 2px 0 0 0;
}
.sidebar-navigation > .sidebar-menu > ul li a svg {
margin: auto 2px;
}
/* Sidebar Navigation Small */
/*.sidebar-navigation.small {
padding: 2rem 0.6rem !important;
width: 80px;
transition: ease .4s;*/
/* transition-timing-function: cubic-bezier(0.9,0,1,1); */
/*}*/
.sidebar-navigation.small {
margin-right: -280px;
transition: ease .4s;
/* transition-timing-function: cubic-bezier(0.9,0,1,1); */
}
.sidebar-navigation.small .menuContainer {
overflow-y: visible;
}
.sidebar-navigation.small .logo {
padding: 0;
width: 50px;
height: 50px;
text-align: center;
}
.sidebar-navigation.small > .sidebar-menu > ul li a span {
display: none;
transition: ease .4s;
/* transition-timing-function: cubic-bezier(0.9,0,1,1); */
}
.sidebar-navigation.small > .sidebar-menu > ul li {
border-radius: 50px;
padding: 8px 0;
margin: 6px auto;
display: flex;
align-items: center;
justify-content: center;
}
.sidebar-navigation.small > .sidebar-menu > ul li .submenu {
position: absolute;
width: auto;
z-index: 60;
background-color: #000;
background: linear-gradient(180deg, #30C2C2 0%, #077171 100%);
top: 0;
right: 42px;
border-radius: 10px 0 0 10px;
display: none;
}
.sidebar-navigation.small > .sidebar-menu > ul li:hover .submenu a span {
display: block;
white-space: nowrap;
text-align: right;
}
.sidebar-navigation.small > .sidebar-menu > ul li:hover .submenu {
display: block !important;
}
.static-menu {
z-index: 1010;
}
@media (max-width: 1366px) {
.sidebar-navigation .logo {
width: 95px;
}
.sidebar-navigation {
width: 280px;
/*padding: 1rem !important;*/
}
.sidebar-navigation > .sidebar-menu > ul li {
padding: 3px;
margin: 3px 2px;
}
.sidebar-navigation > .sidebar-menu > ul li a {
font-size: 12px;
font-weight: 400;
}
}
@media (max-width: 1366px) {
.sidebar-navigation .logo {
width: 95px;
}
.sidebar-navigation {
width: 280px;
/*padding: 1rem !important;*/
}
.sidebar-navigation > .sidebar-menu > ul li {
padding: 5px 8px;
margin: 3px 2px;
}
.sidebar-navigation > .sidebar-menu > ul li a {
font-size: 12px;
font-weight: 400;
}
}
/* Large devices (desktops, 1200px and up) */
@media (min-width: 992px) {
.sidebar-navigation > .sidebar-menu {
display: block;
}
.sidebar-navigation > .sidebar-menu-mobile {
display: none;
}
.sidebar-navigation > .sidebar-menu-mobile > ul {
font-size: 13px;
font-weight: 200;
}
.sidebar-navigation > .sidebar-menu-mobile > ul li {
border-radius: 15px;
border: 0.5px solid transparent;
}
.sidebar-navigation > .sidebar-menu-mobile > ul li a svg {
width: 50px;
height: 50px;
}
.sidebar-navigation > .sidebar-menu-mobile > ul li:hover {
border: 0.5px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.15);
}
/* .sidebar-navigation.small {
padding: 2rem 0.6rem !important;
width: 80px;
margin-right: 0;
transition: ease .4s;
transition-timing-function: cubic-bezier(0.9,0,1,1);
}*/
.sidebar-navigation.small {
margin-right: -280px;
transition: ease .4s;
}
.sidebar-navigation.small .logo {
padding: 0;
width: 50px;
height: 50px;
text-align: center;
}
.sidebar-navigation.small > .sidebar-menu > ul li a .alert-number {
display: flex;
position: absolute;
top: -15px;
right: -9px;
width: 20px;
height: 20px;
font-size: 12px;
}
.sidebar-navigation.small > .sidebar-menu > ul li {
border-radius: 50px;
padding: 8px 0;
margin: 6px auto;
display: flex;
align-items: center;
justify-content: center;
}
.small .toggle {
/*margin-right: 85px;*/
margin-right: 10px;
transition: ease .4s;
}
.content-container.small {
margin-right: 0;
transition: ease .4s;
}
}
/* Large devices (desktops, 992px and up) */
@media (max-width: 992px) {
.sidebar-navigation {
width: 280px;
/* height: 100% !important; */
}
.sidebar-navigation > .sidebar-menu-mobile ul {
font-size: 13px;
font-weight: 200;
}
.sidebar-navigation > .sidebar-menu-mobile ul li {
border-radius: 15px;
margin: 1px 0;
border: 0.5px solid transparent;
transition: all .3s ease;
}
.sidebar-navigation > .sidebar-menu-mobile ul li a svg {
width: 35px;
height: 35px;
stroke-width: 1.5px;
}
.sidebar-navigation > .sidebar-menu-mobile ul li:hover {
border: 0.5px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.15);
}
.sidebar-navigation .sidebar-menu-mobile ul li.active {
border: 0.5px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.15);
}
.header-left {
display: none !important;
}
/* Sidebar Navigation Small */
.sidebar-navigation.small {
padding: 0 !important;
width: 0;
right: 0;
}
.sidebar-navigation.small > .sidebar-menu-mobile {
display: none !important;
}
.sidebar-navigation.small > .sidebar-menu-mobile > ul li a span {
display: none;
}
.sidebar-navigation.small > .sidebar-menu-mobile > ul li {
display: none;
}
.small .toggle {
margin-right: 10px;
transition: ease .4s;
}
.content-container {
margin: 0 0 80px 0;
transition: ease .4s;
}
}
@media (max-width: 576px) {
.sidebar-navigation .sidebar-menu-mobile > ul li a svg {
width: 40px;
height: 40px;
}
}
@media (max-width: 416px) {
.sidebar-navigation .sidebar-menu-mobile > ul li a svg {
width: 40px;
height: 40px;
}
}
/*-----------------------*/
/* Profile in Mobile Section */
.sidebar-navigation .profile-sidebar-menu-mobile {
width: 100%;
background: rgba(255, 255, 255, 0.17);
border-radius: 15px 15px 15px 30px;
text-align: right;
bottom: 15px;
padding: 5px;
flex-direction: column;
justify-content: space-between;
}
.sidebar-navigation .profile-sidebar-menu-mobile svg {
width: 30px;
}
.sidebar-navigation .profile-sidebar-menu-mobile a {
width: 100%;
font-size: 13px;
border: 0.5px solid transparent;
padding: 3px;
margin: 1px 5px;
transition: all .3s ease;
}
.sidebar-navigation .profile-sidebar-menu-mobile a:hover {
border-radius: 15px;
border: 0.5px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.15);
}
.sidebar-navigation .profile-sidebar-menu-mobile .li-profile.active {
border-radius: 15px;
border: 0.5px solid rgba(255, 255, 255, 0.18);
background: rgba(255, 255, 255, 0.15);
}
.sidebar-navigation .profile-sidebar-menu-mobile img {
border-radius: 50%;
width: 40px;
}
.sidebar-navigation .profile-sidebar-menu-mobile a svg {
width: 30px;
height: 30px;
color: #30C2C2;
}
/* End Profile in Mobile Section */