482 lines
17 KiB
C#
482 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BeWo.Data;
|
|
using BeWo.Data.Access;
|
|
using BeWo.Data.Entities;
|
|
using BeWo.Data.Security;
|
|
using BeWo.Service.Configuration;
|
|
using BeWo.Service.DCEntityMapper;
|
|
using BeWo.Service.Plugins;
|
|
using BS.Shared;
|
|
using BS.Shared.Core;
|
|
using BS.Shared.DataContracts;
|
|
using BS.Shared.Extensions;
|
|
using SecurityUtils = BeWo.Service.Security.SecurityUtils;
|
|
|
|
namespace BeWoAuxilioRitter.Validation
|
|
{
|
|
|
|
public class CustomServiceRecordValidator : ServiceRecordValidator
|
|
{
|
|
private IList<ServiceRecord> _EmployeeRecords = null;
|
|
private AppSettings _AppSettings = AppSettings.CreateSettings();
|
|
|
|
public override string[] GetIgnoreCategoriesForOverlappingCheck()
|
|
{
|
|
return new[] { "Arbeitszeit" };
|
|
}
|
|
|
|
public override bool CheckZukunft()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
// TODO auch bei Arbeitszeit auf Frist prüfen ("// 5. Frist")
|
|
public override List<ServiceRecordValidationResultDC> ValidateServiceRecord(ServiceRecordDC newServiceRecord, SupportConceptStatisticsDC statisticsXX, int maxDaysEditServiceRecordsAllowed, IList<long> employeeOids, IList<long> cb2scOids)
|
|
{
|
|
#region Standard
|
|
var result = new List<ServiceRecordValidationResultDC>();
|
|
|
|
if (!newServiceRecord.Start.HasValue)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
_EmployeeRecords = null;
|
|
_AppSettings = AppSettings.CreateSettings();
|
|
|
|
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."
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Meldung anzeigen bei:
|
|
* 1. Neuem ServiceRecord
|
|
* 2. Ändern der Zeitangaben eines bestehenden ServiceRecord
|
|
*/
|
|
|
|
if (newServiceRecord.ServiceDescription.Category.IsBillable)
|
|
{
|
|
var startDate = newServiceRecord.Start.Value;
|
|
var endDate = newServiceRecord.End.Value;
|
|
|
|
var areTimesEqual = true;
|
|
var isNew = newServiceRecord.ServiceRecordOid is null;
|
|
|
|
if (newServiceRecord.ServiceRecordOid.HasValue)
|
|
{
|
|
var originalServiceRecord = DAOFactory.GenericDAO.LoadByID<ServiceRecord>(newServiceRecord.ServiceRecordOid.Value);
|
|
|
|
var isStartUnchanged = Equals(originalServiceRecord.Start, newServiceRecord.Start);
|
|
var isEndUnchanged = Equals(originalServiceRecord.End, newServiceRecord.End);
|
|
|
|
areTimesEqual = isStartUnchanged && isEndUnchanged;
|
|
}
|
|
|
|
var klientenoid = newServiceRecord.SupportConcept?.Customer?.CustomerOid;
|
|
var mitarbeiteroid = newServiceRecord.Employee?.EmployeeOid;
|
|
|
|
if (klientenoid is object && mitarbeiteroid is object)
|
|
{
|
|
var hasMonatsunterschrift = DAOFactory.SearchDAO.HasConfirmationReceiptSignatureByDateAndEmployeeAndCustomer(klientenoid.Value, mitarbeiteroid.Value, startDate, endDate, newServiceRecord.CostBearer2SupportConceptOid.Value, out var existingSignatures);
|
|
|
|
if (hasMonatsunterschrift && (isNew || false == areTimesEqual))
|
|
{
|
|
var customerName = newServiceRecord.SupportConcept.CustomerName;
|
|
|
|
var customerText = customerName is object ? $"Klient {customerName}" : "diesem Hilfeplan";
|
|
|
|
result.Add(new ServiceRecordValidationResultDC
|
|
{
|
|
ResultType = ServiceRecordValidationResult.SignatureExists,
|
|
Allow = true,
|
|
CustomerName = newServiceRecord.SupportConcept.CustomerName,
|
|
EmployeeName = newServiceRecord.Employee.ToString(),
|
|
Message = $"Für den ausgewählten Zeitraum existiert bereits eine Monatsunterschrift bei {customerText}, die mit dem Anlegen dieses Eintrags verfällt und neu geleistet werden muss."
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newServiceRecord.ServiceRecordOid.HasValue && newServiceRecord.ServiceDescription.Category.IsBillable)
|
|
{
|
|
var original = DAOFactory.GenericDAO.LoadByID<ServiceRecord>(newServiceRecord.ServiceRecordOid.Value);
|
|
|
|
var startEqual = Equals(original.Start, newServiceRecord.Start);
|
|
var endEqual = Equals(original.End, newServiceRecord.End);
|
|
|
|
var areTimesEqual = startEqual && endEqual;
|
|
|
|
var confirmationReceiptSignatures = DAOFactory.SearchDAO.GetConfirmationReceiptSignaturesByServiceRecord(newServiceRecord.ServiceRecordOid.Value);
|
|
|
|
if (!areTimesEqual && confirmationReceiptSignatures.Any())
|
|
{
|
|
var message = "Für diesen Eintrag existiert bereits eine Monatsunterschrift. Das Verändern von Datum oder Uhrzeit führt zu einer ungültigen Monatsunterschrift, die erneut geleistet werden muss, um auf dem Quittierungsbeleg angezeigt zu werden.";
|
|
|
|
result.Add(new ServiceRecordValidationResultDC
|
|
{
|
|
ResultType = ServiceRecordValidationResult.SignatureExists,
|
|
Message = message,
|
|
Allow = true,
|
|
CustomerName = newServiceRecord.SupportConcept?.CustomerName,
|
|
EmployeeName = newServiceRecord.Employee?.ToString()
|
|
});
|
|
}
|
|
}
|
|
|
|
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.SupportConcept != null && newServiceRecord.CostBearer != null && 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);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
try
|
|
{
|
|
// 5. Frist (ANPASSUNG: immer auch die Arbeittszeit prüfen
|
|
|
|
if (_AppSettings.AnzTageZeiterfassErfolgt > 0)
|
|
{
|
|
var dt = new DateTime(start.Year, start.Month, start.Day);
|
|
dt = dt.AddDays(_AppSettings.AnzTageZeiterfassErfolgt + 1);
|
|
if (DateTime.Now >= dt)
|
|
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.FristAbgelaufen });
|
|
}
|
|
|
|
}
|
|
catch (Exception)
|
|
{
|
|
result.Add(new ServiceRecordValidationResultDC { ResultType = ServiceRecordValidationResult.Error });
|
|
}
|
|
|
|
#region Standard
|
|
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))
|
|
{
|
|
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<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 der 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 Abwesenheit Mitarbeiter
|
|
var resultMa = CheckAbwesenheitMitarbeiter(newServiceRecord, reldc, rd, isGroupRecord, result);
|
|
|
|
if (resultMa != null)
|
|
{
|
|
result.Add(resultMa);
|
|
}
|
|
|
|
// 11. 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.ServiceCategory = newServiceRecord.ServiceDescription.CategoryName;
|
|
validationMessage.ServiceDescription = newServiceRecord.ServiceDescription.Name;
|
|
validationMessage.ServiceRecordStart = newServiceRecord.Start;
|
|
validationMessage.ServiceRecordEnd = newServiceRecord.End;
|
|
validationMessage.ServiceRecordValidationResult = (int)r.ResultType;
|
|
DAOFactory.GenericDAO.Insert(validationMessage);
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Warnung bei Fehlkontakt, wenn bereits eine abrechenbare Leistung an dem Tag gebucht wurde
|
|
if (newServiceRecord.Start.HasValue && newServiceRecord.SupportConcept != null && newServiceRecord.SupportConcept.CostBearer.Contains("LVR") && newServiceRecord.ServiceDescription.Name.ToLower().Contains("fehlkontakt"))
|
|
{
|
|
// Alle Einträge des Tages holen, für den dokumentiert wird
|
|
var span = new DateTimeSpan() { StartDate = new DateTime(newServiceRecord.Start.Value.Year, newServiceRecord.Start.Value.Month, newServiceRecord.Start.Value.Day), EndDate = newServiceRecord.Start.Value.Date.AddDays(1) };
|
|
var eintraegeAmTag = DAOFactory.SearchDAO.FindServiceRecordsInSpan(newServiceRecord.CostBearer2SupportConceptOid.Value, span);
|
|
|
|
if (eintraegeAmTag != null && eintraegeAmTag.Count() > 0)
|
|
{
|
|
result.Add(new ServiceRecordValidationResultDC
|
|
{
|
|
ResultType = ServiceRecordValidationResult.Custom,
|
|
Allow = true,
|
|
Message = "Achtung, bitte beachten, dass bereits eine andere aberechenbare Leistung an diesem Tag im Hilfeplan erbracht wurde und ein Fehlkontakt daher nicht zulässig ist." +
|
|
"\nWollen Sie dennoch speichern?"
|
|
});
|
|
}
|
|
}
|
|
|
|
else if (newServiceRecord.Start.HasValue && newServiceRecord.SupportConcept != null && newServiceRecord.SupportConcept.CostBearer.Contains("LVR")
|
|
&& newServiceRecord.ServiceDescription.Category.IsBillable && !newServiceRecord.ServiceDescription.Name.ToLower().Contains("fehlkontakt"))
|
|
{
|
|
// Alle Einträge des Tages holen, für den dokumentiert wird
|
|
var span = new DateTimeSpan() { StartDate = new DateTime(newServiceRecord.Start.Value.Year, newServiceRecord.Start.Value.Month, newServiceRecord.Start.Value.Day), EndDate = newServiceRecord.Start.Value.Date.AddDays(1) };
|
|
var eintraegeAmTag = DAOFactory.SearchDAO.FindServiceRecordsInSpan(newServiceRecord.CostBearer2SupportConceptOid.Value, span);
|
|
|
|
|
|
if (eintraegeAmTag != null && eintraegeAmTag.Any(s => s.ServiceDescription.ServiceCategory.IsBillable && s.ServiceDescription.Name.ToLower().Contains("fehlkontakt")))
|
|
{
|
|
result.Add(new ServiceRecordValidationResultDC
|
|
{
|
|
ResultType = ServiceRecordValidationResult.Custom,
|
|
Allow = true,
|
|
Message = "Achtung, bitte beachten, dass bereits ein Fehlkontakt an diesem Tag im Hilfeplan erbracht wurde und eine andere aberechenbare Leistung daher nicht zulässig ist." +
|
|
"\nWollen Sie dennoch speichern?"
|
|
});
|
|
}
|
|
}
|
|
if (newServiceRecord.Start.HasValue && newServiceRecord.Start.Value.Date > DateTime.Now)
|
|
{
|
|
result.Add(new ServiceRecordValidationResultDC
|
|
{
|
|
ResultType = ServiceRecordValidationResult.Custom,
|
|
Message = "Das gewählte Datum darf nicht in der Zukunft liegen."
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
// Muss hier mit rein, da es sonst eine NPE gibt
|
|
public override String[] GetIgnoreServiceDescriptionsForOverlappingCheck()
|
|
{
|
|
if (_AppSettings.ShowWorktime && _AppSettings.EnableArbeitszeiterfassung)
|
|
{
|
|
return new[] { "Arbeitszeit", "Arbeitszeiterfassung" };
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
} |