Files
Backend-Api/Company.Domain/RollCallAgg/DomainService/IRollCallDomainService.cs
2025-04-21 15:42:30 +03:30

556 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
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);
}
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(rollCallEndDate,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);
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 shift = FindRotatingShift(starDateTime, endDateTime, 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);
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);
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;
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
}
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;
}
}