705 lines
30 KiB
C#
705 lines
30 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using _0_Framework.Application;
|
||
using _0_Framework.Domain.CustomizeCheckoutShared.Base;
|
||
using _0_Framework.Domain.CustomizeCheckoutShared.Enums;
|
||
using _0_Framework.Domain.CustomizeCheckoutShared.ValueObjects;
|
||
using Company.Domain.CustomizeWorkshopEmployeeSettingsAgg;
|
||
using Company.Domain.CustomizeWorkshopEmployeeSettingsAgg.Entities;
|
||
using Company.Domain.CustomizeWorkshopSettingsAgg;
|
||
using System.Collections.Generic;
|
||
using System;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using _0_Framework.Application;
|
||
using OfficeOpenXml;
|
||
using OfficeOpenXml.Drawing.Chart;
|
||
using System.Collections;
|
||
|
||
namespace Company.Domain.RollCallAgg.DomainService;
|
||
|
||
public interface IRollCallDomainService
|
||
{
|
||
(WorkshopShiftStatus shiftType, IrregularShift irregularShift, ICollection<CustomizeSifts> regularShifts,
|
||
ICollection<CustomizeRotatingShift> rotatingShifts, TimeSpan BreakTime) GetEmployeeShiftDetails(long employeeId,
|
||
long workshopId);
|
||
|
||
TimeOnly GetEmployeeOffSetForRegularSettings(long employeeId, long workshopId);
|
||
DateTime GetEmployeeShiftDateByRollCallStartDate(long workshopId, long employeeId, DateTime rollCallStartDate, DateTime rollCallEndDate);
|
||
|
||
void CalculateTimeDifferences(RollCall rollCall);
|
||
|
||
(DateTime start, DateTime end) FindRotatingShift(DateTime startRollCall, DateTime endRollCall, ICollection<CustomizeRotatingShift> rotatingShifts);
|
||
(DateTime start, DateTime end) FindRotatingShift(List<(DateTime StartDate, DateTime EndDate)> rollcalls, ICollection<CustomizeRotatingShift> rotatingShifts);
|
||
|
||
BreakTime GetBreakTime(long employeeId, long workshopId);
|
||
|
||
}
|
||
|
||
public class RollCallDomainService : IRollCallDomainService
|
||
{
|
||
private readonly IRollCallRepository _rollCallRepository;
|
||
|
||
private readonly ICustomizeWorkshopEmployeeSettingsRepository _customizeWorkshopEmployeeSettingsRepository;
|
||
private readonly ICustomizeWorkshopSettingsRepository _customizeWorkshopSettingsRepository;
|
||
|
||
public RollCallDomainService(IRollCallRepository rollCallRepository,
|
||
ICustomizeWorkshopEmployeeSettingsRepository customizeWorkshopEmployeeSettingsRepository,
|
||
ICustomizeWorkshopSettingsRepository customizeWorkshopSettingsRepository)
|
||
{
|
||
_rollCallRepository = rollCallRepository;
|
||
_customizeWorkshopEmployeeSettingsRepository = customizeWorkshopEmployeeSettingsRepository;
|
||
_customizeWorkshopSettingsRepository = customizeWorkshopSettingsRepository;
|
||
}
|
||
|
||
public (WorkshopShiftStatus shiftType, IrregularShift irregularShift, ICollection<CustomizeSifts> regularShifts,
|
||
ICollection<CustomizeRotatingShift> rotatingShifts, TimeSpan BreakTime) GetEmployeeShiftDetails(long employeeId,
|
||
long workshopId)
|
||
{
|
||
var employeeSettings =
|
||
_customizeWorkshopEmployeeSettingsRepository.GetByEmployeeIdAndWorkshopIdIncludeGroupSettings(workshopId,
|
||
employeeId);
|
||
|
||
var offset = TimeOnly.MinValue;
|
||
WorkshopShiftStatus shiftType;
|
||
|
||
if (employeeSettings == null)
|
||
{
|
||
|
||
var workshopSettings = _customizeWorkshopSettingsRepository.GetBy(workshopId);
|
||
|
||
|
||
shiftType = workshopSettings.WorkshopShiftStatus;
|
||
|
||
return (shiftType, null,
|
||
workshopSettings.CustomizeWorkshopSettingsShifts.Select(x => (CustomizeSifts)x).ToList(),
|
||
null, TimeSpan.Zero);
|
||
|
||
}
|
||
else
|
||
{
|
||
|
||
shiftType = employeeSettings.WorkshopShiftStatus;
|
||
|
||
var breakTimeSpan = employeeSettings.BreakTime.BreakTimeType == BreakTimeType.WithTime
|
||
? employeeSettings.BreakTime.BreakTimeValue.ToTimeSpan()
|
||
: TimeSpan.Zero;
|
||
|
||
return (shiftType, employeeSettings.IrregularShift,
|
||
employeeSettings.CustomizeWorkshopEmployeeSettingsShifts.Select(x => (CustomizeSifts)x).ToList(),
|
||
employeeSettings.CustomizeRotatingShifts, breakTimeSpan);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
public TimeOnly GetEmployeeOffSetForRegularSettings(long employeeId, long workshopId)
|
||
{
|
||
var workshopSettings = _customizeWorkshopSettingsRepository.GetBy(workshopId);
|
||
var employeeSettings =
|
||
_customizeWorkshopEmployeeSettingsRepository.GetByEmployeeIdAndWorkshopIdIncludeGroupSettings(workshopId,
|
||
employeeId);
|
||
|
||
if (workshopSettings == null)
|
||
return TimeOnly.MinValue;
|
||
|
||
if (employeeSettings == null && workshopSettings.WorkshopShiftStatus == WorkshopShiftStatus.Regular)
|
||
return Tools.CalculateOffset(workshopSettings.CustomizeWorkshopSettingsShifts
|
||
.Select(x => (CustomizeSifts)x).ToList());
|
||
|
||
|
||
|
||
if (workshopSettings.WorkshopShiftStatus == WorkshopShiftStatus.Regular &&
|
||
employeeSettings.WorkshopShiftStatus == WorkshopShiftStatus.Regular)
|
||
{
|
||
// تعریف بازههای زمانی
|
||
var workshopStartTime =
|
||
workshopSettings.CustomizeWorkshopSettingsShifts.MinBy(x => x.Placement).StartTime; // شروع کارگاه
|
||
var workshopEndTime =
|
||
workshopSettings.CustomizeWorkshopSettingsShifts.MaxBy(x => x.Placement).EndTime; // پایان کارگاه
|
||
|
||
var employeeStartTime = employeeSettings.CustomizeWorkshopEmployeeSettingsShifts.MinBy(x => x.Placement)
|
||
.StartTime; // شروع بازه پرسنل
|
||
var employeeEndTime = employeeSettings.CustomizeWorkshopEmployeeSettingsShifts.MaxBy(x => x.Placement)
|
||
.EndTime; // پایان پرسنل
|
||
|
||
// تبدیل زمانها به TimeSpan برای مقایسه
|
||
var workshopStartTimeSpan = workshopStartTime.ToTimeSpan();
|
||
var workshopEndTimeSpan = workshopEndTime.ToTimeSpan();
|
||
var employeeStartTimeSpan = employeeStartTime.ToTimeSpan();
|
||
var employeeEndTimeSpan = employeeEndTime.ToTimeSpan();
|
||
|
||
// مدیریت زمانهای بعد از نیمه شب
|
||
if (workshopEndTimeSpan < workshopStartTimeSpan)
|
||
workshopEndTimeSpan = workshopEndTimeSpan.Add(TimeSpan.FromDays(1)); // افزودن یک روز به پایان بازه اول
|
||
|
||
if (employeeEndTimeSpan < employeeStartTimeSpan)
|
||
employeeEndTimeSpan = employeeEndTimeSpan.Add(TimeSpan.FromDays(1)); // افزودن یک روز به پایان بازه دوم
|
||
|
||
|
||
// محاسبه بزرگترین زمان شروع و کوچکترین زمان پایان
|
||
var overlapStart = workshopStartTimeSpan > employeeStartTimeSpan
|
||
? workshopStartTimeSpan
|
||
: employeeStartTimeSpan;
|
||
var overlapEnd = workshopEndTimeSpan < employeeEndTimeSpan ? workshopEndTimeSpan : employeeEndTimeSpan;
|
||
|
||
if (overlapStart >= overlapEnd) // اگر بازه همپوشانی وجود ندارد
|
||
return Tools.CalculateOffset(employeeSettings.CustomizeWorkshopEmployeeSettingsShifts
|
||
.Select(x => (CustomizeSifts)x).ToList());
|
||
|
||
|
||
|
||
var overlapDuration = (overlapEnd - overlapStart).TotalMinutes; // مدت زمان همپوشانی بر حسب دقیقه
|
||
var duration2 = (employeeEndTime - employeeStartTime).TotalMinutes; // مدت زمان بازه دوم
|
||
|
||
var overlapPercentage = (overlapDuration / duration2) * 100; // درصد همپوشانی
|
||
|
||
if (overlapPercentage < 40)
|
||
{
|
||
return Tools.CalculateOffset(employeeSettings.CustomizeWorkshopEmployeeSettingsShifts
|
||
.Select(x => (CustomizeSifts)x).ToList());
|
||
}
|
||
|
||
return Tools.CalculateOffset(workshopSettings.CustomizeWorkshopSettingsShifts
|
||
.Select(x => (CustomizeSifts)x).ToList());
|
||
}
|
||
|
||
else if (employeeSettings.WorkshopShiftStatus == WorkshopShiftStatus.Regular)
|
||
{
|
||
return Tools.CalculateOffset(employeeSettings.CustomizeWorkshopEmployeeSettingsShifts
|
||
.Select(x => (CustomizeSifts)x).ToList());
|
||
}
|
||
|
||
else
|
||
{
|
||
return TimeOnly.MinValue;
|
||
}
|
||
}
|
||
|
||
public DateTime GetEmployeeShiftDateByRollCallStartDate(long workshopId, long employeeId,
|
||
DateTime rollCallStartDate, DateTime rollCallEndDate)
|
||
{
|
||
var shiftDetails = GetEmployeeShiftDetails(employeeId, workshopId);
|
||
|
||
var offset = GetEmployeeOffSetForRegularSettings(employeeId, workshopId);
|
||
|
||
return shiftDetails.shiftType switch
|
||
{
|
||
WorkshopShiftStatus.Regular => CalculateRegularShiftDate(rollCallStartDate, offset),
|
||
WorkshopShiftStatus.Rotating => FindRotatingShift(rollCallStartDate, rollCallEndDate, shiftDetails.rotatingShifts).start.Date,
|
||
WorkshopShiftStatus.Irregular => rollCallStartDate.Date,
|
||
_ => throw new ArgumentOutOfRangeException()
|
||
};
|
||
}
|
||
|
||
|
||
public void CalculateTimeDifferences(RollCall rollCall)
|
||
{
|
||
var employeeId = rollCall.EmployeeId;
|
||
var workshopId = rollCall.WorkshopId;
|
||
var shiftDate = rollCall.ShiftDate;
|
||
var starDateTime = rollCall.StartDate.Value;
|
||
var endDateTime = rollCall.EndDate.Value;
|
||
|
||
//Todo: Get RollCalls in this shift date
|
||
//گرفتن حضور غیاب ها در این شیفت دیت
|
||
//در ادامه باید بگردم رول کال اول رو پر کنم رول کال های غیره باید بررسی بشه ببینیم که این رول کال ها کدومش مناسب تره
|
||
|
||
// و بررسی کنیم چند تا رول کال باید
|
||
|
||
|
||
|
||
|
||
var shiftDetails = GetEmployeeShiftDetails(employeeId, workshopId);
|
||
|
||
|
||
List<RollCall> rollCalls = GetRollCallsInShiftDate(rollCall.ShiftDate, employeeId, workshopId).GetAwaiter().GetResult();
|
||
|
||
var deletedRollCall = rollCalls.FirstOrDefault(x => x.id == rollCall.id);
|
||
|
||
rollCalls.Remove(deletedRollCall);
|
||
rollCalls.Add(rollCall);
|
||
|
||
rollCall.ClearTimeDiff();
|
||
switch (shiftDetails.shiftType)
|
||
{
|
||
case WorkshopShiftStatus.Regular:
|
||
var employeeShifts = shiftDetails.regularShifts.Select(x =>
|
||
{
|
||
var start = new DateTime(DateOnly.FromDateTime(shiftDate), x.StartTime);
|
||
var end = new DateTime(DateOnly.FromDateTime(shiftDate), x.EndTime);
|
||
if (x.EndTime < x.StartTime)
|
||
end = end.AddDays(1);
|
||
return new { start, end };
|
||
}).ToList();
|
||
|
||
foreach (var employeeShift in employeeShifts)
|
||
{
|
||
|
||
TimeSpan lateEntryDuration = new TimeSpan();
|
||
TimeSpan earlyEntryDuration = new TimeSpan();
|
||
TimeSpan lateExitDuration = new TimeSpan();
|
||
TimeSpan earlyExitDuration = new TimeSpan();
|
||
|
||
var shiftStart = employeeShift.start;
|
||
var shiftEnd = employeeShift.end;
|
||
|
||
var rollCallsInShift = rollCalls.Where(a =>
|
||
(a.StartDate <= shiftStart && a.EndDate >= shiftStart) ||
|
||
(a.StartDate <= shiftEnd && a.EndDate >= shiftEnd) ||
|
||
(a.StartDate >= shiftStart && a.StartDate <= shiftEnd) ||
|
||
(a.EndDate >= shiftStart && a.EndDate <= shiftEnd) // خروج در شیفت باشد
|
||
|
||
).ToList();
|
||
|
||
#region ورود
|
||
|
||
//RollCall entryRollCall;
|
||
// تعجیل در ورود - زود اومدن
|
||
|
||
var earlyEntryRollCall = rollCallsInShift.OrderBy(x => x.StartDate).FirstOrDefault(x => x.StartDate < employeeShift.start);
|
||
|
||
var lateEntryRollCall = rollCallsInShift.OrderBy(x => x.StartDate).FirstOrDefault(x => x.StartDate > employeeShift.start);
|
||
|
||
|
||
|
||
|
||
var previousShift = employeeShifts.OrderByDescending(x => x.start)
|
||
.FirstOrDefault(x => x.end < employeeShift.start);
|
||
|
||
if (earlyEntryRollCall != null)
|
||
{
|
||
if (previousShift != null)
|
||
{
|
||
if (earlyEntryRollCall.StartDate > previousShift.end)
|
||
{
|
||
earlyEntryDuration = shiftStart - earlyEntryRollCall.StartDate!.Value;
|
||
earlyEntryRollCall.SetEarlyEnter(earlyEntryDuration);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
earlyEntryDuration = shiftStart - earlyEntryRollCall.StartDate!.Value;
|
||
earlyEntryRollCall.SetEarlyEnter(earlyEntryDuration);
|
||
}
|
||
}
|
||
// تاخیر در ورود - دیر اومدن
|
||
else
|
||
{
|
||
if (lateEntryRollCall != null && (rollCallsInShift.Any(x =>
|
||
x.EndDate > shiftStart && x.EndDate < lateEntryRollCall.StartDate) == false))
|
||
{
|
||
lateEntryDuration = lateEntryRollCall.StartDate!.Value - shiftStart;
|
||
lateEntryRollCall.SetLateEnter(lateEntryDuration);
|
||
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region خروج
|
||
|
||
RollCall exitRollCall;
|
||
|
||
|
||
var earlyExitRollCall = rollCallsInShift.OrderByDescending(x => x.EndDate).FirstOrDefault(x => x.EndDate < employeeShift.end);
|
||
|
||
var lateExitRollCall = rollCallsInShift.OrderBy(x => x.EndDate).FirstOrDefault(x => x.EndDate > employeeShift.end);
|
||
|
||
// تعجیل در خروج - زود رفتن
|
||
var nextShift = employeeShifts.OrderBy(x => x.start)
|
||
.FirstOrDefault(x => x.start > employeeShift.end);
|
||
|
||
if (earlyExitRollCall != null && (rollCallsInShift.Any(x =>
|
||
x.StartDate < shiftEnd && x.StartDate > earlyExitRollCall.EndDate) == false))
|
||
{
|
||
var earlyExit = (shiftEnd - earlyExitRollCall.EndDate!.Value);
|
||
earlyExitRollCall.SetEarlyExit(earlyExit);
|
||
|
||
}
|
||
// تاخیر در خروج - دیر رفتن
|
||
else
|
||
{
|
||
if (lateExitRollCall != null)
|
||
{
|
||
if (nextShift != null)
|
||
{
|
||
if (lateExitRollCall.EndDate < nextShift.start)
|
||
{
|
||
lateExitDuration = lateExitRollCall.EndDate!.Value - shiftEnd;
|
||
lateExitRollCall.SetLateExit(lateExitDuration);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
lateExitDuration = lateExitRollCall.EndDate!.Value - shiftEnd;
|
||
lateExitRollCall.SetLateExit(lateExitDuration);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
#endregion
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case WorkshopShiftStatus.Rotating:
|
||
|
||
TimeSpan lateEntryDurationRotating = new TimeSpan();
|
||
TimeSpan earlyEntryDurationRotating = new TimeSpan();
|
||
TimeSpan lateExitDurationRotating = new TimeSpan();
|
||
TimeSpan earlyExitDurationRotating = new TimeSpan();
|
||
|
||
var rotatingShifts = shiftDetails.rotatingShifts;
|
||
|
||
var rollCallStartEnds = rollCalls.Select(x => (x.StartDate.Value, x.EndDate.Value)).ToList();
|
||
|
||
var shift = FindRotatingShift(rollCallStartEnds, rotatingShifts);
|
||
|
||
var rotatingShiftStart = shift.start;
|
||
var rotatingShiftEnd = shift.end;
|
||
|
||
var rollCallsInRotatingShift = rollCalls.Where(a =>
|
||
(a.StartDate <= rotatingShiftStart && a.EndDate >= rotatingShiftStart) ||
|
||
(a.StartDate <= rotatingShiftEnd && a.EndDate >= rotatingShiftEnd) ||
|
||
(a.StartDate >= rotatingShiftStart && a.StartDate <= rotatingShiftEnd) ||
|
||
(a.EndDate >= rotatingShiftStart && a.EndDate <= rotatingShiftEnd) // خروج در شیفت باشد
|
||
|
||
).ToList();
|
||
|
||
#region ورود
|
||
|
||
// تعجیل در ورود - زود اومدن
|
||
|
||
var earlyEntryRollCallRotating = rollCallsInRotatingShift.OrderBy(x => x.StartDate).FirstOrDefault(x => x.StartDate < shift.start);
|
||
|
||
var lateEntryRollCallRotating = rollCallsInRotatingShift.OrderBy(x => x.StartDate).FirstOrDefault(x => x.StartDate > shift.start);
|
||
//برای خالی کردن اولیه حضور غیاب انتخاب شده
|
||
earlyEntryRollCallRotating?.SetEarlyEnter(TimeSpan.Zero);
|
||
|
||
if (earlyEntryRollCallRotating != null)
|
||
{
|
||
|
||
earlyEntryDurationRotating = rotatingShiftStart - earlyEntryRollCallRotating.StartDate!.Value;
|
||
earlyEntryRollCallRotating.SetEarlyEnter(earlyEntryDurationRotating);
|
||
|
||
}
|
||
// تاخیر در ورود - دیر اومدن
|
||
else
|
||
{
|
||
if (lateEntryRollCallRotating != null && (rollCallsInRotatingShift.Any(x =>
|
||
x.EndDate > rotatingShiftStart && x.EndDate < lateEntryRollCallRotating.StartDate) == false))
|
||
{
|
||
lateEntryDurationRotating = lateEntryRollCallRotating.StartDate!.Value - rotatingShiftStart;
|
||
lateEntryRollCallRotating.SetLateEnter(lateEntryDurationRotating);
|
||
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region خروج
|
||
|
||
|
||
|
||
var earlyExitRollCallRotating = rollCallsInRotatingShift.OrderByDescending(x => x.EndDate).FirstOrDefault(x => x.EndDate < shift.end);
|
||
|
||
var lateExitRollCallRotating = rollCallsInRotatingShift.OrderBy(x => x.EndDate).FirstOrDefault(x => x.EndDate > shift.end);
|
||
//برای خالی کردن اولیه حضور غیاب انتخاب شده
|
||
earlyExitRollCallRotating?.SetEarlyExit(TimeSpan.Zero);
|
||
|
||
|
||
|
||
if (earlyExitRollCallRotating != null && (rollCallsInRotatingShift.Any(x =>
|
||
x.StartDate < rotatingShiftEnd && x.StartDate > earlyExitRollCallRotating.EndDate) == false))
|
||
{
|
||
var earlyExit = (rotatingShiftEnd - earlyExitRollCallRotating.EndDate!.Value);
|
||
earlyExitRollCallRotating.SetEarlyExit(earlyExit);
|
||
|
||
}
|
||
// تاخیر در خروج - دیر رفتن
|
||
else
|
||
{
|
||
if (lateExitRollCallRotating != null)
|
||
{
|
||
|
||
lateExitDurationRotating = lateExitRollCallRotating.EndDate!.Value - rotatingShiftEnd;
|
||
lateExitRollCallRotating.SetLateExit(lateExitDurationRotating);
|
||
|
||
|
||
}
|
||
|
||
}
|
||
#endregion
|
||
break;
|
||
|
||
case WorkshopShiftStatus.Irregular:
|
||
break;
|
||
}
|
||
|
||
_rollCallRepository.SaveChanges();
|
||
}
|
||
|
||
private async Task<List<RollCall>> GetRollCallsInShiftDate(DateTime rollCallShiftDate, long employeeId, long workshopId)
|
||
{
|
||
return await _rollCallRepository.GetRollCallsInShiftDate(rollCallShiftDate, employeeId, workshopId);
|
||
}
|
||
|
||
public (DateTime start, DateTime end) FindRotatingShift(DateTime startRollCall, DateTime endRollCall,
|
||
ICollection<CustomizeRotatingShift> rotatingShifts)
|
||
{
|
||
DateTime startDate = startRollCall.Date;
|
||
DateTime endDate = endRollCall.Date;
|
||
|
||
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 =>
|
||
// (twoHourBeforeStart <= shift.Start && twoHourAfterStart >= shift.Start) ||
|
||
// (twoHourBeforeStart <= shift.End && twoHourAfterStart >= 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 =>
|
||
// (twoHourBeforeEnd <= shift.Start && twoHourAfterEnd >= shift.Start) ||
|
||
// (twoHourBeforeEnd <= shift.End && twoHourAfterEnd >= shift.End)).ToList();
|
||
//if (endFilteredTimes.Count == 0)
|
||
//{
|
||
// endFilteredTimes = startFilteredTimes;
|
||
//}
|
||
//else if (endFilteredTimes.Count == 1)
|
||
//{
|
||
// var endChosenShift = endFilteredTimes.First();
|
||
// return endChosenShift;
|
||
//}
|
||
|
||
#endregion
|
||
|
||
#region اشتراک حضور غیاب و شیفت
|
||
|
||
var overlapShifts = shiftDateTimes
|
||
.Select(shift => new
|
||
{
|
||
Shift = shift,
|
||
Overlap = new TimeSpan(Math.Max(0,
|
||
Math.Min(shift.End.Ticks, endRollCall.Ticks) -
|
||
Math.Max(shift.Start.Ticks, startRollCall.Ticks))),
|
||
// زمان حضور فرد در شیفت (مجموع Overlap با شیفت)
|
||
TotalTimeInShift = new TimeSpan(Math.Max(0,
|
||
Math.Min(shift.End.Ticks, endRollCall.Ticks) -
|
||
Math.Max(shift.Start.Ticks, startRollCall.Ticks))),
|
||
StartDistance = Math.Abs((shift.Start - startRollCall).Ticks),
|
||
EndDistance = Math.Abs((shift.End - endRollCall).Ticks),
|
||
TotalDistance = Math.Abs((shift.Start - startRollCall).Ticks) + Math.Abs((shift.End - endRollCall).Ticks)
|
||
})
|
||
.OrderByDescending(s => s.TotalTimeInShift) // 1. بیشترین زمان حضور فرد
|
||
.ThenByDescending(s => s.Overlap) // 2. بیشترین Overlap
|
||
.ThenBy(s => s.TotalDistance)
|
||
.ThenBy(s => s.StartDistance)
|
||
.ThenBy(x => x.EndDistance); // 3. اگر برابر بود،
|
||
|
||
|
||
var overlapChosenShift = overlapShifts.First();
|
||
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 (DateTime start, DateTime end) FindRotatingShift(List<(DateTime StartDate, DateTime EndDate)> rollCalls, ICollection<CustomizeRotatingShift> rotatingShifts)
|
||
{
|
||
|
||
DateTime startDate = rollCalls.MinBy(x => x.StartDate).StartDate.Date;
|
||
DateTime endDate = rollCalls.MaxBy(x => x.EndDate).EndDate.Date;
|
||
|
||
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();
|
||
|
||
//var shiftScores = shiftDateTimes
|
||
// .Select(shift =>
|
||
// {
|
||
// var totalOverlap = TimeSpan.Zero;
|
||
|
||
// var firstRollCall = rollCalls.MinBy(x => x.StartDate);
|
||
// var lastRollCall = rollCalls.MaxBy(x => x.EndDate);
|
||
|
||
// long totalStartDistance = Math.Abs((shift.Start - firstRollCall.StartDate).Ticks);
|
||
// long totalEndDistance = Math.Abs((shift.End - lastRollCall.EndDate).Ticks);
|
||
|
||
|
||
// foreach (var rollCall in rollCalls)
|
||
// {
|
||
// var start = rollCall.StartDate;
|
||
// var end = rollCall.EndDate;
|
||
|
||
// var overlapTicks = Math.Max(0,
|
||
// Math.Min(shift.End.Ticks, end.Ticks) -
|
||
// Math.Max(shift.Start.Ticks, start.Ticks));
|
||
|
||
// totalOverlap += new TimeSpan(overlapTicks);
|
||
// //totalStartDistance += Math.Abs((shift.Start - start).Ticks);
|
||
// //totalEndDistance += Math.Abs((shift.End - end).Ticks);
|
||
// }
|
||
|
||
// return new
|
||
// {
|
||
// Shift = shift,
|
||
// TotalOverlap = totalOverlap,
|
||
// TotalDistance = totalStartDistance + totalEndDistance,
|
||
// StartDistance = totalStartDistance,
|
||
// EndDistance = totalEndDistance
|
||
// };
|
||
// })
|
||
// .OrderBy(s => s.StartDistance)
|
||
// .ThenBy(s => s.TotalDistance)
|
||
// .ThenByDescending(s => s.TotalOverlap)
|
||
// .ThenBy(s => s.EndDistance);
|
||
|
||
|
||
// مرحله 1: گروهبندی بر اساس نزدیکی زمان شروع به اولین حضور و غیاب
|
||
var groupedByStart = shiftDateTimes
|
||
.GroupBy(shift =>
|
||
{
|
||
var firstRollCallStart = rollCalls.Min(x => x.StartDate);
|
||
var ticksDiff = Math.Abs((shift.Start - firstRollCallStart).Ticks);
|
||
return Math.Round(TimeSpan.FromTicks(ticksDiff).TotalMinutes / 30); // گروهبندی هر 30 دقیقه
|
||
})
|
||
.OrderBy(g => g.Key)
|
||
.First(); // نزدیکترین گروه به شروع
|
||
|
||
// مرحله 2 (جایگزین مرحله ۴): گروهبندی بر اساس نزدیکی پایان شیفت به آخرین حضور و غیاب
|
||
var filteredByEnd = groupedByStart
|
||
.Select(shift =>
|
||
{
|
||
var lastRollCallEnd = rollCalls.Max(x => x.EndDate);
|
||
var endDistance = Math.Abs((shift.End - lastRollCallEnd).Ticks);
|
||
return new { Shift = shift, EndDistance = endDistance };
|
||
})
|
||
.GroupBy(x => x.EndDistance / TimeSpan.FromMinutes(30).Ticks) // گروهبندی هر 30 دقیقه
|
||
.OrderBy(g => g.Key)
|
||
.First() // نزدیکترین پایان
|
||
.ToList();
|
||
|
||
// مرحله 3: فیلتر بر اساس بیشترین همپوشانی با حضور و غیابها
|
||
var filteredByOverlap = filteredByEnd
|
||
.Select(shift =>
|
||
{
|
||
var totalOverlap = TimeSpan.Zero;
|
||
foreach (var rollCall in rollCalls)
|
||
{
|
||
var overlapTicks = Math.Max(0,
|
||
Math.Min(shift.Shift.End.Ticks, rollCall.EndDate.Ticks) -
|
||
Math.Max(shift.Shift.Start.Ticks, rollCall.StartDate.Ticks));
|
||
totalOverlap += new TimeSpan(overlapTicks);
|
||
}
|
||
return new { Shift = shift.Shift, TotalOverlap = totalOverlap };
|
||
})
|
||
.GroupBy(x => Math.Round(x.TotalOverlap.TotalMinutes /5)) // گروهبندی همپوشانی
|
||
.OrderByDescending(g => g.Key) // بیشترین همپوشانی اول
|
||
.First()
|
||
.ToList();
|
||
|
||
// مرحله 4 (جایگزین مرحله ۲): فیلتر نهایی بر اساس کمترین مجموع فاصله (TotalDistance)
|
||
var bestShift = filteredByOverlap
|
||
.Select(shift =>
|
||
{
|
||
var firstRollCall = rollCalls.MinBy(x => x.StartDate);
|
||
var lastRollCall = rollCalls.MaxBy(x => x.EndDate);
|
||
var totalDistance =
|
||
Math.Abs((shift.Shift.Start - firstRollCall.StartDate).Ticks) +
|
||
Math.Abs((shift.Shift.End - lastRollCall.EndDate).Ticks);
|
||
return new { Shift = shift.Shift, TotalDistance = totalDistance };
|
||
})
|
||
.OrderBy(x => x.TotalDistance) // کمترین فاصله کلی
|
||
.First(); // بهترین شیفت نهایی
|
||
var overlapChosenShift = bestShift;
|
||
var end = overlapChosenShift.Shift.End;
|
||
if (overlapChosenShift.Shift.End < overlapChosenShift.Shift.Start)
|
||
end = overlapChosenShift.Shift.End.AddDays(1);
|
||
|
||
|
||
return (overlapChosenShift.Shift.Start, end);
|
||
}
|
||
|
||
public BreakTime GetBreakTime(long employeeId, long workshopId)
|
||
{
|
||
var employeeSettings = _customizeWorkshopEmployeeSettingsRepository.GetByEmployeeIdAndWorkshopIdIncludeGroupSettings(workshopId,
|
||
employeeId);
|
||
if (employeeSettings == null)
|
||
{
|
||
return new BreakTime(false, TimeOnly.MinValue);
|
||
}
|
||
|
||
return employeeSettings.BreakTime;
|
||
}
|
||
private DateTime CalculateRegularShiftDate(DateTime startDate, TimeOnly offset)
|
||
{
|
||
DateTime nextOffSetDateTime;
|
||
if (startDate.TimeOfDay >= offset.ToTimeSpan())
|
||
{
|
||
nextOffSetDateTime = startDate.AddDays(1).Date + offset.ToTimeSpan();
|
||
}
|
||
else
|
||
nextOffSetDateTime = startDate.Date + offset.ToTimeSpan();
|
||
|
||
if (nextOffSetDateTime.TimeOfDay >= TimeSpan.FromHours(12))
|
||
return nextOffSetDateTime.Date;
|
||
|
||
|
||
return nextOffSetDateTime.AddDays(-1).Date;
|
||
}
|
||
} |