Files
BeWoPlaner/Service/Plugins/ServiceRecordValidator.cs
2021-09-23 18:44:18 +02:00

1096 lines
45 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;
using BS.Shared.Extensions;
using BS.Shared.Services;
using BeWo.Service.Configuration;
namespace BeWo.Service.Plugins
{
public class ServiceRecordValidator : AbstractIDSpecificDefaultClass<ServiceRecordValidator>
{
public virtual Dictionary<ServiceRecordDC, List<ServiceRecordValidationResult>> ValidateGroupServiceRecordEntry(ServiceRecordDC pServiceRecord, List<ServiceRecordDC> groupServiceRecords, List<CompactEmployeeDC> employees, DateTime? date, int maxDaysEditServiceRecordsAllowed, decimal flsTotalRounded, out Dictionary<long, string> overlappingRecords, out Dictionary<long, string> overlappingEmployees)
{
var result = new Dictionary<ServiceRecordDC, List<ServiceRecordValidationResult>>();
overlappingRecords = new Dictionary<long, string>();
overlappingEmployees = new Dictionary<long, string>();
long gid = pServiceRecord.GroupOid ?? -1;
foreach (ServiceRecordDC groupRecord in groupServiceRecords)
{
DateTime start = groupRecord.Start.Value;
bool noTime = start.Hour == 0 && start.Minute == 0 && start.Second == 1; //Keine Zeit angegeben
if (!noTime)
{
IList<ServiceRecord> records =
DAOFactory.SearchDAO.FindServiceRecordsDetailsForLastDays(
groupRecord.CostBearer2SupportConceptOid.Value, null);
// 1. ServiceRecordOverlapping
try
{
records = records.Where(r =>
{
bool valid = true;
if (r.GroupOid.HasValue)
valid = r.GroupOid.Value != gid;
return valid;
}).ToList();
List<ServiceRecord> belongsToSupportConcept = records.ToList();
bool overlapping =
belongsToSupportConcept.Any(
rec =>
TimeSpanOverlapsWithOtherTimeSpan(groupRecord.Start, rec.Start,
groupRecord.GroupRoundedDuration ?? groupRecord.RoundedDuration,
rec.GroupRoundedDuration ?? rec.RoundedDuration));
if (overlapping)
{
result.AddOrUpdateValueInDictionary(groupRecord,
new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.ServiceRecordOverlapping });
ServiceRecord rec =
belongsToSupportConcept.First(
re =>
TimeSpanOverlapsWithOtherTimeSpan(groupRecord.Start, re.Start,
groupRecord.GroupRoundedDuration ?? groupRecord.RoundedDuration,
re.GroupRoundedDuration ?? re.RoundedDuration));
overlappingRecords.Add(groupRecord.ServiceRecordOid.Value,
rec.Start.Value.ToShortTimeString() + ", Dauer " +
string.Format("{0:0}", rec.GroupRoundedDuration ?? rec.RoundedDuration) + ", " +
groupRecord.Customer.FullName);
}
}
catch (Exception)
{
result.AddOrUpdateValueInDictionary(groupRecord,
new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.Error });
}
// 2. EmployeeOverlapping
try
{
foreach (CompactEmployeeDC employee in employees)
{
var span = new DateTimeSpan();
span.StartDate = start.Date.AddDays(-1);
span.EndDate = groupRecord.End.Value.Date.AddDays(1);
IList<ServiceRecord> empRecords = DAOFactory.SearchDAO.FindEmployeeServiceRecords(employee.EmployeeOid, span, null, null);
if (groupRecord.ServiceRecordOid.HasValue)
{
empRecords = empRecords.Where(r => r.Oid != groupRecord.ServiceRecordOid.Value).ToList();
}
empRecords = empRecords.Where(r => r.Start.HasValue && r.Start.Value.Second == 0).ToList();
bool overlapping =
empRecords.Any(
record =>
TimeSpanOverlapsWithOtherTimeSpan(pServiceRecord.Start, record.Start, pServiceRecord.RoundedDuration,
record.RoundedDuration));
if (overlapping)
{
result.AddOrUpdateValueInDictionary(groupRecord,
new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.EmployeeOverlapping });
overlappingEmployees.Add(groupRecord.ServiceRecordOid.Value,
string.Format("{1}, {0}", employee.FirstName, employee.LastName));
}
}
}
catch (Exception)
{
result.AddOrUpdateValueInDictionary(groupRecord,
new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.Error });
}
}
// 4. ApprovedFLSOverspending
try
{
SupportConceptCostBearerRelDC reldc = MapperFactory.SupportConceptCostBearerRelDC_CostBearer2SupportConcept.MapToNewDC(DAOFactory.GenericDAO.GetByID<CostBearer2SupportConcept>(groupRecord.CostBearer2SupportConceptOid.Value));
Calculations calc = PluginLoader.FindClass<Calculations>(reldc.CostBearer.CostBearerID) ?? Calculations.GetInstance(reldc.CostBearer.CostBearerID);
decimal totalFLSApproved = calc.GetApprovedHoursTotal(reldc, true) ?? 0m;
if (totalFLSApproved > 0)
{
decimal totalFLSNew = flsTotalRounded + (groupRecord.RoundedDuration / 60);
if (pServiceRecord.ServiceDescription.Category.IsBillable && totalFLSNew > totalFLSApproved)
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.OutsideOfSupportConcept });
}
// 3. OutsideOfSupportConcept
if (date.HasValue && reldc.StartDate.HasValue && reldc.EndDate.HasValue)
if (!date.Value.InBetween(reldc.StartDate.Value, reldc.EndDate.Value, true))
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.OutsideOfSupportConcept });
}
catch (Exception)
{
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.Error });
}
// 6. SettlementInvoiceAlreadyExisting
try
{
IList<SettlementInvoice> allSettlementInvoices = DAOFactory.GenericDAO.GetAll<SettlementInvoice>();
if (allSettlementInvoices.Where(rechnung => rechnung.InvoiceBase.CostBearer2SupportConcept.Oid.HasValue).Any(rechnung => rechnung.InvoiceBase.CostBearer2SupportConcept.Oid.Value.Equals(groupRecord.CostBearer2SupportConceptOid.Value) && rechnung.IsActive.Equals(ActivationTypeId.Active)))
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.SettlementInvoiceAlreadyExisting });
}
catch (Exception)
{
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.Error });
}
try
{
// 5. Frist
var settings = AppSettings.CreateSettings();
if (settings.AnzTageZeiterfassErfolgt > 0)
{
var dt = new DateTime(date.Value.Year, date.Value.Month, 1);
dt = dt.AddDays(settings.AnzTageZeiterfassErfolgt);
if (DateTime.Now >= dt)
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.FristAbgelaufen });
}
}
catch (Exception)
{
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.Error });
}
if (maxDaysEditServiceRecordsAllowed <= 0)
return result;
try
{
// 6. OutOfEditLimit
var dt = new DateTime(date.Value.Year, date.Value.Month, 1);
dt = dt.AddMonths(1);
dt = dt.AddDays(maxDaysEditServiceRecordsAllowed + 1);
if (DateTime.Now >= dt)
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.OutOfEditLimit });
}
catch (Exception)
{
result.AddOrUpdateValueInDictionary(groupRecord, new List<ServiceRecordValidationResult> { ServiceRecordValidationResult.Error });
}
}
return result;
}
public virtual List<ServiceRecordValidationResultDC> ValidateServiceRecord(ServiceRecordDC newServiceRecord, SupportConceptStatisticsDC statistics, int maxDaysEditServiceRecordsAllowed, IList<long> employeeOids, IList<long> cb2scOids)
{
var result = new List<ServiceRecordValidationResultDC>();
if (!newServiceRecord.Start.HasValue)
{
return result;
}
if (!ValidateSignature(newServiceRecord))
{
result.Add(new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.Custom,
Message = "Der Eintrag wurde bereits unterschrieben. Das Verändern von Datum und Uhrzeit ist nicht möglich."
});
}
//// TODO: CheckForExistingSignature an den Stellen einbauen an denen ValidateServiceRecord aufgerufen wird
//if (!ValidateSignature(newServiceRecord))
//{
// result.Add(new ServiceRecordValidationResultDC
// {
// ResultType = ServiceRecordValidationResult.SignatureExists,
// Message = "Der Eintrag wurde bereits unterschrieben. Das Verändern von Datum und Uhrzeit führt zur Löschen der Unterschrift!"
// });
//}
bool isGroupRecord = (cb2scOids != null && cb2scOids.Count > 1) ||
(employeeOids != null && employeeOids.Count > 1);
DateTime start = newServiceRecord.Start.Value;
bool noTime = (start.Hour == 0 && start.Minute == 0 && start.Second == 1); //Keine Zeit angegeben
if (!noTime)
{
noTime = newServiceRecord.ServiceDescription.DoNotCheckOverlapping;
}
var duration = (decimal)newServiceRecord.End.Value.Subtract(newServiceRecord.Start.Value).TotalMinutes;
if (!noTime && duration == 0)
{
noTime = true;
}
newServiceRecord.RoundedDuration = duration;
decimal roundedDuration = duration;
var srdc = PluginLoader.FindClass<ServiceRecordDurationCalculator>();
if (srdc != null)
{
srdc.CalculateRoundedDuration(newServiceRecord);
roundedDuration = newServiceRecord.RoundedDuration;
}
decimal? grd = newServiceRecord.GroupRoundedDuration;
if (isGroupRecord)
{
var groupcalc = PluginLoader.FindClass<GroupDurationCalculator>();
if (groupcalc != null)
{
var gd = groupcalc.CalculateGroupDuration(cb2scOids.Count, employeeOids.Count, duration, cb2scOids.ToArray());
if (gd == null)
{
grd = roundedDuration;
roundedDuration = Math.Round(roundedDuration / cb2scOids.Count, 0, MidpointRounding.AwayFromZero);
}
else
{
grd = gd.TotalDuration;
roundedDuration = gd.SingleDuration;
}
}
}
//var minuteInterval = cbDC.ActualMinuteIntervall;
//if (minuteInterval > 0)
newServiceRecord.RoundedDuration = roundedDuration;
decimal rd = newServiceRecord.RoundedDuration;
long? cb2ScOid = null;
if (newServiceRecord.CostBearer2SupportConceptOid.HasValue)
{
cb2ScOid = newServiceRecord.CostBearer2SupportConceptOid.Value;
}
IList<ServiceRecord> allCustomerRecords = null;
if (!noTime)
{
bool overlapsCustomer = false;
if (cb2ScOid.HasValue)
{
// 1. Customer ServiceRecordOverlapping
try
{
allCustomerRecords = DAOFactory.SearchDAO.FindServiceRecordsDetailsForLastDays(cb2ScOid.Value, null);
if (newServiceRecord.ServiceRecordOid.HasValue)
{
allCustomerRecords = allCustomerRecords.Where(r => r.Oid != newServiceRecord.ServiceRecordOid.Value).ToList();
}
ServiceRecordValidationResultDC resultDc = CheckOverlappingCustomerServiceRecords(newServiceRecord, start, allCustomerRecords, isGroupRecord, grd ?? rd);
if (resultDc != null)
{
overlapsCustomer = true;
result.Add(resultDc);
}
}
catch (Exception)
{
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error });
}
}
// 2. EmployeeOverlapping
if (!overlapsCustomer)
{
if (!isGroupRecord)
{
ServiceRecordValidationResultDC resultdc = CheckOverlappingEmployeeServiceRecords(newServiceRecord, start, newServiceRecord.Employee.EmployeeOid, rd);
if (resultdc != null)
result.Add(resultdc);
}
else if (employeeOids != null)
{
foreach (long employeeOid in employeeOids)
{
ServiceRecordValidationResultDC resultdc = null;
if (resultdc == null)
{
resultdc = CheckOverlappingEmployeeServiceRecords(newServiceRecord, start, employeeOid, grd ?? rd);
if (resultdc != null)
result.Add(resultdc);
}
}
}
}
}
try
{
// 5. Frist
var settings = AppSettings.CreateSettings();
if (settings.AnzTageZeiterfassErfolgt > 0)
{
var dt = new DateTime(start.Year, start.Month, start.Day);
dt = dt.AddDays(settings.AnzTageZeiterfassErfolgt + 1);
if (DateTime.Now >= dt)
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.FristAbgelaufen });
}
}
catch (Exception)
{
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error });
}
if (cb2ScOid.HasValue)
{
SupportConceptCostBearerRelDC reldc =
MapperFactory.SupportConceptCostBearerRelDC_CostBearer2SupportConcept.MapToNewDC(
DAOFactory.GenericDAO.GetByID<CostBearer2SupportConcept>(cb2ScOid.Value));
// 4. ApprovedFLSOverspending
try
{
var resultOverspending = CheckApprovedOverspending(reldc, newServiceRecord);
if (resultOverspending != null)
{
result.Add(resultOverspending);
}
// 3. OutsideOfSupportConcept
if (reldc.StartDate.HasValue && reldc.EndDate.HasValue)
if (!start.InBetween(reldc.StartDate.Value, reldc.EndDate.Value, true))
{
var dc = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.OutsideOfSupportConcept,
StartDate = reldc.StartDate.Value,
EndDate = reldc.EndDate.Value
};
result.Add(dc);
}
}
catch (Exception)
{
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error });
}
// 6. SettlementInvoiceAlreadyExisting
try
{
IList<SettlementInvoice> allSettlementInvoices =
DAOFactory.SearchDAO.GetSettlementInvoiceByCostBearer2SupportConceptOid(cb2ScOid.Value);
if (allSettlementInvoices.Count > 0)
{
var r = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.SettlementInvoiceAlreadyExisting
};
r.CustomerName = reldc.SupportConcept.CustomerFullName;
result.Add(r);
}
}
catch (Exception)
{
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error });
}
//7. Prüfe ob Datum in Zukunft liegt
if (CheckZukunft())
{
if (start.Date > DateTime.Now)
{
result.Add(new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.Custom,
Message = "Das gewählte Datum darf nicht in der Zukunft liegen."
});
}
}
// 8. OutOfEditLimit
try
{
if (maxDaysEditServiceRecordsAllowed > 0)
{
var dt = new DateTime(start.Year, start.Month, 1);
dt = dt.AddMonths(1);
dt = dt.AddDays(maxDaysEditServiceRecordsAllowed);
if (DateTime.Now >= dt)
result.Add(new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.OutOfEditLimit
});
}
}
catch (Exception)
{
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error });
}
// 9. Prüfe Abwesenheiten
var resultdc = CheckAbwesenheiten(newServiceRecord, allCustomerRecords, reldc, rd, isGroupRecord);
if (resultdc != null)
{
result.Add(resultdc);
}
}
return result;
}
public virtual ServiceRecordValidationResultDC CheckAbwesenheiten(ServiceRecordDC newServiceRecord, IList<ServiceRecord> allRecords, SupportConceptCostBearerRelDC reldc, decimal rd, bool isGroupRecord)
{
if (reldc != null && newServiceRecord.ServiceDescription.Category.IsBillable)
{
if (reldc.SupportConcept.Customer.AbsenceTimes != null &&
reldc.SupportConcept.Customer.AbsenceTimes.Count > 0)
{
Calculations calc = PluginLoader.FindClass<Calculations>(reldc.CostBearer.CostBearerID) ??
Calculations.GetInstance(reldc.CostBearer.CostBearerID);
Dictionary<DateTimeSpan, decimal> span2MaxHours =
calc.GetAbsencesTimesNotBillable(reldc.SupportConcept.Customer.AbsenceTimes);
if (allRecords == null)
{
allRecords =
DAOFactory.SearchDAO.FindServiceRecordsDetailsForLastDays(
reldc.CostBearer2SupportConceptOid.Value, null);
if (newServiceRecord.ServiceRecordOid.HasValue)
{
allRecords = allRecords.Where(r => r.Oid != newServiceRecord.ServiceRecordOid.Value)
.ToList();
}
if (isGroupRecord && newServiceRecord.GroupOid.HasValue)
{
allRecords = allRecords.Where(r => !r.GroupOid.HasValue || r.GroupOid != newServiceRecord.GroupOid).ToList();
}
}
//var c2s = DAOFactory.GenericDAO.LoadByID<CostBearer2SupportConcept>(newServiceRecord.CostBearer2SupportConceptOid.Value);
//var customer = c2s.SupportConcept.Customer;
//var absenceTimes = MapperFactory.AbsenceTimeDC_AbsenceTime.MapToNewDCs(customer.AbsenceTimes);
Dictionary<DateTimeSpan, decimal> span2Geleistet = new Dictionary<DateTimeSpan, decimal>();
foreach (var span in span2MaxHours.Keys)
{
if (!span2Geleistet.ContainsKey(span))
{
span2Geleistet.Add(span, 0);
}
}
foreach (var sr in allRecords)
{
if (sr.Start.HasValue && sr.End.HasValue)
{
foreach (var span in span2MaxHours.Keys)
{
if (sr.End.Value.Date >= span.StartDateTime &&
sr.Start.Value.Date <= span.EndDateTime)
{
if (sr.ServiceDescription.ServiceCategory.IsBillable)
{
if (!span2Geleistet.ContainsKey(span))
{
span2Geleistet.Add(span, 0);
}
var duration = sr.RoundedDuration;
span2Geleistet[span] += duration;
}
}
}
}
}
foreach (var span in span2MaxHours.Keys)
{
if (newServiceRecord.End.Value.Date >= span.StartDateTime &&
newServiceRecord.Start.Value.Date <= span.EndDateTime)
{
var max = span2MaxHours[span];
if (span2Geleistet.ContainsKey(span))
{
max -= span2Geleistet[span];
}
if (max < 0 || max - rd < 0)
{
String message = String.Format(
"In der Woche vom {0:dd.MM.yyyy}-{1:dd.MM.yyyy} wurden bereits {2:0.##} Stunden geleistet.\nDie eingegebene Zeit kann nicht komplett abgerechnet werden, da der/die gewählte KlientIn in dem angegebenen Zeitraum abwesend ist und die maximal abrechenbaren Stunden in dieser Woche überschritten werden.\nMöchten Sie trotzdem speichern?",
span.StartDateTime, span.EndDateTime, span2Geleistet[span] / 60);
if (span2Geleistet[span] == 0)
{
message = "Die eingegebene Zeit kann nicht komplett abgerechnet werden, da der/die gewählte KlientIn in dem angegebenen Zeitraum abwesend ist und die maximal abrechenbaren Stunden in dieser Woche überschritten werden.\nMöchten Sie trotzdem speichern?";
}
return new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.Custom,
Message = message,
Allow = true
};
}
}
}
}
}
return null;
}
public virtual bool ValidateSignature(ServiceRecordDC sr)
{
if (sr.ServiceRecordOid.HasValue && sr.SignatureOid.HasValue)
{
var existingRecord = DAOFactory.GenericDAO.LoadByID<ServiceRecord>(sr.ServiceRecordOid.Value);
if (existingRecord.Start != sr.Start || existingRecord.End != sr.End)
{
return false;
}
}
return true;
}
public virtual ServiceRecordValidationResultDC CheckApprovedOverspending(SupportConceptCostBearerRelDC reldc, ServiceRecordDC newServiceRecord)
{
decimal totalFLSApproved = 0;
var factory = PluginLoader.FindClass<ServiceRecordStatisticFactory>();
SupportConceptStatisticsDC stats = null;
if (factory == null)
{
factory = new ServiceRecordStatisticFactory();
}
Calculations calc = PluginLoader.FindClass<Calculations>(reldc.CostBearer.CostBearerID) ?? Calculations.GetInstance(reldc.CostBearer.CostBearerID);
//if (reldc.ApprovalPeriodList != null && reldc.ApprovalPeriodList.Count > 1)
//{
// stats = factory.CreateSupportConceptStatisticsImpl(reldc.CostBearer2SupportConceptOid.Value,
// DateTime.Now, newServiceRecord.ServiceRecordOid);
// }
//else
//{
totalFLSApproved = calc.GetApprovedHoursTotal(reldc, true) ?? 0m;
//}
if (totalFLSApproved > 0)
{
stats = factory.CreateSupportConceptStatisticsImpl(reldc.CostBearer2SupportConceptOid.Value,
newServiceRecord.Start ?? DateTime.Now, newServiceRecord.ServiceRecordOid);
decimal flmTotalRounded = 0;
decimal flmApprovedForPeriod = 0;
SupportConceptPeriodStatisticsDC periodStat = GetStatisticsForServiceRecord(stats, newServiceRecord);
if (periodStat != null)
{
flmTotalRounded = periodStat.MinutesProvidedTotalRounded;
flmApprovedForPeriod = periodStat.MinutesApprovedTotal;
//Prüfe nicht übertragbare Stunden
if (periodStat.SupportConceptApprovalPeriod != null && !periodStat.SupportConceptApprovalPeriod.IsApprovedBEShifting && (newServiceRecord.ServiceDescription.Category.IsBillable || periodStat.SupportConceptApprovalPeriod.ServiceCategory != null))
{
if (periodStat.SupportConceptApprovalPeriod.ApprovedBEInterval.HasValue && periodStat.SupportConceptApprovalPeriod.ApprovedBEPerInterval.HasValue)
{
decimal totalfls = (newServiceRecord.RoundedDuration) / 60;
decimal maxAllowed = periodStat.SupportConceptApprovalPeriod.ApprovedBEPerInterval.Value;
if (periodStat.SupportConceptApprovalPeriod.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly)
{
totalfls += periodStat.MinutesProvidedThisWeek / 60;
}
else if (periodStat.SupportConceptApprovalPeriod.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Monthly)
{
totalfls += periodStat.MinutesProvidedThisMonth / 60;
}
if (maxAllowed > 0 && totalfls > maxAllowed)
{
var dc = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.ApprovedFLSOverspending,
ApprovedHours = Math.Round(maxAllowed, 2, MidpointRounding.AwayFromZero),
HoursNew = Math.Round(totalfls, 2, MidpointRounding.AwayFromZero)
};
dc.CustomerName = reldc.SupportConcept.CustomerFullName;
return dc;
}
}
}
}
else
{
flmTotalRounded = stats.MinutesProvidedTotalRounded;
flmApprovedForPeriod = stats.MinutesApprovedTotal;
}
decimal totalFLSNew = (flmTotalRounded + newServiceRecord.RoundedDuration) / 60;
decimal approvedFlsForPeriod = (flmApprovedForPeriod / 60);
approvedFlsForPeriod = Math.Round(approvedFlsForPeriod, 2, MidpointRounding.AwayFromZero);
if (approvedFlsForPeriod > 0 && totalFLSNew > approvedFlsForPeriod)
{
if (newServiceRecord.ServiceDescription.Category.IsBillable ||
(periodStat != null && periodStat.SupportConceptApprovalPeriod != null && periodStat.SupportConceptApprovalPeriod.ServiceCategory != null))
{
var dc = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.ApprovedFLSOverspending,
ApprovedHours = Math.Round((flmApprovedForPeriod / 60), 2, MidpointRounding.AwayFromZero),
HoursNew = Math.Round(totalFLSNew, 2, MidpointRounding.AwayFromZero)
};
dc.CustomerName = reldc.SupportConcept.CustomerFullName;
return dc;
}
}
}
return null;
}
public virtual bool CheckZukunft()
{
//#if DEBUG
//return false;
//#endif
return true;
}
public virtual ServiceRecordValidationResultDC CheckOverlappingCustomerServiceRecords(ServiceRecordDC newServiceRecord, DateTime start, IList<ServiceRecord> allCustomerRecords, bool isGroupRecord, decimal roundedDuration)
{
//Nur für abrechenbare Leistungen prüfen.
if (newServiceRecord.ServiceDescription.Category.IsBillable)
{
//if (isGroupRecord && newServiceRecord.GroupOid.HasValue)
//{
// records = records.Where(r => !r.GroupOid.HasValue || r.GroupOid != newServiceRecord.GroupOid).ToList();
// }
var customerRecordsWithTime = allCustomerRecords.Where(r => r.Start.HasValue && r.Start.Value.Second == 0).ToList();
if (isGroupRecord && newServiceRecord.GroupOid.HasValue)
{
customerRecordsWithTime =
customerRecordsWithTime.Where(r => !r.GroupOid.HasValue || r.GroupOid != newServiceRecord.GroupOid).ToList();
}
ServiceRecord overlappingRecord =
customerRecordsWithTime.FirstOrDefault(
record => record.ServiceDescription.ServiceCategory.IsBillable &&
TimeSpanOverlapsWithOtherTimeSpan(start, record.Start,
roundedDuration,
record.GroupRoundedDuration ?? record.RoundedDuration));
if (overlappingRecord != null)
{
var rdc = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.ServiceRecordOverlapping,
Message = String.Format("{0}, Dauer {1:0} Minuten", overlappingRecord.Start.Value.ToShortTimeString(),
overlappingRecord.GroupRoundedDuration ?? overlappingRecord.RoundedDuration)
};
Customer c = overlappingRecord.Customer;
if (c != null)
{
rdc.CustomerName = String.Format("{0}, {1}", c.Person.LastName, c.Person.FirstName);
}
Employee e = overlappingRecord.Employee;
if (e != null)
{
rdc.EmployeeName = String.Format("{0} {1}", e.Person.FirstName, e.Person.LastName);
}
return rdc;
}
}
return null;
}
public virtual ServiceRecordValidationResultDC CheckOverlappingEmployeeServiceRecords(ServiceRecordDC newServiceRecord, DateTime start, long employeeOid, decimal roundedDuration)
{
var span = new DateTimeSpan();
span.StartDate = start.Date.AddDays(-1);
span.EndDate = newServiceRecord.End.Value.Date.AddDays(1);
IList<ServiceRecord> empRecords = DAOFactory.SearchDAO.FindEmployeeServiceRecords(employeeOid, span, null, null);
if (newServiceRecord.ServiceRecordOid.HasValue)
{
empRecords = empRecords.Where(r => r.Oid != newServiceRecord.ServiceRecordOid.Value).ToList();
}
if (newServiceRecord.GroupOid.HasValue)
{
empRecords = empRecords.Where(r => !r.GroupOid.HasValue || r.GroupOid != newServiceRecord.GroupOid).ToList();
}
empRecords = empRecords.Where(r => r.Start.HasValue && r.Start.Value.Second == 0).ToList();
String[] ignoreNames = GetIgnoreCategoriesForOverlappingCheck();
if (ignoreNames != null)
{
bool ignore = ignoreNames.Contains(newServiceRecord.ServiceDescription.Category.Name);
if (ignore)
{
empRecords =
empRecords.Where(r => ignoreNames.Contains(r.ServiceDescription.ServiceCategory.Name)).ToList();
}
else
{
empRecords =
empRecords.Where(r => !ignoreNames.Contains(r.ServiceDescription.ServiceCategory.Name)).ToList();
}
}
ignoreNames = GetIgnoreServiceDescriptionsForOverlappingCheck();
if (ignoreNames != null)
{
bool ignore = ignoreNames.Contains(newServiceRecord.ServiceDescription.Name);
if (ignore)
{
empRecords =
empRecords.Where(r => ignoreNames.Contains(r.ServiceDescription.Name)).ToList();
}
else
{
empRecords =
empRecords.Where(r => !ignoreNames.Contains(r.ServiceDescription.Name)).ToList();
}
}
try
{
ServiceRecord overlappingRecord =
empRecords.FirstOrDefault(
record =>
TimeSpanOverlapsWithOtherTimeSpan(start, record.Start, roundedDuration, record.GroupRoundedDuration ?? record.RoundedDuration));
if (overlappingRecord != null)
{
var rdc = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.EmployeeOverlapping,
Message = String.Format("{0}, Dauer {1:0} Minuten", overlappingRecord.Start.Value.ToShortTimeString(),
overlappingRecord.GroupRoundedDuration ?? overlappingRecord.RoundedDuration)
};
Customer c = overlappingRecord.Customer;
if (c != null)
{
rdc.Message += String.Format(" bei Klient/in {0}, {1}", c.Person.LastName, c.Person.FirstName);
}
Employee e = overlappingRecord.Employee;
if (e != null)
{
rdc.EmployeeName = String.Format("{0} {1}", e.Person.FirstName, e.Person.LastName);
}
return rdc;
}
}
catch (Exception)
{
return new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error };
}
return null;
}
public virtual String[] GetIgnoreCategoriesForOverlappingCheck()
{
return null;
}
public virtual String[] GetIgnoreServiceDescriptionsForOverlappingCheck()
{
return null;
}
private SupportConceptPeriodStatisticsDC GetStatisticsForServiceRecord(SupportConceptStatisticsDC statistics, ServiceRecordDC newServiceRecord)
{
if (statistics != null && statistics.PeriodStatistics.Count > 0)
{
//Prüfe erst Bewilligungen die nicht übertragbar sind
foreach (SupportConceptPeriodStatisticsDC pstat in statistics.PeriodStatistics)
{
if (pstat.SupportConceptApprovalPeriod != null &&
!pstat.SupportConceptApprovalPeriod.IsApprovedBEShifting)
{
if (BelongsToPeriodStatistic(newServiceRecord, pstat))
return pstat;
}
}
foreach (SupportConceptPeriodStatisticsDC pstat in statistics.PeriodStatistics)
{
if (pstat.SupportConceptApprovalPeriod != null && !pstat.SupportConceptApprovalPeriod.IsApprovedBEShifting && pstat.SupportConceptApprovalPeriod.ServiceCategory == null)
{
if (pstat.SupportConceptApprovalPeriod.StartDate <= newServiceRecord.Start)
{
if (!pstat.SupportConceptApprovalPeriod.EndDate.HasValue ||
(newServiceRecord.Start.Value.Date <= pstat.SupportConceptApprovalPeriod.EndDate.Value.Date))
{
return pstat;
}
}
}
}
//Prüfen, ob es Bewilligungen mit Kategorie gibt:
if (statistics.PeriodStatistics.Count > 1)
{
bool mindestensEinerHatKategorie = false;
foreach (SupportConceptPeriodStatisticsDC pstat in statistics.PeriodStatistics)
{
if (pstat.SupportConceptApprovalPeriod != null &&
pstat.SupportConceptApprovalPeriod.ServiceCategory != null)
{
mindestensEinerHatKategorie = true;
}
}
if (!mindestensEinerHatKategorie)
{
return null;
}
//Prüfen, ob es Bewilligungen mit unterschiedlicher Kategorie gibt:
bool mindestensZweiHabenUnterschiedlicheKategorie = false;
ServiceCategoryDC cat = null;
foreach (SupportConceptPeriodStatisticsDC pstat in statistics.PeriodStatistics)
{
if (pstat.SupportConceptApprovalPeriod != null &&
pstat.SupportConceptApprovalPeriod.ServiceCategory != null)
{
if (cat != null)
{
if (pstat.SupportConceptApprovalPeriod.ServiceCategory.ServiceCategoryOid !=
cat.ServiceCategoryOid)
{
mindestensZweiHabenUnterschiedlicheKategorie = true;
}
}
else
{
cat = pstat.SupportConceptApprovalPeriod.ServiceCategory;
}
}
}
if (!mindestensZweiHabenUnterschiedlicheKategorie)
{
return null;
}
}
foreach (SupportConceptPeriodStatisticsDC pstat in statistics.PeriodStatistics)
{
if (BelongsToPeriodStatistic(newServiceRecord, pstat))
return pstat;
}
foreach (SupportConceptPeriodStatisticsDC pstat in statistics.PeriodStatistics)
{
if (pstat.SupportConceptApprovalPeriod != null && pstat.SupportConceptApprovalPeriod.ServiceCategory == null)
{
if (pstat.SupportConceptApprovalPeriod.StartDate <= newServiceRecord.Start)
{
if (!pstat.SupportConceptApprovalPeriod.EndDate.HasValue ||
(newServiceRecord.Start.Value.Date <= pstat.SupportConceptApprovalPeriod.EndDate.Value.Date))
{
return pstat;
}
}
}
}
}
return null;
}
public static bool TimeSpanOverlapsWithOtherTimeSpan(DateTime? pStart, DateTime? rStart, decimal pRoundedDuration, decimal rRoundedDuration)
{
DateTime? pEnd = pStart.Value.AddMinutes(Convert.ToDouble(pRoundedDuration));
DateTime? rEnd = rStart.Value.AddMinutes(Convert.ToDouble(rRoundedDuration));
bool overlaps = pStart == rStart || rEnd == pEnd || pEnd == rStart || pStart == rEnd ||
rStart > pStart && rStart < pEnd || rEnd > pStart && rEnd < pEnd ||
pStart > rStart && pStart < rEnd || pEnd > rStart && pEnd < rEnd;
overlaps = !(pEnd <= rStart || pStart >= rEnd);
return overlaps;
}
private bool BelongsToPeriodStatistic(ServiceRecordDC newServiceRecord, SupportConceptPeriodStatisticsDC pstat)
{
if (pstat.SupportConceptApprovalPeriod != null && pstat.SupportConceptApprovalPeriod.ServiceCategory != null)
if (pstat.SupportConceptApprovalPeriod.ServiceCategory.ServiceCategoryOid ==
newServiceRecord.ServiceDescription.Category.ServiceCategoryOid)
{
if (pstat.SupportConceptApprovalPeriod.StartDate <= newServiceRecord.Start)
{
if (!pstat.SupportConceptApprovalPeriod.EndDate.HasValue ||
(newServiceRecord.Start.Value.Date <= pstat.SupportConceptApprovalPeriod.EndDate.Value.Date))
{
return true;
}
}
}
return false;
}
public List<ServiceRecordValidationResultDC> ValidateServiceRecordDeletion(ServiceRecordDC serviceRecord, long employeeOid)
{
var result = new List<ServiceRecordValidationResultDC>();
// 1. Prüfe Unterschrift vorhanden
if (serviceRecord.SignatureOid.HasValue)
{
result.Add(new ServiceRecordValidationResultDC
{
Allow = true,
ResultType = ServiceRecordValidationResult.Custom,
Message = "Dieser Eintrag wurde bereits unterschrieben.\nMöchten Sie ihn trotzdem löschen?"
});
}
// TODO: Flag beachten
// 2. Prüfe, ob eine Mitarbeiterunterschrift mit diesem Eintrag verknüpft ist
//if(serviceRecord.ServiceRecordOid.HasValue)
//{
// var hasEmployeeSignature = DAOFactory.SearchDAO.HasEmployeeSignaturesForServiceRecord(serviceRecord.ServiceRecordOid.Value);
// if(hasEmployeeSignature)
// {
// result.Add(new ServiceRecordValidationResultDC
// {
// ResultType = ServiceRecordValidationResult.HasEmployeeSignature
// });
// }
//}
return result;
}
// Nur bei Updates von ServiceRecords
public virtual ServiceRecordValidationResultDC CheckForExistingSignature(ServiceRecordDC serviceRecord)
{
if(serviceRecord == null)
{
return null;
}
var serviceRecords = new List<ServiceRecordDC> {serviceRecord};
if (serviceRecord.GroupOid.HasValue)
{
var serviceRecordGroup = DAOFactory.SearchDAO.FindServiceRecordGroup(serviceRecord.GroupOid.Value);
if(serviceRecordGroup?.ServiceRecordList.Any() ?? false)
{
serviceRecords.AddRange(MapperFactory.ServiceRecordDC_ServiceRecord.MapToNewDCs(serviceRecordGroup.ServiceRecordList.Where(sr => sr.Oid != serviceRecord.ServiceRecordOid)));
}
}
var message = serviceRecords.Count > 1
? "Es existiert bereits eine Unterschrift. Das Ändern des Datums oder der Uhrzeiten führt zur Löschung der vorhandenen Unterschrift"
: "Es existieren bereits Unterschriften. Das Ändern des Datums oder der Uhrzeiten führt zur Löschung der vorhandenen Unterschriften";
var result = new ServiceRecordValidationResultDC
{
ResultType = ServiceRecordValidationResult.SignatureExists,
Message = message
};
var originals = DAOFactory.GenericDAO.LoadByIDs<ServiceRecord>(serviceRecords.Where(sr => sr.ServiceRecordOid.HasValue).Select(sr => sr.ServiceRecordOid.Value));
var oldVsNew = new Dictionary<ServiceRecord, ServiceRecordDC>();
var orderedOldOnes = originals.Where(x => x.Oid.HasValue).OrderBy(x => x.Oid.Value).ToList();
var orderedNewOnes = serviceRecords.Where(x => x.ServiceRecordOid.HasValue).OrderBy(x => x.ServiceRecordOid.Value).ToList();
foreach (var oldSr in orderedOldOnes)
{
var newSr = orderedNewOnes.FirstOrDefault(f => f.ServiceRecordOid == oldSr.Oid);
if (newSr != null)
{
oldVsNew.Add(oldSr, newSr);
}
}
foreach(var kvp in oldVsNew)
{
if ((kvp.Key.Start != kvp.Value.Start || kvp.Key.End != kvp.Value.End) && kvp.Key.SignatureOid.HasValue)
{
return result;
}
}
return null;
}
}
}