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; using BeWo.Data.Security; using BeWo.Service.Security; namespace BeWo.Service.Plugins { public class ServiceRecordValidator : AbstractIDSpecificDefaultClass { public virtual Dictionary> ValidateGroupServiceRecordEntry(ServiceRecordDC pServiceRecord, List groupServiceRecords, List employees, DateTime? date, int maxDaysEditServiceRecordsAllowed, decimal flsTotalRounded, out Dictionary overlappingRecords, out Dictionary overlappingEmployees) { var result = new Dictionary>(); overlappingRecords = new Dictionary(); overlappingEmployees = new Dictionary(); 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 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 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.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.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 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.EmployeeOverlapping }); overlappingEmployees.Add(groupRecord.ServiceRecordOid.Value, string.Format("{1}, {0}", employee.FirstName, employee.LastName)); } } } catch (Exception) { result.AddOrUpdateValueInDictionary(groupRecord, new List { ServiceRecordValidationResult.Error }); } } // 4. ApprovedFLSOverspending try { SupportConceptCostBearerRelDC reldc = MapperFactory.SupportConceptCostBearerRelDC_CostBearer2SupportConcept.MapToNewDC(DAOFactory.GenericDAO.GetByID(groupRecord.CostBearer2SupportConceptOid.Value)); Calculations calc = PluginLoader.FindClass(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.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.OutsideOfSupportConcept }); } catch (Exception) { result.AddOrUpdateValueInDictionary(groupRecord, new List { ServiceRecordValidationResult.Error }); } // 6. SettlementInvoiceAlreadyExisting try { IList allSettlementInvoices = DAOFactory.GenericDAO.GetAll(); 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.SettlementInvoiceAlreadyExisting }); } catch (Exception) { result.AddOrUpdateValueInDictionary(groupRecord, new List { 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.FristAbgelaufen }); } } catch (Exception) { result.AddOrUpdateValueInDictionary(groupRecord, new List { 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.OutOfEditLimit }); } catch (Exception) { result.AddOrUpdateValueInDictionary(groupRecord, new List { ServiceRecordValidationResult.Error }); } } return result; } public virtual List ValidateServiceRecord(ServiceRecordDC newServiceRecord, SupportConceptStatisticsDC statistics, int maxDaysEditServiceRecordsAllowed, IList employeeOids, IList cb2scOids) { var result = new List(); 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(); if (srdc != null) { srdc.CalculateRoundedDuration(newServiceRecord); roundedDuration = newServiceRecord.RoundedDuration; } decimal? grd = newServiceRecord.GroupRoundedDuration; if (isGroupRecord) { var groupcalc = PluginLoader.FindClass(); 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 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(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)) { String handlungsOption = "Das Speichern ist nicht möglich."; bool hatDasRechtTrotzdemZuSpeichern = false; if (UserRightHelper.LoggedInUserHasRight(UserRightType.BookServiceRecordOutOfSupportConcept)) { handlungsOption = "Möchten Sie trotzdem Speichern?"; hatDasRechtTrotzdemZuSpeichern = true; } var dc = new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Custom, Message = $"Das gewählte Datum '{start:dd.MM.yyyy}' liegt außerhalb des Hilfeplanzeitraums ('{reldc.StartDate.Value.ToShortDateString()}' bis '{reldc.EndDate.Value.ToShortDateString()}').\n" + handlungsOption, StartDate = reldc.StartDate.Value, EndDate = reldc.EndDate.Value, Allow = hatDasRechtTrotzdemZuSpeichern }; result.Add(dc); } } catch (Exception) { result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error }); } // 6. SettlementInvoiceAlreadyExisting try { IList 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); } // 10. Prüfe Fehlkontakte > 2 Std. if (duration > 120 && (newServiceRecord.ServiceDescription.Name.ToLower().Contains("fehlkontakt") || newServiceRecord.ServiceDescription.Category.Name.ToLower().Contains("fehlkontakt"))) { String message = String.Format("Die eingegebene Dauer des Fehlkontakts überschreitet die maximal abrechenbare Dauer von 2 Stunden.\nMöchten Sie trotzdem speichern?"); result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Custom, Message = message, Allow = true }); } } long? userOid = SecurityUtils.GetLoggedInUser().Oid; foreach (var r in result) { ValidationMessage validationMessage = new ValidationMessage(); validationMessage.Message = r.Message; validationMessage.UserOid = userOid; validationMessage.EmployeeOid = newServiceRecord.Employee?.EmployeeOid; validationMessage.SupportConceptOid = newServiceRecord.SupportConcept?.SupportConceptOid; validationMessage.CustomerName = r.CustomerName; validationMessage.ServiceRecordStart = newServiceRecord.Start; validationMessage.ServiceRecordEnd = newServiceRecord.End; validationMessage.ServiceRecordValidationResult = (int)r.ResultType; DAOFactory.GenericDAO.Insert(validationMessage); } return result; } public virtual ServiceRecordValidationResultDC CheckAbwesenheiten(ServiceRecordDC newServiceRecord, IList 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(reldc.CostBearer.CostBearerID) ?? Calculations.GetInstance(reldc.CostBearer.CostBearerID); Dictionary 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(newServiceRecord.CostBearer2SupportConceptOid.Value); //var customer = c2s.SupportConcept.Customer; //var absenceTimes = MapperFactory.AbsenceTimeDC_AbsenceTime.MapToNewDCs(customer.AbsenceTimes); Dictionary span2Geleistet = new Dictionary(); 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(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(); SupportConceptStatisticsDC stats = null; if (factory == null) { factory = new ServiceRecordStatisticFactory(); } Calculations calc = PluginLoader.FindClass(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 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 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; } public 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 ValidateServiceRecordDeletion(ServiceRecordDC serviceRecord, long employeeOid) { var result = new List(); // 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 {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(serviceRecords.Where(sr => sr.ServiceRecordOid.HasValue).Select(sr => sr.ServiceRecordOid.Value)); var oldVsNew = new Dictionary(); 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; } } }