using _0_Framework.Application; using _0_Framework.Domain.CustomizeCheckoutShared.Base; using _0_Framework.Domain.CustomizeCheckoutShared.Enums; using _0_Framework.Domain.CustomizeCheckoutShared.ValueObjects; using _0_Framework.InfraStructure; using Company.Domain.CustomizeWorkshopEmployeeSettingsAgg.Entities; using Company.Domain.EmployeeAgg; using Company.Domain.RollCallAgg; using Company.Domain.RollCallEmployeeAgg; using Company.Domain.WorkshopAgg; using CompanyManagment.App.Contracts.HolidayItem; using CompanyManagment.App.Contracts.RollCall; using MD.PersianDateTime.Standard; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using _0_Framework.Exceptions; using OfficeOpenXml.Packaging.Ionic.Zip; namespace CompanyManagment.EFCore.Repository; public class RollCallRepository : RepositoryBase, IRollCallRepository { private readonly CompanyContext _context; private readonly IHolidayItemApplication _holidayItemApplication; private readonly IWorkshopRepository _workshopRepository; private readonly IRollCallMandatoryRepository _rollCallMandatoryRepository; public RollCallRepository(CompanyContext context, IHolidayItemApplication holidayItemApplication, IAuthHelper authHelper, IWorkshopRepository workshopRepository, IRollCallMandatoryRepository rollCallMandatoryRepository) : base(context) { _context = context; _holidayItemApplication = holidayItemApplication; _workshopRepository = workshopRepository; _rollCallMandatoryRepository = rollCallMandatoryRepository; } #region Pooya //حضور غیاب گروهی از پرسنل برای پرینت گروهی فیش حقوقی غیر رسمی موقت public List GetEmployeeRollCallsInDates(IEnumerable employeeIds, long workshopId, DateTime start, DateTime end) { var rollCalls = _context.RollCalls.Where(x => employeeIds.Contains(x.EmployeeId) && workshopId == x.WorkshopId && x.StartDate != null && x.EndDate != null && x.ShiftDate.Date >= start && x.ShiftDate.Date <= end).ToList(); int dateRange = (int)(end - start).TotalDays + 1; var completeDaysList = Enumerable.Range(0, dateRange).Select(offset => start.AddDays(offset).Date); List result = new(); foreach (var employeeId in employeeIds) { var absentRecords = completeDaysList.Where(x => !rollCalls.Any(y => y.EmployeeId == employeeId && x.Date.Date == y.ShiftDate.Date)) .Select(x => new CheckoutDailyRollCallViewModel() { StartDate1 = null, EndDate1 = null, DateTimeGr = x.Date.Date, DayOfWeek = x.Date.DayOfWeek.ToString(), RollCallDateFa = x.Date.ToFarsi() }); var presentDays = rollCalls.Where(x => x.EmployeeId == employeeId).GroupBy(x => x.ShiftDate.Date) .Select(x => { var orderedRollcalls = x.OrderBy(y => y.ShiftDate); return new CheckoutDailyRollCallViewModel() { StartDate1 = orderedRollcalls.FirstOrDefault().StartDate.Value.ToString("HH:mm"), EndDate1 = orderedRollcalls.FirstOrDefault().EndDate.Value.ToString("HH:mm"), StartDate2 = orderedRollcalls.Skip(1).FirstOrDefault()?.StartDate?.ToString("HH:mm") ?? "", EndDate2 = orderedRollcalls.Skip(1).FirstOrDefault()?.EndDate?.ToString("HH:mm") ?? "", TotalhourseSpan = new TimeSpan(x.Where(y => y.EndDate != null) .Sum(y => (y.EndDate - y.StartDate)!.Value.Ticks)), DayOfWeek = x.Key.DayOfWeek.DayOfWeeKToPersian(), RollCallDateFa = x.Key.Date.ToFarsi(), DateTimeGr = x.Key.Date, IsSliced = x.Count() > 2 }; }); presentDays = presentDays.Select(x => new CheckoutDailyRollCallViewModel { StartDate1 = x.StartDate1, EndDate1 = x.EndDate1, EndDate2 = x.EndDate2, StartDate2 = x.StartDate2, TotalWorkingHours = $"{(int)(x.TotalhourseSpan.TotalHours)}:{x.TotalhourseSpan.Minutes.ToString("00")}", DayOfWeek = x.DayOfWeek, RollCallDateFa = x.RollCallDateFa, DateTimeGr = x.DateTimeGr, IsSliced = x.IsSliced }); List checkoutDailyRollCalls = presentDays.Concat(absentRecords).OrderBy(x => x.DateTimeGr).ToList(); var resultItem = new PersonnelCheckoutDailyRollCallViewModel() { EmployeeId = employeeId, WorkshopId = workshopId, DailyRollCalls = checkoutDailyRollCalls }; result.Add(resultItem); } return result; } //قطع شده توسط سیستم public List GetRollCallWorkFlowsCutByBgService(long workshopId, DateTime start, DateTime end) { var personnelCode = _context.PersonnelCodeSet.Where(x => x.WorkshopId == workshopId); var rollCalls = _context.RollCalls .Where(x => personnelCode.Any(y => y.EmployeeId == x.EmployeeId) && x.RollCallModifyType == RollCallModifyType.CutByBgService && x.WorkshopId == workshopId && x.ShiftDate.Date >= start.Date && x.ShiftDate.Date <= end.Date).ToList(); var names = _context.RollCallEmployees.Where(x => x.WorkshopId == workshopId) .Select(x => new { x.EmployeeId, x.WorkshopId, x.EmployeeFullName }).ToList(); return rollCalls.GroupBy(x => x.ShiftDate.Date) .Select(g => new RollCallsByDateViewModel() { DateGr = g.Key, DateFa = g.Key.ToFarsi(), ActiveEmployees = g.Select(x => new RollCallViewModel() { EmployeeId = x.EmployeeId, EmployeeFullName = names.FirstOrDefault(y => y.EmployeeId == x.EmployeeId)?.EmployeeFullName ?? "", WorkshopId = x.WorkshopId, Id = x.id, StartDate = x.StartDate, EndDate = x.EndDate }) }) .ToList(); } //حضور غیاب در پرینت فیش حقوقی رسمی public List GetEmployeeRollCallsForMonth(long employeeId, long workshopId, DateTime startMonthDay, DateTime endMonthDay) { var firstDayOfMonth = $"{startMonthDay.ToFarsi().Substring(0, 8)}01".ToGeorgianDateTime(); var endFarvardin = "1404/01/31".ToGeorgianDateTime(); //گرفتن ساعت استراحت پرسنل از تنظیمات #region breakTime BaseCustomizeEntity settings = _context.CustomizeWorkshopEmployeeSettings.AsSplitQuery().FirstOrDefault(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId); //اگر ساعت استراحت پرسنل وجود نداشت صفر است var breakTime = settings == null ? new BreakTime(false, new TimeOnly()) : settings.BreakTime; #endregion var rollCalls = _context.RollCalls.Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId && x.StartDate != null && x.EndDate != null && x.RollCallModifyType != RollCallModifyType.Undefined && x.ShiftDate.Date >= startMonthDay && x.ShiftDate.Date <= endMonthDay).ToList(); var leaves = _context.LeaveList.Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && x.EndLeave.Date >= startMonthDay.Date && x.StartLeave.Date <= endMonthDay.Date).ToList(); var year = Convert.ToInt32(firstDayOfMonth.ToFarsi().Substring(0, 4)); var month = Convert.ToInt32(firstDayOfMonth.ToFarsi().Substring(5, 2)); var firstDayOfCurrentMonth = new DateTime(year, month, 1, new PersianCalendar()); if (month == 12) { year += 1; month = 1; } else month += 1; var nextMonthDate = new DateTime(year, month, 1, new PersianCalendar()); var lastDayOfCurrentMonth = nextMonthDate.AddDays(-1); int dateRange = (int)(lastDayOfCurrentMonth - firstDayOfCurrentMonth).TotalDays + 1; var holidays = _holidayItemApplication.Search(new HolidayItemSearchModel() { HolidayYear = startMonthDay.ToFarsiYear() }); //all the dates from start to end, to be compared with present days to get absent dates var completeDaysList = Enumerable.Range(0, dateRange) .Select(offset => firstDayOfCurrentMonth.AddDays(offset).Date).ToList(); var absentRecords = completeDaysList .ExceptBy(rollCalls.Select(x => x.ShiftDate.Date), y => y.Date) .Select(x => { var leave = leaves.FirstOrDefault(y => y.EmployeeId == employeeId && y.EndLeave.Date >= x.Date && y.StartLeave.Date <= x.Date); var isHoliday = holidays.Any(y => y.HolidaydateGr == x.Date); var isFriday = x.Date.DayOfWeek == DayOfWeek.Friday; var isNormalWorkingDay = isHoliday == false && isFriday == false; return new CheckoutDailyRollCallViewModel() { StartDate1 = null, EndDate1 = null, DateTimeGr = x.Date, DayOfWeek = x.Date.DayOfWeek.ToString(), RollCallDateFa = x.Date.ToFarsi(), LeaveType = leave != null ? leave.LeaveType : "", IsAbsent = leave == null && isNormalWorkingDay }; }); var presentDays = rollCalls.GroupBy(x => x.ShiftDate.Date).Select(x => { var orderedRollcalls = x.OrderBy(y => y.ShiftDate); var rollCallTimeSpanPerDay = new TimeSpan(x.Where(y => y.EndDate != null).Sum(y => (y.EndDate - y.StartDate)!.Value.Ticks)); TimeSpan breakTimePerDay; if (startMonthDay > endFarvardin) breakTimePerDay = RollCallMandatoryRepository.CalculateBreakTime(x.First().BreakTimeSpan, rollCallTimeSpanPerDay); else breakTimePerDay = RollCallMandatoryRepository.CalculateBreakTime(breakTime, rollCallTimeSpanPerDay); return new CheckoutDailyRollCallViewModel() { StartDate1 = orderedRollcalls.FirstOrDefault().StartDate.Value.ToString("HH:mm"), EndDate1 = orderedRollcalls.FirstOrDefault().EndDate.Value.ToString("HH:mm"), StartDate2 = orderedRollcalls.Skip(1).FirstOrDefault()?.StartDate?.ToString("HH:mm") ?? "", EndDate2 = orderedRollcalls.Skip(1).FirstOrDefault()?.EndDate?.ToString("HH:mm") ?? "", TotalhourseSpan = rollCallTimeSpanPerDay - breakTimePerDay, BreakTimeTimeSpan = breakTimePerDay, DayOfWeek = x.Key.DayOfWeek.DayOfWeeKToPersian(), RollCallDateFa = x.Key.Date.ToFarsi(), DateTimeGr = x.Key.Date, IsSliced = x.Count() > 2, IsAbsent = false }; }); presentDays = presentDays.Select(x => new CheckoutDailyRollCallViewModel { StartDate1 = x.StartDate1, EndDate1 = x.EndDate1, EndDate2 = x.EndDate2, StartDate2 = x.StartDate2, TotalWorkingHours = $"{(int)(x.TotalhourseSpan.TotalHours)}:{x.TotalhourseSpan.Minutes.ToString("00")}", BreakTimeString = $"{(int)(x.BreakTimeTimeSpan.TotalHours)}:{x.BreakTimeTimeSpan.Minutes.ToString("00")}", TotalhourseSpan = x.TotalhourseSpan, BreakTimeTimeSpan = x.BreakTimeTimeSpan, DayOfWeek = x.DayOfWeek, RollCallDateFa = x.RollCallDateFa, DateTimeGr = x.DateTimeGr, IsSliced = x.IsSliced, IsAbsent = false }); var result = presentDays.Concat(absentRecords).OrderBy(x => x.DateTimeGr).ToList(); result.ForEach(x => { x.IsHoliday = holidays.Any(y => x.DateTimeGr.Date == y.HolidaydateGr.Date); x.IsFriday = x.DateTimeGr.DayOfWeek == DayOfWeek.Friday; }); return result; } //محاسبه کارکرد پرسنل در هنگام جستجو public TimeSpan GetEmployeeRollCallTimeSpanForDuration(long employeeId, long workshopId, DateTime start, DateTime end) { var rollCalls = _context.RollCalls.Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId && x.StartDate != null && x.EndDate != null && x.RollCallModifyType != RollCallModifyType.Undefined && x.ShiftDate.Date >= start && x.ShiftDate.Date <= end).ToList(); return new TimeSpan(rollCalls.Sum(x => (x.EndDate - x.StartDate).Value.Ticks)); } //حضور غیاب گروهی از پرسنل برای پرینت گروهی فیش حقوقی غیر رسمی نهایی public List GetEmployeeRollCallsForMonth(IEnumerable employeeIds, long workshopId, DateTime start, DateTime end) { if (workshopId == 170) return GetEmployeeRollCallsForMonthForKababMahdi(employeeIds, workshopId, start, end); var rollCalls = _context.RollCalls.Where(x => employeeIds.Contains(x.EmployeeId) && workshopId == x.WorkshopId && x.StartDate != null && x.EndDate != null && x.RollCallModifyType != RollCallModifyType.Undefined && x.ShiftDate.Date >= start && x.ShiftDate.Date <= end).ToList(); var leaves = _context.LeaveList.Where(x => x.WorkshopId == workshopId && employeeIds.Contains(x.EmployeeId) && x.EndLeave.Date >= start.Date && x.StartLeave.Date <= end.Date).ToList(); var year = Convert.ToInt32(start.ToFarsi().Substring(0, 4)); var month = Convert.ToInt32(start.ToFarsi().Substring(5, 2)); var firstDayOfCurrentMonth = new DateTime(year, month, 1, new PersianCalendar()); if (month == 12) { year += 1; month = 1; } else month += 1; var nextMonthDate = new DateTime(year, month, 1, new PersianCalendar()); var lastDayOfCurrentMonth = nextMonthDate.AddDays(-1); int dateRange = (int)(lastDayOfCurrentMonth - firstDayOfCurrentMonth).TotalDays + 1; var employeeSettingsList = _context.CustomizeWorkshopEmployeeSettings.AsSplitQuery().Where(x => x.WorkshopId == workshopId).ToList(); var holidays = _holidayItemApplication.Search(new HolidayItemSearchModel() { HolidayYear = start.ToFarsiYear() }); //all the dates from start to end, to be compared with present days to get absent dates var completeDaysList = Enumerable.Range(0, dateRange).Select(offset => start.AddDays(offset).Date).ToList(); List result = new(); foreach (var employeeId in employeeIds) { var settings = employeeSettingsList.FirstOrDefault(x => x.EmployeeId == employeeId); var worksInFriday = settings.FridayWork != FridayWork.Default; var worksInHolidays = settings.HolidayWork != HolidayWork.Default; var absentRecords = completeDaysList.Where(x => !rollCalls.Any(y => y.EmployeeId == employeeId && x.Date.Date == y.ShiftDate.Date)) .Select(x => { var leave = leaves.FirstOrDefault(y => y.EmployeeId == employeeId && y.EndLeave.Date >= x.Date && y.StartLeave.Date <= x.Date); var isHoliday = holidays.Any(y => y.HolidaydateGr == x.Date); var isFriday = x.Date.DayOfWeek == DayOfWeek.Friday; var isNormalWorkingDay = isHoliday == false && isFriday == false; return new CheckoutDailyRollCallViewModel() { StartDate1 = null, EndDate1 = null, DateTimeGr = x.Date, DayOfWeek = x.Date.DayOfWeek.ToString(), RollCallDateFa = x.Date.ToFarsi(), LeaveType = leave != null ? leave.LeaveType : "", IsAbsent = leave == null && (isNormalWorkingDay || (worksInFriday && x.Date.DayOfWeek == DayOfWeek.Friday) || (worksInHolidays && isHoliday)) }; }); var presentDays = rollCalls.Where(x => x.EmployeeId == employeeId).GroupBy(x => x.ShiftDate.Date) .Select(x => { var orderedRollcalls = x.OrderBy(y => y.StartDate!.Value).ToList(); var firstRollCall = orderedRollcalls.FirstOrDefault(); var secondRollCall = orderedRollcalls.Skip(1).FirstOrDefault(); //این برای این هست که ببینه اگر که این شخص گردشی بوده و دوبار وارد در دو شیفت مختلف وارد شده شیفت دوم تاثیر نخواهد گذاشت //منطق کباب مهدی!!!! #region SecondTimeDiff var hasSecondTimeDiff = false; if (settings.WorkshopShiftStatus == WorkshopShiftStatus.Rotating) { if (firstRollCall != null && secondRollCall != null) { var firstShift = FindRotatingShift(firstRollCall.StartDate.Value, firstRollCall.EndDate.Value, settings.CustomizeRotatingShifts); var secondShift = FindRotatingShift(secondRollCall.StartDate.Value, secondRollCall.EndDate.Value, settings.CustomizeRotatingShifts); if (firstShift.start == secondShift.start && firstShift.end == secondShift.end) { hasSecondTimeDiff = true; } } } else { hasSecondTimeDiff = true; } #endregion return new CheckoutDailyRollCallViewModel() { StartDate1 = firstRollCall?.StartDate?.ToString("HH:mm"), EndDate1 = firstRollCall?.EndDate?.ToString("HH:mm"), StartDate2 = secondRollCall?.StartDate?.ToString("HH:mm") ?? "", EndDate2 = secondRollCall?.EndDate?.ToString("HH:mm") ?? "", TotalhourseSpan = new TimeSpan(x.Where(y => y.EndDate != null) .Sum(y => (y.EndDate - y.StartDate)!.Value.Ticks)), DayOfWeek = x.Key.DayOfWeek.DayOfWeeKToPersian(), RollCallDateFa = x.Key.Date.ToFarsi(), DateTimeGr = x.Key.Date, IsSliced = x.Count() > 2, IsAbsent = false, EnterDifferencesMinutes1 = firstRollCall != null && firstRollCall.LateEntryDuration > TimeSpan.Zero ? CalculateEntryMinuteDifference(firstRollCall.EarlyEntryDuration, firstRollCall.LateEntryDuration) : "", ExitDifferencesMinutes1 = firstRollCall != null && firstRollCall.EarlyExitDuration > TimeSpan.Zero ? CalculateExitMinuteDifference(firstRollCall.EarlyExitDuration, firstRollCall.LateExitDuration) : "", EnterDifferencesMinutes2 = secondRollCall != null && secondRollCall.LateEntryDuration > TimeSpan.Zero && hasSecondTimeDiff ? CalculateEntryMinuteDifference(secondRollCall.EarlyEntryDuration, secondRollCall.LateEntryDuration) : "", ExitDifferencesMinutes2 = secondRollCall != null && secondRollCall.EarlyExitDuration > TimeSpan.Zero && hasSecondTimeDiff ? CalculateExitMinuteDifference(secondRollCall.EarlyExitDuration, secondRollCall.LateExitDuration) : "" }; }); presentDays = presentDays.Select(x => new CheckoutDailyRollCallViewModel { StartDate1 = x.StartDate1, EndDate1 = x.EndDate1, EndDate2 = x.EndDate2, StartDate2 = x.StartDate2, TotalWorkingHours = $"{(int)(x.TotalhourseSpan.TotalHours)}:{x.TotalhourseSpan.Minutes.ToString("00")}", DayOfWeek = x.DayOfWeek, RollCallDateFa = x.RollCallDateFa, DateTimeGr = x.DateTimeGr, IsSliced = x.IsSliced, IsAbsent = false, EnterDifferencesMinutes1 = x.EnterDifferencesMinutes1, ExitDifferencesMinutes1 = x.ExitDifferencesMinutes1, EnterDifferencesMinutes2 = x.EnterDifferencesMinutes2, ExitDifferencesMinutes2 = x.ExitDifferencesMinutes2 }); List checkoutDailyRollCalls = presentDays.Concat(absentRecords).OrderBy(x => x.DateTimeGr).ToList(); checkoutDailyRollCalls.ForEach(x => { x.IsFriday = x.DateTimeGr.DayOfWeek == DayOfWeek.Friday; x.IsHoliday = holidays.Any(y => y.HolidaydateGr == x.DateTimeGr); }); var resultItem = new PersonnelCheckoutDailyRollCallViewModel() { EmployeeId = employeeId, WorkshopId = workshopId, DailyRollCalls = checkoutDailyRollCalls }; result.Add(resultItem); } return result; } private string CalculateExitMinuteDifference(TimeSpan early, TimeSpan late) { if (early == TimeSpan.Zero && late == TimeSpan.Zero) { return "-"; } else if (late != TimeSpan.Zero) { var minutes = late.TotalMinutes > 999 ? "999" : late.TotalMinutes.ToString(); return $"{minutes}+"; } else if (early != TimeSpan.Zero) { var minutes = early.TotalMinutes > 999 ? "999" : early.TotalMinutes.ToString(); return $"{minutes}-"; } else { return $""; } } private string CalculateEntryMinuteDifference(TimeSpan early, TimeSpan late) { if (early == TimeSpan.Zero && late == TimeSpan.Zero) { return "-"; } else if (late != TimeSpan.Zero) { var minutes = late.TotalMinutes > 999 ? "999" : late.TotalMinutes.ToString(); return $"{minutes}-"; } else if (early != TimeSpan.Zero) { var minutes = early.TotalMinutes > 999 ? "999" : early.TotalMinutes.ToString(); return $"{minutes}+"; } else { return $""; } } //خواسته کارفرما: هنگام جستجوی کارمند حضور غیاب های فرد در قالب های یک ماهه ظاهر شوند //این قالب ها متناسب با جستجوی کاربر، یا حضور غیاب های موجود، ممکن است بازه ای کمتر از یک ماه را شامل باشد //صورت گیرد AJAX ضمنا چون دیتا سینگین است // میدهد و سپس در جواب سرور ایندکس تاریخ بعدی را بدست می آورد و در ایجکس بعدی قرار میدهد NULL پیاده سازی: فرانت بار اول ایندکس تاریخ را // باشد یعنی قبل از این تاریخ دیگر فعالیتی نداشته NULL اگر در جواب سرور ایندکس تاریخ /// /// جستجوی سوابق حضور غیاب بر اساس کارمند /// public EmployeeRollCallsByMonthViewModel GetEmployeeRollCallsHistory(long employeeId, long workshopId, DateTime? startDateTime, DateTime? endDateTime, DateTime? exactDateTime, DateTime? dateIndex) { // اگر کد پرسنلی نداشت نباید چیزی نشان ندهد if (!_context.PersonnelCodeSet.Any(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId)) return new(); //get RollCallEmployee and RollCallEmployeeStatus for that employee in that workshop var employeeRollCallStatuses = _context.RollCallEmployees.Include(x => x.EmployeesStatus) .FirstOrDefault(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId); //if no status was found return empty 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(); //filling the list foreach (var status in employeeRollCallStatuses.EmployeesStatus) { var persianEndDate = new PersianDateTime(status.EndDate.Date); var persianStartDate = new PersianDateTime(status.StartDate.Date); var persianStartFirstDayOfMonth = new PersianDateTime(persianStartDate.Year, persianStartDate.Month, 1); for (PersianDateTime start = persianStartFirstDayOfMonth; start <= persianEndDate && start < PersianDateTime.Today.Date; start = start.AddMonths(1)) { activeMonths.Add(start); } } //might have duplicated records, this is the reason for distinct var activeMonthsList = activeMonths.OrderByDescending(x => x.Date).Distinct().ToList(); PersianDateTime startSearch = new(); PersianDateTime endSearch = new(); //if search has these parameters below if (startDateTime.HasValue && endDateTime.HasValue) { //change them to persian date time and store them startSearch = new PersianDateTime(startDateTime.Value); endSearch = new PersianDateTime(endDateTime.Value); //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 (exactDateTime.HasValue) { 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(); } //dateIndex is a kind of selector which moves if (dateIndex != null) { 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)); activeMonthsList = activeMonthsList.Where(x => dateIndexFirstOfMonth >= x).ToList(); } if (!activeMonthsList.Any()) return new(); //get the first month which user was active in PersianDateTime selectedMonthPersian = activeMonthsList.First(); DateTime selectedMonthFirstDay = selectedMonthPersian; DateTime nextMonthFirstDay = selectedMonthPersian.AddMonths(1); //get statuses which were active in the selected month var statusesOfMonth = employeeRollCallStatuses.EmployeesStatus.Where(x => x.EndDate >= selectedMonthFirstDay && x.StartDate < nextMonthFirstDay); //get leaves in the specified month var leavesQuery = _context.LeaveList.Where(x => (x.LeaveType == "استعلاجی" || (x.LeaveType == "استحقاقی" && x.PaidLeaveType == "روزانه")) && x.IsAccepted && x.EndLeave >= selectedMonthFirstDay && x.StartLeave < nextMonthFirstDay && x.WorkshopId == workshopId && x.EmployeeId == employeeId); //get employee rollcalls where there is no leave in that day, var rollCalls = _context.RollCalls.Where(x => x.EmployeeId == employeeId && !leavesQuery.Any(y => y.StartLeave.Date <= x.ShiftDate.Date && y.EndLeave.Date >= x.ShiftDate.Date) && x.WorkshopId == workshopId && x.StartDate != null && x.ShiftDate >= selectedMonthFirstDay && x.ShiftDate < nextMonthFirstDay && x.RollCallModifyType != RollCallModifyType.Undefined); var personnelCode = _context.PersonnelCodeSet.FirstOrDefault(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId) ?.PersonnelCode; var employeeName = _context.RollCallEmployees.Where(x => x.WorkshopId == workshopId) .Select(x => new { x.EmployeeId, x.EmployeeFullName }) .FirstOrDefault(x => x.EmployeeId == employeeId); var rollCallsList = rollCalls.ToList(); var leavesList = leavesQuery.ToList(); //number of days in period, since we are subtracting from next month's first day,there is no need for ".TotalDays() - 1" int dateRange = (int)(nextMonthFirstDay - selectedMonthFirstDay).TotalDays; var todayDate = DateTime.Now.Date; //all the dates from start to end, to be compared with present days to get absent dates var completeDaysList = Enumerable.Range(0, dateRange) .Select(offset => selectedMonthFirstDay.AddDays(offset).Date).Where(x => x.Date < todayDate); //if user search range is within a month for example, we dont want 30/31/29 days for month, we want it to be the size of the search range //user input = 2024/04/15~2024/04/21, output days count is 21-15+1=6 days from 15th to 21st if (exactDateTime.HasValue || (startDateTime.HasValue && endDateTime.HasValue)) completeDaysList = completeDaysList.Where(x => x.Date >= startSearch.Date && x.Date <= endSearch.Date); //get the days in which user was active and no rollcall with enddate==null is present //(im thinking about removing the rollcall condition) var result = completeDaysList.Where(x => !rollCallsList.Any(y => y.ShiftDate.Date == x.Date.Date && y.EndDate == null) && statusesOfMonth.Any(y => x >= y.StartDate.Date && x <= y.EndDate.Date)) .Select(x => { //iterating day by day //get the leaves for this day so we can find out if employee was on a permitted absence var leave = leavesList.FirstOrDefault(y => y.EndLeave >= x.Date && y.StartLeave <= x.Date); return new RollCallViewModel() { DateGr = x.Date, DateFa = x.Date.ToFarsi(), //all the roll calls which have a start date in this day RollCallTimesList = rollCallsList.Where(y => x.Date == y.ShiftDate.Date).Select(y => new RollCallTimeViewModel() { StartDate = y.StartDate.Value.ToString("HH:mm"), EndDate = y.EndDate!.Value.ToString("HH:mm"), StartDateGr = y.StartDate.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)), Reason = leave?.LeaveType ?? "", HasLeave = (leave?.PaidLeaveType == "روزانه" || leave?.LeaveType == "استعلاجی") ? true : false }; }); //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), 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)); //if there are any other months available that the selector return new EmployeeRollCallsByMonthViewModel() { PersianMonthName = selectedMonthPersian.ToString("MMMM"), PersianYear = selectedMonthPersian.ToString("yyyy"), DateGr = selectedMonthFirstDay, RollCalls = result.OrderByDescending(x => x.DateGr), DateIndex = activeMonthsList.Count > 1 ? activeMonthsList.Skip(1).First().ToString("yyyy/MM/dd") : null, TotalWorkingHours = new TimeSpan(result.Sum(x => x.TotalWorkingHoursSpan.Ticks)).ToFarsiHoursAndMinutes(), TotalWorkingHoursTimeSpan = $"{(int)totalWorkingHours.TotalHours}:{totalWorkingHours.Minutes:00}" }; } private string CalculateExitTimeDifferences(TimeSpan early, TimeSpan late) { if (early == TimeSpan.Zero && late == TimeSpan.Zero) { return "-"; } else if (late != TimeSpan.Zero) { return $"{late.Hours}:{late.Minutes:00}+"; } else if (early != TimeSpan.Zero) { return $"{early.Hours}:{early.Minutes:00}-"; } else { return $""; } } private string CalculateEntryTimeDifferences(TimeSpan early, TimeSpan late) { if (early == TimeSpan.Zero && late == TimeSpan.Zero) { return "-"; } else if (late != TimeSpan.Zero) { return $"{late.Hours}:{late.Minutes:00}-"; } else if (early != TimeSpan.Zero) { return $"{early.Hours}:{early.Minutes:00}+"; } else { return $""; } } private TimeSpan CalculateExitTimeDifferencesTimeSpan(TimeSpan early, TimeSpan late) { if (early == TimeSpan.Zero && late == TimeSpan.Zero) { return TimeSpan.Zero; } if (late != TimeSpan.Zero) { return late * 1; } if (early != TimeSpan.Zero) { return early * -1; } return TimeSpan.Zero; } private TimeSpan CalculateEntryTimeDifferencesTimeSpan(TimeSpan early, TimeSpan late) { if (early == TimeSpan.Zero && late == TimeSpan.Zero) { return TimeSpan.Zero; } if (late != TimeSpan.Zero) { return late * -1; } if (early != TimeSpan.Zero) { return early * 1; } return TimeSpan.Zero; } //Without Paginate, With EndDate==null, without undefined, Ordered Descending By StartDate public List GetEmployeeRollCallsHistoryAllInDates(long workshopId, long employeeId, DateTime start, DateTime end) { var withPersonnelCode = _context.PersonnelCodeSet.Where(x => x.WorkshopId == workshopId); return _context.RollCalls.Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && withPersonnelCode.Any(y => y.EmployeeId == x.EmployeeId) && (x.EndDate == null || x.EndDate.Value.Date >= start.Date) && x.ShiftDate.Date <= end.Date) .OrderByDescending(x => x.StartDate.Value) .Select(x => new RollCallViewModel { EmployeeId = employeeId, WorkshopId = workshopId, StartDate = x.StartDate, EndDate = x.EndDate, DateGr = x.ShiftDate, ShiftDate = x.ShiftDate }).ToList(); } //سوابق حضور غیاب کارگاه public RollCallsByDateViewModel GetWorkshopRollCallHistory(RollCallSearchModel searchModel) { //initialize DateTime searchDurationEnd = DateTime.Now.AddDays(-1).Date; DateTime searchDurationStart = DateTime.MinValue; //override if user has entered inputs (dates must be validated in the application layer) if (!string.IsNullOrWhiteSpace(searchModel.StarDateFa) && !string.IsNullOrWhiteSpace(searchModel.StarDateFa)) { searchDurationEnd = searchModel.EndDateFa.ToGeorgianDateTime().Date; searchDurationStart = searchModel.StarDateFa.ToGeorgianDateTime().AddDays(-1).Date; } else if (!string.IsNullOrWhiteSpace(searchModel.ExactDateFa)) { searchDurationEnd = searchModel.ExactDateFa.ToGeorgianDateTime().Date; searchDurationStart = searchModel.ExactDateFa.ToGeorgianDateTime().AddDays(-1).Date; } if (searchDurationEnd < searchDurationStart) return new(); DateTime dateIndex = searchDurationEnd.AddDays(-1 * (searchModel.DateIndex)).Date; if (dateIndex <= searchDurationStart) return new(); //get leaves for workshop that have been activated in dateIndex date var leavesQuery = _context.LeaveList.Where(x => x.WorkshopId == searchModel.WorkshopId && x.IsAccepted && (x.LeaveType == "استعلاجی" || (x.LeaveType == "استحقاقی" && x.PaidLeaveType == "روزانه")) && x.EndLeave.Date >= dateIndex && x.StartLeave.Date <= dateIndex); //roll calls for current workshop where shift start is in dateIndex date (filters today's shifts) var rollCallsQuery = _context.RollCalls .Where(x => x.WorkshopId == searchModel.WorkshopId && x.StartDate.HasValue && x.RollCallModifyType != RollCallModifyType.Undefined && x.ShiftDate < DateTime.Now.Date && x.ShiftDate.Date == dateIndex.Date); var personnelCodeQuery = _context.PersonnelCodeSet.Where(x => x.WorkshopId == searchModel.WorkshopId); //get active employees of workshop in dateIndex date var activeEmployeesQuery = _context.RollCallEmployees.Include(x => x.EmployeesStatus) .Where(x => x.WorkshopId == searchModel.WorkshopId && personnelCodeQuery.Any(y => y.EmployeeId == x.EmployeeId) && x.EmployeesStatus.Any(y => y.EndDate.Date >= dateIndex && y.StartDate.Date <= dateIndex) && !rollCallsQuery.Any(rc => rc.EmployeeId == x.EmployeeId && !rc.EndDate.HasValue)); if (searchModel.EmployeeId > 0) { rollCallsQuery = rollCallsQuery.Where(x => x.EmployeeId == searchModel.EmployeeId); activeEmployeesQuery = activeEmployeesQuery.Where(x => x.EmployeeId == searchModel.EmployeeId); leavesQuery = leavesQuery.Where(x => x.EmployeeId == searchModel.EmployeeId); } var personnelCodeList = personnelCodeQuery.ToList(); var activatedEmployeesList = activeEmployeesQuery.ToList(); rollCallsQuery = rollCallsQuery.Where(x => x.EndDate.HasValue); var rollCallsList = rollCallsQuery.ToList(); var leavesList = leavesQuery.ToList(); rollCallsList = rollCallsList.Where(x => leavesList.All(y => y.EmployeeId != x.EmployeeId)).ToList(); var result = new RollCallsByDateViewModel() { DateGr = dateIndex, DayOfWeekFa = dateIndex.DayOfWeek.DayOfWeeKToPersian(), IsHoliday = _holidayItemApplication.IsHoliday(dateIndex), DateFa = dateIndex.ToFarsi(), ActiveEmployees = activatedEmployeesList.Select(x => { var leave = leavesList.FirstOrDefault(y => y.EmployeeId == x.EmployeeId); var employeeRollCallsForDate = rollCallsList.Where(y => y.EmployeeId == x.EmployeeId).ToList(); return new RollCallViewModel() { EmployeeFullName = x.EmployeeFullName, EmployeeId = x.EmployeeId, Reason = leave == null ? "" : $"{leave.LeaveType}-{leave.PaidLeaveType}", 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(), TotalWorkingHoursSpan = new TimeSpan(employeeRollCallsForDate.Sum(y => (y.EndDate!.Value - y.StartDate!.Value).Ticks)), PersonnelCode = personnelCodeList.FirstOrDefault(y => y.EmployeeId == x.EmployeeId)?.PersonnelCode .ToString(), }; }) }; result.ActiveEmployees = result.ActiveEmployees.Select(x => new RollCallViewModel() { EmployeeFullName = x.EmployeeFullName, EmployeeId = x.EmployeeId, Reason = x.Reason, RollCallTimesList = x.RollCallTimesList, HasLeave = x.HasLeave, IsAbsent = x.IsAbsent, PersonnelCode = x.PersonnelCode, TotalWorkingHours = $"{(int)x.TotalWorkingHoursSpan.TotalHours}:{(x.TotalWorkingHoursSpan.Minutes).ToString("00")}" }); return result; } //برای دریافت سوابق به صورت collapsed public RollCallsByDateViewModel GetWorkshopRollCallHistoryCollapsed(RollCallSearchModel searchModel) { //initialize DateTime searchDurationEnd = DateTime.Now.AddDays(-1).Date; DateTime searchDurationStart = searchDurationEnd.AddDays(-16); //override if user has entered inputs (dates must be validated in the application layer) if (!string.IsNullOrWhiteSpace(searchModel.StarDateFa) && !string.IsNullOrWhiteSpace(searchModel.StarDateFa)) { searchDurationEnd = searchModel.EndDateFa.ToGeorgianDateTime().Date; searchDurationStart = searchModel.StarDateFa.ToGeorgianDateTime().AddDays(-1).Date; } else if (!string.IsNullOrWhiteSpace(searchModel.ExactDateFa)) { searchDurationEnd = searchModel.ExactDateFa.ToGeorgianDateTime().Date; searchDurationStart = searchModel.ExactDateFa.ToGeorgianDateTime().AddDays(-1).Date; } if (searchDurationEnd < searchDurationStart) return new(); DateTime dateIndex = searchDurationEnd.AddDays(-1 * (searchModel.DateIndex)).Date; if (dateIndex <= searchDurationStart) return new(); var activeEmployeesList = _context.RollCallEmployees.Include(x => x.EmployeesStatus) .Where(x => x.WorkshopId == searchModel.WorkshopId && x.EmployeesStatus.Any(y => y.EndDate.Date >= dateIndex && y.StartDate.Date <= dateIndex)) .ToList(); var result = new RollCallsByDateViewModel() { DateGr = dateIndex, DayOfWeekFa = dateIndex.DayOfWeek.DayOfWeeKToPersian(), IsHoliday = _holidayItemApplication.IsHoliday(dateIndex), DateFa = dateIndex.ToFarsi(), ActiveEmployeesCount = activeEmployeesList.Count() }; return result; } //سوابق غیبت public List GetWorkshopAbsentHistory(long workshopId, DateTime startSearch, DateTime endSearch) { if (endSearch.Date == DateTime.Now.Date) endSearch = endSearch.AddDays(-1); //get leaves for workshop that have been activated in dateIndex date var leavesQuery = _context.LeaveList.AsSplitQuery().Where(x => x.WorkshopId == workshopId && x.IsAccepted && (x.LeaveType == "استعلاجی" || (x.LeaveType == "استحقاقی" && x.PaidLeaveType == "روزانه")) && x.EndLeave.Date >= startSearch.Date && x.StartLeave.Date <= endSearch.Date); //roll calls for current workshop where shift start is in dateIndex date (filters today's shifts) var rollCallsQuery = _context.RollCalls.AsSplitQuery() .Where(x => x.WorkshopId == workshopId && x.StartDate.HasValue && x.ShiftDate.Date >= startSearch.Date && x.ShiftDate.Date <= endSearch.Date && x.RollCallModifyType != RollCallModifyType.Undefined); var withPersonnelCode = _context.PersonnelCodeSet.AsSplitQuery().Where(x => x.WorkshopId == workshopId); //get active employees of workshop in dateIndex date var activeEmployeesQuery = _context.RollCallEmployees.AsSplitQuery().Include(x => x.EmployeesStatus) .Where(x => x.WorkshopId == workshopId && x.EmployeesStatus.Any(y => y.EndDate.Date >= startSearch && y.StartDate.Date <= endSearch) && withPersonnelCode.Any(y => y.EmployeeId == x.EmployeeId)); var employeeSettingsList = _context.CustomizeWorkshopEmployeeSettings.AsSplitQuery().Where(x => x.WorkshopId == workshopId).ToList(); var rollCallsList = rollCallsQuery.ToList(); var activatedEmployeesList = activeEmployeesQuery.ToList(); var leavesList = leavesQuery.ToList(); //start of search year may be different to end of search year var holidays1 = _holidayItemApplication.Search(new HolidayItemSearchModel() { HolidayYear = startSearch.ToFarsiYear() }); var holidays2 = _holidayItemApplication.Search(new HolidayItemSearchModel() { HolidayYear = endSearch.ToFarsiYear() }); var totalHolidays = holidays1.Concat(holidays2).Distinct().ToList(); int daysCount = (int)((endSearch.Date - startSearch.Date).TotalDays + 1); List days = Enumerable.Range(0, daysCount).Select(x => startSearch.Date.AddDays(x)).ToList(); List result = new(); foreach (var day in days) { bool isHoliday = totalHolidays.Any(x => x.HolidaydateGr == day); List activatedEmployeesListInDay = new(); activatedEmployeesListInDay = activatedEmployeesList .Join(employeeSettingsList.Where(x => x.WeeklyOffDays == null || !x.WeeklyOffDays.Select(w => w.DayOfWeek).Contains(day.DayOfWeek)) , x => x.EmployeeId, y => y.EmployeeId, (x, _) => x).ToList(); if (isHoliday) { activatedEmployeesListInDay = activatedEmployeesListInDay .Join(employeeSettingsList.Where(x => x.WeeklyOffDays == null || x.HolidayWork != HolidayWork.Default) , x => x.EmployeeId, y => y.EmployeeId, (x, _) => x).ToList(); } ////in working days everyone should be present //if (day.DayOfWeek != DayOfWeek.Friday && isHoliday == false) //{ // activatedEmployeesListInDay = activatedEmployeesList; //} ////in fridays, friday workers should be present //else if (day.DayOfWeek == DayOfWeek.Friday) //{ // activatedEmployeesListInDay = activatedEmployeesList // .Join(employeeSettingsList.Where(x => x.FridayWork != FridayWork.Default) // , x => x.EmployeeId, y => y.EmployeeId, // (x, y) => x).ToList(); //} ////in holidays holiday workers should be present //else //{ // activatedEmployeesListInDay = activatedEmployeesList // .Join(employeeSettingsList.Where(x => x.HolidayWork != HolidayWork.Default) // , x => x.EmployeeId, y => y.EmployeeId, // (x, y) => x).ToList(); //} var item = new RollCallsByDateViewModel() { DateGr = day, DayOfWeekFa = day.DayOfWeek.DayOfWeeKToPersian(), DateFa = day.ToFarsi(), ActiveEmployees = activatedEmployeesListInDay.Where(x => x.EmployeesStatus.Any(y => y.StartDate.Date <= day && y.EndDate.AddDays(-1).Date >= day) && !leavesList.Any(y => y.EmployeeId == x.EmployeeId && y.StartLeave.Date <= day && y.EndLeave.Date >= day) && !rollCallsList.Any(rc => rc.EmployeeId == x.EmployeeId && rc.ShiftDate.Date == day.Date)) .Select(x => new RollCallViewModel() { EmployeeId = x.EmployeeId, EmployeeFullName = x.EmployeeFullName, Id = x.id, WorkshopId = x.WorkshopId }), IsHoliday = isHoliday, IsFriday = day.DayOfWeek == DayOfWeek.Friday }; result.Add(item); } return result; } //گزارش آنلاین حضور غیاب کارگاه public CurrentDayRollCall GetWorkshopCurrentDayRollCalls(long workshopId, WorkshopCurrentDayRollCallSearchModel searchModel) { var date = DateTime.Now.Date; //get active leaves for workshop which are active today and accepted and are either روزانه and استحقاقی or استعلاجی var leaves = _context.LeaveList .Where(x => (x.WorkshopId == workshopId && x.EndLeave.Date >= date && x.StartLeave.Date <= date && x.IsAccepted) && ((x.LeaveType == "استعلاجی" || (x.LeaveType == "استحقاقی" && x.PaidLeaveType == "روزانه")) || (x.LeaveType == "استحقاقی" && x.PaidLeaveType == "ساعتی" && x.StartLeave <= DateTime.Now && x.EndLeave >= DateTime.Now)) ); var personnelCodes = _context.PersonnelCodeSet.Where(x => x.WorkshopId == workshopId); //get currently working employees with leftWork table //var workingEmployees = // _context.LeftWorkList.Where(x => x.WorkshopId == workshopId && x.StartWorkDate < date && x.LeftWorkDate.Date > date); if (searchModel.PersonnelCode>0) { personnelCodes = personnelCodes.Where(x=>x.PersonnelCode == searchModel.PersonnelCode); } //get activated employees var activeEmployees = _context.RollCallEmployees.Include(x => x.EmployeesStatus) .Where(x => x.WorkshopId == workshopId && personnelCodes.Any(y => y.EmployeeId == x.EmployeeId) && x.EmployeesStatus.Any(y => y.StartDate.Date <= date && y.EndDate.Date >= date)); //get today's roll calls var rollCallsQuery = _context.RollCalls.Where(x => x.WorkshopId == workshopId && (x.ShiftDate.Date == date || x.EndDate == null) && x.RollCallModifyType != RollCallModifyType.Undefined); if (searchModel.EmployeeId>0) { leaves = leaves.Where(x=>x.WorkshopId == searchModel.EmployeeId); personnelCodes = personnelCodes.Where(x=>x.EmployeeId == searchModel.EmployeeId); activeEmployees = activeEmployees.Where(x=>x.EmployeeId == searchModel.EmployeeId); rollCallsQuery = rollCallsQuery.Where(x=>x.EmployeeId == searchModel.EmployeeId); } var mustBePresent = activeEmployees.Where(x => !leaves.Any(y => y.EmployeeId == x.EmployeeId)); var filteredRollCallQuery = rollCallsQuery.Where(x => mustBePresent .Any(y => x.EmployeeId == y.EmployeeId)).ToList(); var nameReferences = activeEmployees.ToList(); //presents list is ready var presentEmployees = filteredRollCallQuery.GroupBy(x => x.EmployeeId).Select(x => new RollCallViewModel() { PersonnelCode = personnelCodes .FirstOrDefault(y => y.EmployeeId == x.Key)? .PersonnelCode.ToString(), 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.OrderBy(r => r.StartDate).Select(y => new RollCallTimeViewModel() { StartDate = y.StartDate!.Value.ToString("HH:mm"), EndDate = y.EndDate?.ToString("HH:mm"), EntryTimeDifferences = CalculateEntryTimeDifferences(y.EarlyEntryDuration, y.LateEntryDuration), ExitTimeDifferences = CalculateExitTimeDifferences(y.EarlyExitDuration, y.LateExitDuration) }).ToList() }); presentEmployees = presentEmployees.Select(x => new RollCallViewModel() { PersonnelCode = x.PersonnelCode, EmployeeFullName = x.EmployeeFullName, EmployeeId = x.EmployeeId, TotalWorkingHours = $"{(int)x.TotalWorkingHoursSpan.TotalHours}:{x.TotalWorkingHoursSpan.Minutes.ToString("00")}", RollCallTimesList = x.RollCallTimesList }).OrderBy(x => x.RollCallTimesList.Any(y => y.EndDate != null)).ToList(); //absent ones are those who are active and not on the roll call list var absentsQuery = activeEmployees.AsEnumerable() .ExceptBy(presentEmployees.Select(x => x.EmployeeId), e => e.EmployeeId).ToList(); //get today's active leaves var employeesWithLeave = leaves.AsEnumerable() .Where(x => absentsQuery.Any(y => y.EmployeeId == x.EmployeeId)) .Select(x => new { x.EmployeeId, x.LeaveType }).ToList(); var personnelCodesList = personnelCodes.ToList(); //join leave and absents var absentsViewModel = absentsQuery.Select(x => new AbsentEmployeeViewModel() { PersonnelCode = personnelCodesList .FirstOrDefault(y => y.EmployeeId == x.EmployeeId)? .PersonnelCode.ToString(), EmployeeId = x.EmployeeId, EmployeeFullName = nameReferences.FirstOrDefault(y => x.EmployeeId == y.EmployeeId).EmployeeFullName, HasLeave = employeesWithLeave.Any(y => y.EmployeeId == x.EmployeeId), Reason = employeesWithLeave.FirstOrDefault(y => y.EmployeeId == x.EmployeeId)?.LeaveType }).ToList(); return new CurrentDayRollCall() { AbsentEmployees = absentsViewModel.ToList(), PresentEmployees = presentEmployees .OrderByDescending(x => x.RollCallTimesList.Any(y => string.IsNullOrWhiteSpace(y.EndDate))) .ThenByDescending(x => x.RollCallTimesList.Max(y => y.StartDate)).ToList() }; } public void AddRange(List rollCalls) { _context.RollCalls.AddRange(rollCalls); } //تداخل مرخصی در کارپوشه public EditRollCall GetByEmployeeIdAndWorkshopId(long employeeId, long workshopId) { throw new NotImplementedException(); } public EditRollCall GetById(long id) { throw new NotImplementedException(); } public List GetOverlappedRollCallsWithLeaveInDates(long workshopId, DateTime start, DateTime end) { var rollCalls = _context.RollCalls.Where(x => x.WorkshopId == workshopId && x.ShiftDate.Date >= start.Date && x.EndDate.HasValue && x.ShiftDate.Date <= end).ToList(); var leaves = _context.LeaveList.Where(x => x.WorkshopId == workshopId && x.EndLeave >= start && x.StartLeave <= end) .ToList(); var dailyLeaves = leaves.Where(x => x.PaidLeaveType == "روزانه").ToList(); var hourlyLeaves = leaves.Where(x => x.PaidLeaveType == "ساعتی").ToList(); var hourlyOverlappedRollCalls = rollCalls.Where(x => hourlyLeaves.Any(y => x.EmployeeId == y.EmployeeId && x.EndDate.Value >= y.StartLeave && x.StartDate.Value <= y.EndLeave)); var dailyOverlappedRollCalls = rollCalls.Where(x => dailyLeaves.Any(y => x.EmployeeId == y.EmployeeId && x.ShiftDate.Date == y.StartLeave.Date)); var hourlyOverlappedRollCallsResult = hourlyOverlappedRollCalls.Select(x => { var leave = leaves.FirstOrDefault(y => x.EmployeeId == y.EmployeeId && x.EndDate.Value >= y.StartLeave && x.StartDate.Value <= y.EndLeave); return new OverlappedRollCallWithLeave() { RollCallId = x.id, LeaveId = leave.id, StartOfOverlapDateTime = leave.StartLeave > x.StartDate.Value ? leave.StartLeave : x.StartDate.Value, EndOfOverlapDateTime = leave.EndLeave < x.EndDate ? leave.EndLeave : x.EndDate.Value, EmployeeFullName = x.EmployeeFullName, EmployeeId = x.EmployeeId }; }).ToList(); var dailyOverlappedRollCallsResult = dailyOverlappedRollCalls.Select(x => { var leave = leaves.FirstOrDefault(y => x.EmployeeId == y.EmployeeId && x.ShiftDate.Date == y.StartLeave.Date); return new OverlappedRollCallWithLeave() { RollCallId = x.id, LeaveId = leave.id, StartOfOverlapDateTime = leave.StartLeave > x.StartDate.Value ? leave.StartLeave : x.StartDate.Value, EndOfOverlapDateTime = leave.EndLeave < x.EndDate ? leave.EndLeave : x.EndDate.Value, EmployeeFullName = x.EmployeeFullName, EmployeeId = x.EmployeeId }; }).ToList(); var result = hourlyOverlappedRollCallsResult.Concat(dailyOverlappedRollCallsResult); return result.Select(x => new OverlappedRollCallWithLeave() { RollCallId = x.RollCallId, LeaveId = x.LeaveId, StartOfOverlapDateTime = x.StartOfOverlapDateTime, EndOfOverlapDateTime = x.EndOfOverlapDateTime, StartOfOverlapDateFa = x.StartOfOverlapDateTime.ToFarsi(), EndOfOverlapDateFa = x.EndOfOverlapDateTime.ToFarsi(), EndOfOverlapTime = x.EndOfOverlapDateTime.ToString("HH:mm"), StartOfOverlapTime = x.StartOfOverlapDateTime.ToString("HH:mm"), EmployeeFullName = x.EmployeeFullName, EmployeeId = x.EmployeeId }).ToList(); } public void UpdateRange(List rollCalls) { _context.RollCalls.UpdateRange(rollCalls); } //چون ویرایش دستی تمام حضور غیاب های یک روز را تعیین می کند //قبل از ثبت باید حضور غیاب های شخص در آن روز پاک شود تا رکورد تکراری نداشته باشیم //نیز پاک می شوند Undefined حضور و غیاب های public void RemoveEmployeeRollCallsInDate(long workshopId, long employeeId, DateTime date) { var rollCalls = _context.RollCalls.IgnoreQueryFilters().Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && x.ShiftDate == date.Date); if (!rollCalls.Any()) return; _context.RollCalls.RemoveRange(rollCalls); } /// /// دریافت حضور و غیاب های شخص در یک روز /// public List GetWorkshopEmployeeRollCallsForDate(long workshopId, long employeeId, DateTime date) { var names = _context.RollCallEmployees.Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId) .Select(x => new { x.EmployeeId, x.EmployeeFullName }).ToList(); var rollcalls = _context.RollCalls .Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && x.ShiftDate.Date == date.Date) .Select(x => new RollCallViewModel() { Id = x.id, StartDate = x.StartDate.Value, EndDate = x.EndDate.Value, DateFa = x.ShiftDate.Date.ToFarsi(), DateGr = x.ShiftDate.Date, StartDateFa = x.StartDate.Value.ToFarsi(), EndDateFa = x.EndDate != null ? x.EndDate.Value.ToFarsi() : null }).ToList(); return rollcalls.Select(x => new RollCallViewModel() { Id = x.Id, StartDate = x.StartDate, EndDate = x.EndDate, StartTimeString = x.StartDate!.Value.ToString("HH:mm"), EndTimeString = x.EndDate?.ToString("HH:mm"), DateFa = x.DateFa, DateGr = x.DateGr, StartDateFa = x.StartDateFa, EndDateFa = x.EndDateFa, EmployeeId = employeeId, EmployeeFullName = names.FirstOrDefault(e => e.EmployeeId == employeeId)!.EmployeeFullName, DayOfWeekFa = x.StartDate.Value.DayOfWeek.DayOfWeeKToPersian(), StartDayOfWeekFa = x.StartDate.Value.DayOfWeek.DayOfWeeKToPersian(), EndDayOfWeekFa = x.EndDate?.DayOfWeek.DayOfWeeKToPersian(), TotalWorkingHoursSpan = x.EndDate == null ? new() : x.EndDate.Value - x.StartDate.Value }).OrderBy(x => x.StartDate).ToList(); } /// /// دریافت حضور غیاب های غیر منقطع /// public IEnumerable GetNotSlicedRollCallsByWorkshopId(long workshopId, DateTime durationStart, DateTime durationEnd) { if (durationEnd.Date >= DateTime.Now.Date) durationEnd = DateTime.Now.AddDays(-1).Date; var names = _context.RollCallEmployees.Where(x => x.WorkshopId == workshopId).ToList(); var rollCalls = _context.RollCalls.Where(x => x.WorkshopId == workshopId && x.ShiftDate.Date >= durationStart && x.ShiftDate.Date <= durationEnd && x.EndDate.HasValue).ToList(); if (rollCalls == null || !rollCalls.Any()) return new List(); return rollCalls.GroupBy(x => x.ShiftDate.Date).SelectMany(x => x.GroupBy(y => y.EmployeeId).Where(y => y.Count() == 1) .SelectMany(y => y)).Select(x => new RollCallViewModel { Id = x.id, StartDate = x.StartDate.Value, EndDate = x.EndDate.Value, EmployeeId = x.EmployeeId, DateGr = x.ShiftDate.Date, EmployeeFullName = names.FirstOrDefault(y => y.EmployeeId == x.EmployeeId)?.EmployeeFullName ?? "" }); } public List GetRange(IEnumerable rollCallIds) { var query = _context.RollCalls.Where(x => rollCallIds.Contains(x.id)); var names = _context.RollCallEmployees .Where(x => query.Any(y => y.EmployeeId == x.EmployeeId && y.WorkshopId == x.WorkshopId)).ToList(); return query.Select(x => new RollCallViewModel { StartDate = x.StartDate.Value, EndDate = x.EndDate.Value, Id = x.id, EmployeeFullName = names.FirstOrDefault(y => y.EmployeeId == x.EmployeeId && y.WorkshopId == x.WorkshopId) .EmployeeFullName }).ToList(); } public List GetRangeByWorkshopIdEmployeeIdForDate(long workshopId, long employeeId, DateTime date) { return _context.RollCalls.Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && x.ShiftDate.Date == date).ToList(); } /// /// undefined دریافت حضور و غیاب های /// public List GetUndefinedRollCallWorkFlowsInDates(long workshopId, DateTime start, DateTime end) { { var rollCalls = _context.RollCalls.IgnoreQueryFilters() .Where(x => x.RollCallModifyType == RollCallModifyType.Undefined && x.WorkshopId == workshopId && x.ShiftDate.Date >= start.Date && x.ShiftDate.Date <= end.Date).ToList(); var names = _context.RollCallEmployees.Where(x => x.WorkshopId == workshopId) .Select(x => new { x.EmployeeId, x.WorkshopId, x.EmployeeFullName }).ToList(); var result = rollCalls.GroupBy(x => x.ShiftDate.Date) .Select(g => new RollCallsByDateViewModel() { DateGr = g.Key, DateFa = g.Key.ToFarsi(), DayOfWeekFa = g.Key.DayOfWeek.DayOfWeeKToPersian(), ActiveEmployees = g.Select(x => new RollCallViewModel() { EmployeeId = x.EmployeeId, EmployeeFullName = names.FirstOrDefault(y => y.EmployeeId == x.EmployeeId)?.EmployeeFullName ?? "", WorkshopId = x.WorkshopId, Id = x.id, StartDate = x.StartDate, EndDate = x.EndDate }) }) .ToList(); return result; } } #endregion public long Flag(long employeeId, long workshopId) { var checkDate = DateTime.Now.AddDays(-5); var query = _context.RollCalls .Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId && x.CreationDate >= checkDate); if (query.Any()) { var checkLastOne = query.OrderByDescending(x => x.StartDate).FirstOrDefault(); if (checkLastOne != null && checkLastOne.EndDate == null) return checkLastOne.id; return 0; } else { return 0; } } public string CheckRepeat(long employeeId, long workshopId) { var validSpan = new TimeSpan(0, 0, 2, 0); var checkDate = DateTime.Now.AddDays(-3); var query = _context.RollCalls .Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId && x.CreationDate >= checkDate); if (query.Any()) { var checkTime = DateTime.Now; var checkLastOne = query.OrderByDescending(x => x.StartDate).FirstOrDefault(); if (checkLastOne != null && checkLastOne.EndDate == null && checkLastOne.RollCallModifyType == RollCallModifyType.None) { var checkSpan = (DateTime.Now - checkLastOne.StartDate); if (checkSpan < validSpan) { return "IncomRegistred-InvalidOut"; } } if (checkLastOne != null && checkLastOne.EndDate != null && checkLastOne.RollCallModifyType == RollCallModifyType.None) { var checkSpan = (DateTime.Now - checkLastOne.EndDate); if (checkSpan < validSpan) { return "outRegistred-InvalidIncom"; } } return "Valid"; } else { return "Valid"; } //test } public RollCallViewModel GetDetails(long rollCallId) { var entity = _context.RollCalls.FirstOrDefault(x => x.id == rollCallId); if (entity == null) return null; var name = _context.RollCallEmployees .FirstOrDefault(x => x.WorkshopId == entity.WorkshopId && x.EmployeeId == entity.EmployeeId) .EmployeeFullName; return new RollCallViewModel { WorkshopId = entity.WorkshopId, EmployeeId = entity.EmployeeId, StartDate = entity.StartDate.Value, EndDate = entity.EndDate.Value, EmployeeFullName = name, Id = entity.id, DateGr = entity.ShiftDate.Date }; } /// /// برای پرینت سوابق حضور و غیاب /// public EmployeeRollCallsByMonthViewModel GetEmployeeRollCallsHistoryForPrint(long employeeId, long workshopId, DateTime firstRollCallToPrintStartDate, DateTime lastRollCallToPrintStartDate) { var selectedMonthPersian = (new PersianCalendar()).GetMonth(firstRollCallToPrintStartDate).ToFarsiMonthByIntNumber(); var selectedYearPersian = (new PersianCalendar()).GetYear(firstRollCallToPrintStartDate); var rollCallsList = _context.RollCalls.Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId && x.ShiftDate.Date >= firstRollCallToPrintStartDate.Date && x.ShiftDate.Date <= lastRollCallToPrintStartDate.Date).ToList(); var employeeName = _context.Employees.FirstOrDefault(x => x.id == employeeId).FullName; var personnelCode = _context.PersonnelCodeSet.FirstOrDefault(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId); var statuses = _context.RollCallEmployeesStatus.Include(x => x.RollCallEmployee) .Where(x => x.RollCallEmployee.EmployeeId == employeeId && x.RollCallEmployee.WorkshopId == workshopId) .ToList(); var leavesList = _context.LeaveList.Where(x => x.EmployeeId == employeeId && x.WorkshopId == workshopId && x.EndLeave.Date >= firstRollCallToPrintStartDate.Date && x.StartLeave.Date <= lastRollCallToPrintStartDate.AddDays(2)).ToList(); //if (rollCallsList.Count < 1) // return new(); int dateRange = (int)(lastRollCallToPrintStartDate - firstRollCallToPrintStartDate).TotalDays + 1; var completeDaysList = Enumerable.Range(0, dateRange) .Select(offset => firstRollCallToPrintStartDate.AddDays(offset).Date); var result = completeDaysList.Where(x => !rollCallsList.Any(y => y.ShiftDate.Date == x.Date && y.EndDate == null) && statuses.Any(y => x >= y.StartDate.Date && x <= y.EndDate.Date)) .Select(x => { var leave = leavesList.FirstOrDefault(y => y.EndLeave >= x.Date && y.StartLeave <= x.Date); return new RollCallViewModel() { DateGr = x.Date, DateFa = x.Date.ToFarsi(), RollCallTimesList = rollCallsList.Where(y => x.Date == y.StartDate!.Value.Date).Select(y => new RollCallTimeViewModel() { StartDate = y.StartDate.Value.ToString("HH:mm"), EndDate = y.EndDate!.Value.ToString("HH:mm"), StartDateGr = y.StartDate.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)), Reason = leave?.LeaveType ?? "", HasLeave = (leave?.PaidLeaveType == "روزانه" || leave?.LeaveType == "استعلاجی") ? true : false }; }); result = result.Select(x => new RollCallViewModel() { EmployeeFullName = employeeName, EmployeeId = employeeId, PersonnelCode = 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, HasLeave = x.HasLeave, TotalWorkingHoursSpan = x.TotalWorkingHoursSpan, TotalWorkingHours = $"{(int)x.TotalWorkingHoursSpan.TotalHours}:{x.TotalWorkingHoursSpan.Minutes.ToString("00")}" }).ToList(); var totalWorkingHours = new TimeSpan(result.Sum(x => x.TotalWorkingHoursSpan.Ticks)); return new EmployeeRollCallsByMonthViewModel() { PersianMonthName = selectedMonthPersian, PersianYear = selectedYearPersian.ToString(), DateGr = firstRollCallToPrintStartDate.AddMonthsFa(0, out _).ToGeorgianDateTime(), RollCalls = result.OrderByDescending(x => x.DateGr), TotalWorkingHours = totalWorkingHours.ToFarsiHoursAndMinutes(), TotalWorkingHoursTimeSpan = $"{(int)totalWorkingHours.TotalHours}:{totalWorkingHours.Minutes:00}" }; } public List GetWorkshopEmployeeRollCallsWithUndefinedForDate(long workshopId, long employeeId, DateTime date) { var names = _context.RollCallEmployees.Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId) .Select(x => new { x.EmployeeId, x.EmployeeFullName }).ToList(); var rollcalls = _context.RollCalls.IgnoreQueryFilters() .Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && x.ShiftDate.Date == date.Date) .Select(x => new RollCallViewModel() { Id = x.id, StartDate = x.StartDate.Value, EndDate = x.EndDate.Value, DateFa = x.ShiftDate.Date.ToFarsi(), DateGr = x.ShiftDate.Date, StartDateFa = x.StartDate.Value.ToFarsi(), EndDateFa = x.EndDate != null ? x.EndDate.Value.ToFarsi() : null }).ToList(); return rollcalls.Select(x => new RollCallViewModel() { Id = x.Id, StartDate = x.StartDate, EndDate = x.EndDate, StartTimeString = x.StartDate!.Value.ToString("HH:mm"), EndTimeString = x.EndDate?.ToString("HH:mm"), DateFa = x.DateFa, DateGr = x.DateGr, StartDateFa = x.StartDateFa, EndDateFa = x.EndDateFa, EmployeeId = employeeId, EmployeeFullName = names.FirstOrDefault(e => e.EmployeeId == employeeId)!.EmployeeFullName, DayOfWeekFa = x.StartDate.Value.DayOfWeek.DayOfWeeKToPersian(), StartDayOfWeekFa = x.StartDate.Value.DayOfWeek.DayOfWeeKToPersian(), EndDayOfWeekFa = x.EndDate?.DayOfWeek.DayOfWeeKToPersian(), TotalWorkingHoursSpan = x.EndDate == null ? new() : x.EndDate.Value - x.StartDate.Value }).ToList(); } public void RemoveEmployeeRollCallsWithUndefinedInDate(long workshopId, long employeeId, DateTime date) { var rollCalls = _context.RollCalls.IgnoreQueryFilters().Where(x => x.WorkshopId == workshopId && x.EmployeeId == employeeId && x.ShiftDate == date.Date); if (!rollCalls.Any()) return; _context.RollCalls.RemoveRange(rollCalls); } public List GetEmployeeRollCallsForCustomizeCheckoutTemp( IEnumerable customizeCheckoutIds, long workshopId) { //if (workshopId == 170) // return GetEmployeeRollCallsInDatesForKababMahdi(employeeIds, workshopId, start, end); var checkoutJoinEmployee = _context.CustomizeCheckoutTemps.Where(x => customizeCheckoutIds.Contains(x.id)) .Join(_context.Employees, (customizeCheckout) => customizeCheckout.EmployeeId, (employee) => employee.id, ((customizeCheckout, employee) => new { customizeCheckout, employee })).Select(x => new { CustomizeCheckoutId = x.customizeCheckout.id, CheckoutStart = x.customizeCheckout.ContractStart, CheckoutEnd = x.customizeCheckout.ContractEnd, EmployeeId = x.employee.id, }).ToList(); List result = new(); PersianCalendar pc = new(); foreach (var join in checkoutJoinEmployee) { var start = join.CheckoutStart; var end = join.CheckoutEnd; var employeeId = join.EmployeeId; var rollCalls = _context.RollCalls.Where(x => employeeId == x.EmployeeId && workshopId == x.WorkshopId && x.StartDate != null && x.EndDate != null && x.ShiftDate.Date >= start && x.ShiftDate.Date <= end).ToList(); var leaves = _context.LeaveList.Where(x => x.WorkshopId == workshopId && employeeId == x.EmployeeId && x.EndLeave.Date >= start.Date && x.StartLeave.Date <= end.Date && x.PaidLeaveType == "روزانه").ToList(); var employeeSettingsList = _context.CustomizeWorkshopEmployeeSettings.AsSplitQuery().Where(x => x.WorkshopId == workshopId) .ToList(); var endOfMonth = start.AddMonthsFa(1, out _).ToGeorgianDateTime().Date.AddTicks(-1); int dateRange = (int)(endOfMonth.Date - start).TotalDays + 1; var completeDaysList = Enumerable.Range(0, dateRange).Select(offset => start.AddDays(offset).Date).ToList(); var holidays = _holidayItemApplication.Search(new HolidayItemSearchModel() { HolidayYear = start.ToFarsiYear() }); var settings = employeeSettingsList.FirstOrDefault(x => x.EmployeeId == employeeId); var birthDay = _context.Employees.Where(x => x.id == employeeId).Select(x => x.DateOfBirth).First(); var worksInFriday = settings.FridayWork != FridayWork.Default; var worksInHolidays = settings.HolidayWork != HolidayWork.Default; var absentRecords = completeDaysList.Where(x => !rollCalls.Any(y => y.EmployeeId == employeeId && x.Date.Date == y.ShiftDate.Date)) .Select(x => { var leave = leaves.FirstOrDefault(y => y.EmployeeId == employeeId && y.EndLeave.Date >= x.Date && y.StartLeave.Date <= x.Date); var isHoliday = holidays.Any(y => y.HolidaydateGr == x.Date); var isFriday = x.Date.DayOfWeek == DayOfWeek.Friday; var isNormalWorkingDay = isHoliday == false && isFriday == false; var isInContractRange = x <= end && x >= start; return new CheckoutDailyRollCallViewModel() { StartDate1 = null, EndDate1 = null, DateTimeGr = x.Date.Date, DayOfWeek = x.Date.DayOfWeek.ToString(), RollCallDateFa = x.Date.ToFarsi(), LeaveType = leave != null ? leave.LeaveType : "", IsAbsent = leave == null && isInContractRange && (isNormalWorkingDay || (worksInFriday && x.Date.DayOfWeek == DayOfWeek.Friday) || (worksInHolidays && isHoliday)), }; }); var presentDays = rollCalls.Where(x => x.EmployeeId == employeeId).GroupBy(x => x.ShiftDate.Date) .Select(x => { var orderedRollcalls = x.OrderBy(y => y.ShiftDate).ToList(); var firstRollCall = orderedRollcalls.FirstOrDefault(); var secondRollCall = orderedRollcalls.Skip(1).FirstOrDefault(); //این برای این هست که ببینه اگر که این شخص گردشی بوده و دوبار وارد در دو شیفت مختلف وارد شده شیفت دوم تاثیر نخواهد گذاشت //منطق کباب مهدی!!!! #region SecondTimeDiff var hasSecondTimeDiff = false; if (settings.WorkshopShiftStatus == WorkshopShiftStatus.Rotating) { if (firstRollCall != null && secondRollCall != null) { var firstShift = FindRotatingShift(firstRollCall.StartDate.Value, firstRollCall.EndDate.Value, settings.CustomizeRotatingShifts); var secondShift = FindRotatingShift(secondRollCall.StartDate.Value, secondRollCall.EndDate.Value, settings.CustomizeRotatingShifts); if (firstShift.start == secondShift.start && firstShift.end == secondShift.end) { hasSecondTimeDiff = true; } } } else { hasSecondTimeDiff = true; } #endregion return new CheckoutDailyRollCallViewModel() { StartDate1 = firstRollCall?.StartDate?.ToString("HH:mm"), EndDate1 = firstRollCall?.EndDate?.ToString("HH:mm"), StartDate2 = secondRollCall?.StartDate?.ToString("HH:mm") ?? "", EndDate2 = secondRollCall?.EndDate?.ToString("HH:mm") ?? "", TotalhourseSpan = new TimeSpan(x.Where(y => y.EndDate != null) .Sum(y => (y.EndDate - y.StartDate)!.Value.Ticks)), DayOfWeek = x.Key.DayOfWeek.DayOfWeeKToPersian(), RollCallDateFa = x.Key.Date.ToFarsi(), DateTimeGr = x.Key.Date, IsSliced = x.Count() > 2, IsAbsent = false, EnterDifferencesMinutes1 = firstRollCall != null && firstRollCall.LateEntryDuration > TimeSpan.Zero ? CalculateEntryMinuteDifference(firstRollCall.EarlyEntryDuration, firstRollCall.LateEntryDuration) : "", ExitDifferencesMinutes1 = firstRollCall != null && firstRollCall.EarlyExitDuration > TimeSpan.Zero ? CalculateExitMinuteDifference(firstRollCall.EarlyExitDuration, firstRollCall.LateExitDuration) : "", EnterDifferencesMinutes2 = secondRollCall != null && secondRollCall.LateEntryDuration > TimeSpan.Zero && hasSecondTimeDiff ? CalculateEntryMinuteDifference(secondRollCall.EarlyEntryDuration, secondRollCall.LateEntryDuration) : "", ExitDifferencesMinutes2 = secondRollCall != null && secondRollCall.EarlyExitDuration > TimeSpan.Zero && hasSecondTimeDiff ? CalculateExitMinuteDifference(secondRollCall.EarlyExitDuration, secondRollCall.LateExitDuration) : "" }; }); presentDays = presentDays.Select(x => new CheckoutDailyRollCallViewModel { StartDate1 = x.StartDate1, EndDate1 = x.EndDate1, EndDate2 = x.EndDate2, StartDate2 = x.StartDate2, TotalWorkingHours = $"{(int)(x.TotalhourseSpan.TotalHours)}:{x.TotalhourseSpan.Minutes.ToString("00")}", DayOfWeek = x.DayOfWeek, RollCallDateFa = x.RollCallDateFa, DateTimeGr = x.DateTimeGr, IsSliced = x.IsSliced, IsAbsent = false, EnterDifferencesMinutes1 = x.EnterDifferencesMinutes1, EnterDifferencesMinutes2 = x.EnterDifferencesMinutes2, ExitDifferencesMinutes1 = x.ExitDifferencesMinutes1, ExitDifferencesMinutes2 = x.ExitDifferencesMinutes2 }); List checkoutDailyRollCalls = presentDays.Concat(absentRecords).OrderBy(x => x.DateTimeGr).ToList(); checkoutDailyRollCalls.ForEach(x => { x.IsFriday = x.DateTimeGr.DayOfWeek == DayOfWeek.Friday; x.IsHoliday = holidays.Any(y => y.HolidaydateGr == x.DateTimeGr); x.IsBirthDay = workshopId == 170 && pc.GetMonth(x.DateTimeGr) == pc.GetMonth(birthDay) && pc.GetDayOfMonth(x.DateTimeGr) == pc.GetDayOfMonth(birthDay); }); var resultItem = new PersonnelCheckoutDailyRollCallViewModel() { EmployeeId = employeeId, WorkshopId = workshopId, DailyRollCalls = checkoutDailyRollCalls }; result.Add(resultItem); } return result; } public async Task> GetRollCallsInShiftDate(DateTime rollCallShiftDate, long employeeId, long workshopId) { return await _context.RollCalls.Where(x => x.ShiftDate.Date == rollCallShiftDate.Date && x.WorkshopId == workshopId && x.EmployeeId == employeeId) .ToListAsync(); } //حضور غیاب گروهی از پرسنل برای پرینت گروهی فیش حقوقی غیر رسمی نهایی public List GetEmployeeRollCallsForMonthForKababMahdi( IEnumerable employeeIds, long workshopId, DateTime start, DateTime end) { var rollCalls = _context.RollCalls.Where(x => employeeIds.Contains(x.EmployeeId) && workshopId == x.WorkshopId && x.StartDate != null && x.EndDate != null && x.RollCallModifyType != RollCallModifyType.Undefined && x.ShiftDate.Date >= start && x.ShiftDate.Date <= end).ToList(); var leaves = _context.LeaveList.Where(x => x.WorkshopId == workshopId && employeeIds.Contains(x.EmployeeId) && x.EndLeave.Date >= start.Date && x.StartLeave.Date <= end.Date).ToList(); var employees = _context.Employees.Where(x => employeeIds.Contains(x.id)) .Select(x => new { Id = x.id, x.DateOfBirth }).ToList(); var year = Convert.ToInt32(start.ToFarsi().Substring(0, 4)); var month = Convert.ToInt32(start.ToFarsi().Substring(5, 2)); var firstDayOfCurrentMonth = new DateTime(year, month, 1, new PersianCalendar()); if (month == 12) { year += 1; month = 1; } else month += 1; var nextMonthDate = new DateTime(year, month, 1, new PersianCalendar()); var lastDayOfCurrentMonth = nextMonthDate.AddDays(-1); int dateRange = (int)(lastDayOfCurrentMonth - firstDayOfCurrentMonth).TotalDays + 1; var employeeSettingsList = _context.CustomizeWorkshopEmployeeSettings.AsSplitQuery().Where(x => x.WorkshopId == workshopId).ToList(); var holidays = _holidayItemApplication.Search(new HolidayItemSearchModel() { HolidayYear = start.ToFarsiYear() }); //all the dates from start to end, to be compared with present days to get absent dates var completeDaysList = Enumerable.Range(0, dateRange).Select(offset => start.AddDays(offset).Date).ToList(); PersianCalendar pc = new(); List result = new(); foreach (var employeeId in employeeIds) { var settings = employeeSettingsList.FirstOrDefault(x => x.EmployeeId == employeeId); var worksInFriday = settings.FridayWork != FridayWork.Default; var worksInHolidays = settings.HolidayWork != HolidayWork.Default; var birthDay = employees.First(x => x.Id == employeeId).DateOfBirth; var absentRecords = completeDaysList.Where(x => !rollCalls.Any(y => y.EmployeeId == employeeId && x.Date.Date == y.ShiftDate.Date)) .Select(x => { var leave = leaves.FirstOrDefault(y => y.EmployeeId == employeeId && y.EndLeave.Date >= x.Date && y.StartLeave.Date <= x.Date); var isHoliday = holidays.Any(y => y.HolidaydateGr == x.Date); var isFriday = x.Date.DayOfWeek == DayOfWeek.Friday; var isBirthday = pc.GetMonth(x) == pc.GetMonth(birthDay) && pc.GetDayOfMonth(x) == pc.GetDayOfMonth(birthDay); var isNormalWorkingDay = isHoliday == false && isFriday == false; return new CheckoutDailyRollCallViewModel() { StartDate1 = null, EndDate1 = null, DateTimeGr = x.Date, DayOfWeek = x.Date.DayOfWeek.ToString(), RollCallDateFa = x.Date.ToFarsi(), LeaveType = leave != null ? leave.LeaveType : "", IsAbsent = leave == null && (isNormalWorkingDay || (worksInFriday && x.Date.DayOfWeek == DayOfWeek.Friday) || (worksInHolidays && isHoliday)), IsBirthDay = isBirthday }; }); var presentDays = rollCalls.Where(x => x.EmployeeId == employeeId).GroupBy(x => x.ShiftDate.Date) .Select(x => { var orderedRollcalls = x.OrderBy(y => y.StartDate!.Value).ToList(); var firstRollCall = orderedRollcalls.FirstOrDefault(); var secondRollCall = orderedRollcalls.Skip(1).FirstOrDefault(); //این برای این هست که ببینه اگر که این شخص گردشی بوده و دوبار وارد در دو شیفت مختلف وارد شده شیفت دوم تاثیر نخواهد گذاشت //منطق کباب مهدی!!!! #region SecondTimeDiff var hasSecondTimeDiff = false; if (settings.WorkshopShiftStatus == WorkshopShiftStatus.Rotating) { if (firstRollCall != null && secondRollCall != null) { var firstShift = FindRotatingShift(firstRollCall.StartDate.Value, firstRollCall.EndDate.Value, settings.CustomizeRotatingShifts); var secondShift = FindRotatingShift(secondRollCall.StartDate.Value, secondRollCall.EndDate.Value, settings.CustomizeRotatingShifts); if (firstShift.start == secondShift.start && firstShift.end == secondShift.end) { hasSecondTimeDiff = true; } } } else { hasSecondTimeDiff = true; } #endregion return new CheckoutDailyRollCallViewModel() { StartDate1 = orderedRollcalls.FirstOrDefault()?.StartDate?.ToString("HH:mm"), EndDate1 = orderedRollcalls.FirstOrDefault()?.EndDate?.ToString("HH:mm"), StartDate2 = orderedRollcalls.Skip(1).FirstOrDefault()?.StartDate?.ToString("HH:mm") ?? "", EndDate2 = orderedRollcalls.Skip(1).FirstOrDefault()?.EndDate?.ToString("HH:mm") ?? "", TotalhourseSpan = new TimeSpan(x.Where(y => y.EndDate != null) .Sum(y => (y.EndDate - y.StartDate)!.Value.Ticks)), DayOfWeek = x.Key.DayOfWeek.DayOfWeeKToPersian(), RollCallDateFa = x.Key.Date.ToFarsi(), DateTimeGr = x.Key.Date, IsSliced = x.Count() > 2, IsAbsent = false, IsBirthDay = pc.GetMonth(x.Key) == pc.GetMonth(birthDay) && pc.GetDayOfMonth(x.Key) == pc.GetDayOfMonth(birthDay), EnterDifferencesMinutes1 = firstRollCall != null && firstRollCall.LateEntryDuration > TimeSpan.Zero && workshopId == 170 ? CalculateEntryMinuteDifference(firstRollCall.EarlyEntryDuration, firstRollCall.LateEntryDuration) : "", ExitDifferencesMinutes1 = "", EnterDifferencesMinutes2 = secondRollCall != null && secondRollCall.LateEntryDuration > TimeSpan.Zero && workshopId == 170 && hasSecondTimeDiff ? CalculateEntryMinuteDifference(secondRollCall.EarlyEntryDuration, secondRollCall.LateEntryDuration) : "", ExitDifferencesMinutes2 = "" }; }); presentDays = presentDays.Select(x => new CheckoutDailyRollCallViewModel { StartDate1 = x.StartDate1, EndDate1 = x.EndDate1, EndDate2 = x.EndDate2, StartDate2 = x.StartDate2, TotalWorkingHours = $"{(int)(x.TotalhourseSpan.TotalHours)}:{x.TotalhourseSpan.Minutes.ToString("00")}", DayOfWeek = x.DayOfWeek, RollCallDateFa = x.RollCallDateFa, DateTimeGr = x.DateTimeGr, IsSliced = x.IsSliced, IsAbsent = false, IsBirthDay = x.IsBirthDay, EnterDifferencesMinutes1 = x.EnterDifferencesMinutes1, ExitDifferencesMinutes1 = x.ExitDifferencesMinutes1, EnterDifferencesMinutes2 = x.EnterDifferencesMinutes2, ExitDifferencesMinutes2 = x.ExitDifferencesMinutes2, }); List checkoutDailyRollCalls = presentDays.Concat(absentRecords).OrderBy(x => x.DateTimeGr).ToList(); checkoutDailyRollCalls.ForEach(x => { x.IsFriday = x.DateTimeGr.DayOfWeek == DayOfWeek.Friday; x.IsHoliday = holidays.Any(y => y.HolidaydateGr == x.DateTimeGr); }); var resultItem = new PersonnelCheckoutDailyRollCallViewModel() { EmployeeId = employeeId, WorkshopId = workshopId, DailyRollCalls = checkoutDailyRollCalls }; result.Add(resultItem); } return result; } public static (DateTime start, DateTime end) FindRotatingShift(DateTime startRollCall, DateTime endRollCall, ICollection rotatingShifts) { DateTime startDate = startRollCall.Date; DateTime endDate = endRollCall.Date; DateTime startEntryWithDate = startDate.Add(startRollCall.TimeOfDay); DateTime endEntryWithDate = endDate.Add(endRollCall.TimeOfDay); DateTime oneHourBeforeStart = startEntryWithDate.AddHours(-1); DateTime oneHourAfterStart = startEntryWithDate.AddHours(1); DateTime oneHourBeforeEnd = endEntryWithDate.AddHours(-1); DateTime oneHourAfterEnd = endEntryWithDate.AddHours(1); var shiftDateTimes = rotatingShifts.SelectMany(shift => { var shifts = new List<(DateTime Start, DateTime End)>(); for (int i = -1; i <= 1; i++) { var shiftStart = startDate.AddDays(i).Date; shiftStart = shiftStart.Add(shift.StartTime.ToTimeSpan()); var shiftEnd = shift.StartTime < shift.EndTime ? startDate.AddDays(i).Date.Add(shift.EndTime.ToTimeSpan()) : startDate.AddDays(i + 1).Date.Add(shift.EndTime.ToTimeSpan()); shifts.Add((shiftStart, shiftEnd)); } return shifts; }).ToList(); #region مقایسه شروع حضور غیاب با شیفت var startFilteredTimes = shiftDateTimes.Where(shift => (oneHourBeforeStart <= shift.Start && oneHourAfterStart >= shift.Start) || (oneHourBeforeStart <= shift.End && oneHourAfterStart >= shift.End)).ToList(); if (startFilteredTimes.Count == 0) { startFilteredTimes = shiftDateTimes; } else if (startFilteredTimes.Count == 1) { var startChosenShift = startFilteredTimes.First(); if (startChosenShift.End < startChosenShift.Start) startChosenShift.End = startChosenShift.End.AddDays(1); return startChosenShift; } #endregion #region مقایسه پایان حضورغیاب با شیفت var endFilteredTimes = shiftDateTimes.Where(shift => (oneHourBeforeEnd <= shift.Start && oneHourAfterEnd >= shift.Start) || (oneHourBeforeEnd <= shift.End && oneHourAfterEnd >= shift.End)).ToList(); if (endFilteredTimes.Count == 0) { endFilteredTimes = startFilteredTimes; } else if (endFilteredTimes.Count == 1) { var endChosenShift = endFilteredTimes.First(); return endChosenShift; } #endregion #region اشتراک حضور غیاب و شیفت var overlapShifts = endFilteredTimes.Select(shift => new { Shift = shift, Overlap = new TimeSpan(Math.Max(0, Math.Min(shift.End.Ticks, oneHourAfterEnd.Ticks) - Math.Max(shift.Start.Ticks, oneHourBeforeStart.Ticks))) }); var overlapChosenShift = overlapShifts.MaxBy(s => s.Overlap); var end = overlapChosenShift.Shift.End; if (overlapChosenShift.Shift.End < overlapChosenShift.Shift.Start) end = overlapChosenShift.Shift.End.AddDays(1); return (overlapChosenShift.Shift.Start, end); #endregion } public async Task> GetRollCallsUntilNowWithWorkshopIdEmployeeIds(long workshopId, List employeeIds, DateTime fromDate) { return await _context.RollCalls.Where(x => x.ShiftDate >= fromDate && x.WorkshopId == workshopId && employeeIds.Contains(x.EmployeeId) && x.EndDate != null).ToListAsync(); } public async Task> GetCaseHistoryTitles(long workshopId, RollCallCaseHistorySearchModel searchModel) { var yesterday = DateTime.Now.AddDays(-1).Date; var groupByMonth = false; var rollCallStatusQuery = _context.RollCallEmployeesStatus .Where(x => x.RollCallEmployee.WorkshopId == workshopId); if (searchModel.EmployeeId is > 0) { rollCallStatusQuery = rollCallStatusQuery.Where(x => x.RollCallEmployee.EmployeeId == searchModel.EmployeeId); groupByMonth = true; } var startDateNullable = (await rollCallStatusQuery .OrderBy(x => x.StartDate).FirstOrDefaultAsync())?.StartDate; var endDateNullable = (await rollCallStatusQuery .OrderByDescending(x => x.EndDate).FirstOrDefaultAsync())?.EndDate; if (!startDateNullable.HasValue || !endDateNullable.HasValue) { return new PagedResult(); } var startDate = startDateNullable.Value.Date; var endDate = endDateNullable.Value.Date; if (endDate.Date > yesterday) { endDate = yesterday; } var dateRange = (int)(endDate - startDate).TotalDays + 1; var dates = Enumerable.Range(0, dateRange) .Select(offset => startDate.AddDays(offset)).OrderByDescending(x => x).ToList(); if (!string.IsNullOrWhiteSpace(searchModel.OneDayDate)) { var date = searchModel.OneDayDate.ToGeorgianDateTime(); dates = dates .Where(x => x.Date == date.Date).ToList(); } else if (!string.IsNullOrWhiteSpace(searchModel.StartDate) && !string.IsNullOrWhiteSpace(searchModel.EndDate)) { var start = searchModel.StartDate.ToGeorgianDateTime(); var end = searchModel.EndDate.ToGeorgianDateTime(); dates = dates .Where(x => x <= end && x >= start).ToList(); } List rollCallCaseHistoryTitles; if (groupByMonth) { var persianDates = dates .Select(x => new PersianTools.Core.PersianDateTime(x)).ToList(); rollCallCaseHistoryTitles = persianDates.GroupBy(x => (x.Year, x.Month)) .Select(x => new RollCallCaseHistoryTitleDto() { Id = $"{x.Key.Year}_{x.Key.Month}", Title = $"{x.Key.Year} {x.Key.Month.ToFarsiMonthByIntNumber()}" }).ToList(); } else { rollCallCaseHistoryTitles = dates .Select(x => new RollCallCaseHistoryTitleDto() { Id = x.ToFarsi(), Title = $"{x.ToFarsi()} {x.DayOfWeek.DayOfWeeKToPersian()}" }).ToList(); } var res = new PagedResult() { List = rollCallCaseHistoryTitles, TotalCount = rollCallCaseHistoryTitles.Count }; return res; } public async Task> GetCaseHistoryDetails(long workshopId, string titleId, RollCallCaseHistorySearchModel searchModel) { var query = _context.RollCallEmployeesStatus .Include(x => x.RollCallEmployee) .Where(x => x.RollCallEmployee.WorkshopId == workshopId); DateTime startDate; DateTime endDate; //این برای این هست که اگر بر اساس پرسنل جستجو شده بود و در titleId اگر به فرمت YYYY_MM اومد به این معنی هست که باید بر طبق ماه فیلتر بشه نه به صورت روزانه if (searchModel.EmployeeId is > 0) { if (!Regex.IsMatch(titleId, @"^\d{4}_\d{2}$")) throw new BadRequestException("شناسه سر تیتر وارد شده نامعتبر است"); var splitDate = titleId.Split("_"); var year = Convert.ToInt32(splitDate.First()); var month = Convert.ToInt32(splitDate.Last()); var startDateFa = $"{year:D4}/{month:D2}/01"; if (!startDateFa.TryToGeorgianDateTime(out startDate)) { throw new BadRequestException("تاریخ وارد شده نامعتبر است"); } var endDateFa = startDateFa.FindeEndOfMonth(); if (!endDateFa.TryToGeorgianDateTime(out endDate)) { throw new BadRequestException("تاریخ وارد شده نامعتبر است"); } query = query .Where(x => x.RollCallEmployee.EmployeeId == searchModel.EmployeeId); } else { if (!Regex.IsMatch(titleId, @"^\d{4}/\d{2}/\d{2}$")) throw new BadRequestException("شناسه سر تیتر وارد شده نامعتبر است"); startDate = titleId.ToGeorgianDateTime(); endDate = startDate; } query = query.Where(x => x.StartDate <= endDate && x.EndDate >= startDate); var employeeIds = await query.Select(x => x.RollCallEmployee.EmployeeId).ToListAsync(); var rollCalls = await _context.RollCalls .Where(x => x.WorkshopId == workshopId && employeeIds.Contains(x.EmployeeId) && startDate <= x.ShiftDate && endDate >= x.ShiftDate).ToListAsync(); var leaves = await _context.LeaveList.Where(x => x.WorkshopId == workshopId && employeeIds.Contains(x.EmployeeId) && x.StartLeave >= endDate && x.EndLeave <= startDate) .ToListAsync(); var dateRange = (int)(endDate - startDate).TotalDays + 1; var dates = Enumerable.Range(0, dateRange).Select(x => startDate.AddDays(x)).ToList(); var employees = await _context.Employees.Where(x => employeeIds .Contains(x.id)).ToListAsync(); var personnelCodes = await _context.PersonnelCodeSet .Where(x => x.WorkshopId == workshopId && employeeIds.Contains(x.EmployeeId)).ToListAsync(); var res = new List(); foreach (var date in dates) { var rollCallInDate = rollCalls .Where(x => x.ShiftDate.Date == date.Date).ToList(); foreach (var employeeId in employeeIds) { var leave = leaves.FirstOrDefault(y => y.EmployeeId == employeeId); var employeeRollCallsForDate = rollCallInDate .Where(y => y.EmployeeId == employeeId).ToList(); var employee = employees.FirstOrDefault(x => x.id == employeeId); var item = new RollCallCaseHistoryDetail() { EmployeeFullName = employee?.FullName ?? "", EmployeeId = employee?.id ?? 0, Records = employeeRollCallsForDate.OrderBy(r => r.StartDate).Select(y => new RollCallCaseHistoryDetailRecord() { EndTime = y.EndDate!.Value.ToString("HH:mm"), StartTime = y.StartDate!.Value.ToString("HH:mm"), EntryTimeDifference = CalculateEntryTimeDifferencesTimeSpan(y.EarlyEntryDuration, y.LateEntryDuration), ExitTimeDifference = CalculateExitTimeDifferencesTimeSpan(y.EarlyExitDuration, y.LateExitDuration) }).ToList(), Status = employeeRollCallsForDate.Any() ? RollCallRecordStatus.Worked : leave != null ? RollCallRecordStatus.Leaved : RollCallRecordStatus.Absent, TotalWorkingTime = new TimeSpan( employeeRollCallsForDate.Sum(y => (y.EndDate!.Value - y.StartDate!.Value).Ticks)), PersonnelCode = personnelCodes.FirstOrDefault(y => y.EmployeeId == employeeId)?.PersonnelCode .ToString(), }; res.Add(item); } } return res; } }