MH: Team Pfeil Korridore in der Abrechnung finale Version

SupportOID=167413
This commit is contained in:
2026-07-07 16:13:35 +02:00
parent 639c700300
commit 5e7573ba2b
5 changed files with 141 additions and 27 deletions

View File

@@ -179,7 +179,7 @@ namespace TeamPfeil
if (ap.ServiceCategory.Name.Contains("Qualifizierte Assistenz"))
{
budgetFuerQualAss = KorridorberechnungHelper.BerechneBudgetFuerQualAss(bewilligtGesamt);
double genutzt = KorridorberechnungHelper.BerechneGenutzteKorridorstunden(scOid, ap.ServiceCategory, budgetFuerQualAss, ro.EndDate, true);
double genutzt = KorridorberechnungHelper.BerechneGenutzteKorridorstunden(scOid, ap.ServiceCategory, budgetFuerQualAss, ro.EndDate.AddDays(1).AddTicks(-1), true);
ro.TotalFLMQualified = genutzt;
ro.TotalFLSQualified = budgetFuerQualAss - genutzt;
@@ -194,7 +194,7 @@ namespace TeamPfeil
else if (ap.ServiceCategory.Name.Contains("Unterstützende Assistenz"))
{
budgetFuerUnterstAss = KorridorberechnungHelper.BerechneBudgetFuerUnterstAss(bewilligtGesamt);
double genutzt = KorridorberechnungHelper.BerechneGenutzteKorridorstunden(scOid, ap.ServiceCategory, budgetFuerUnterstAss, ro.EndDate, false);
double genutzt = KorridorberechnungHelper.BerechneGenutzteKorridorstunden(scOid, ap.ServiceCategory, budgetFuerUnterstAss, ro.EndDate.AddDays(1).AddTicks(-1), false);
ro.TotalFLMUnqualified = genutzt;
ro.TotalFLSUnqualified = budgetFuerUnterstAss - genutzt;

View File

@@ -69,7 +69,57 @@ namespace TeamPfeil.Invoicing
}
}
public override IList<InvoiceItem> CreateInvoiceItems(List<ServiceRecordDC> serviceRecordsInPeriod, DateTimeSpan invoicePeriod, SupportConceptApprovalPeriod scap, Calculations calc)
{
var list = new List<InvoiceItem>();
foreach (ServiceRecordDC iServiceRecord in serviceRecordsInPeriod)
{
if (iServiceRecord.ServiceDescription.Category.IsBillable)
{
if (!iServiceRecord.AlreadyAccounted)
{
InvoiceItem invoiceItem = CreateInvoiceItem(iServiceRecord, scap, calc);
if (invoiceItem != null)
{
// Wenn das Item ganz normal abgerechnet wird, einfach normal zur Liste
if (invoiceItem.CustomString5 != "Mischitem")
list.Add(invoiceItem);
else
{
// Ist es ein Mischitem, müssen zwei Items erstellt werden. Eins mit dem Mitarbeiterstundensatz und eins mit dem Leistungsstundensatz
// invoiceItem.CustomNumber1: Stunden für den MA-Stundensatz
// invoiceItem.CustomNumber2: MA-Stundensatz
// invoiceItem.CustomString2: Leistungsname für MA-Stundensatz
// invoiceItem.CustomNumber3: Stunden für den Leistungs-Stundensatz
// invoiceItem.CustomNumber4: Leistungs-Stundensatz
// invoiceItem.CustomString4: Leistungsname für Leistungsstundensatz
var item1 = invoiceItem;
var item2 = CopyInvoiceItem(invoiceItem);
item1.UnitCount = invoiceItem.CustomNumber1;
item1.AmountPerUnit = invoiceItem.CustomNumber2;
item1.AmountTotal = item1.UnitCount * invoiceItem.AmountPerUnit;
item1.GrossAmountTotal = item1.AmountTotal;
item1.ItemDescription = item1.CustomString2;
list.Add(item1);
item2.UnitCount = invoiceItem.CustomNumber3;
item2.AmountPerUnit = invoiceItem.CustomNumber4;
item2.AmountTotal = item2.UnitCount * invoiceItem.AmountPerUnit;
item2.GrossAmountTotal = item2.AmountTotal;
item2.ItemDescription = item1.CustomString4;
list.Add(item2);
}
}
iServiceRecord.AlreadyAccounted = true;
}
}
}
return CreateSummaryInvoiceItems(list, invoicePeriod, scap, calc);
}
public override InvoiceItem CreateInvoiceItem(ServiceRecordDC iServiceRecord, SupportConceptApprovalPeriod scap, Calculations calc)
{
@@ -111,6 +161,7 @@ namespace TeamPfeil.Invoicing
}
}
// Ab 2026 wird das abgerechnet, was dokumentiert wird.
// Sonderfall: Korridor
// Korridor wird Thema, wenn die bewilligte Leistungsform sich von der Qualifikation der dokumentierenden Person unterscheidet.
@@ -121,81 +172,100 @@ namespace TeamPfeil.Invoicing
// den der Korridormitarbeiter eigentlich hat (also qualfi.abhäng. Stundensatz im Kostenträger, der der Qualifikation des Mitarbeiters entspricht)
else
{
bool korridorAufgebraucht = false;
bool mischitem = false; // Mischitem = ServiceRecord kann nur teilweise mit Leistungsstundensatz abgerechnet werden, teils aber mit MA-Qual--Std.satz
bool korridorAufgebraucht = false; // Wenn true, wird statt dem Leistungsstundensatz der MA-Stundensatz genommen
// Mischitem = ServiceRecord kann nur teilweise mit Leistungsstundensatz abgerechnet werden, der Rest muss aber mit MA-Qual--Std.satz abgerechnet werden
// In dem Fall werden im Item beide Teilstunden und beide jeweiligem Stundensätze gesetzt und im Nachgang wird ein weiteres Item erzeugt (in CreateInvoiceItems)
bool mischitem = false;
// KORRIDOR ERMITTELN
var scapDC = MapperFactory.SupportConceptApprovalPeriodDC_SupportConceptApprovalPeriod.MapToNewDC(scap);
var gesamtBewilligt = KorridorberechnungHelper.BerechneStundenBewilligtGesamt(scapDC);
double korridorbudget = 0;
double genutztesKorridorbudget = 0;
double rest = 0;
if (scap.ServiceCategory.Name.Contains("Qualifizierte Assistenz"))
{
korridorbudget = KorridorberechnungHelper.BerechneBudgetFuerQualAss(gesamtBewilligt);
genutztesKorridorbudget = KorridorberechnungHelper.BerechneGenutzteKorridorstunden(iServiceRecord.SupportConcept.SupportConceptOid, scapDC.ServiceCategory,
korridorbudget, iServiceRecord.Start.Value, true);
korridorbudget, iServiceRecord.Start.Value.AddTicks(-1), true);
rest = korridorbudget - genutztesKorridorbudget;
}
else if (scap.ServiceCategory.Name.Contains("Unterstützende Assistenz"))
{
korridorbudget = KorridorberechnungHelper.BerechneBudgetFuerUnterstAss(gesamtBewilligt);
genutztesKorridorbudget = KorridorberechnungHelper.BerechneGenutzteKorridorstunden(iServiceRecord.SupportConcept.SupportConceptOid, scapDC.ServiceCategory,
korridorbudget, iServiceRecord.Start.Value, false);
korridorbudget, iServiceRecord.Start.Value.AddTicks(-1), false);
rest = korridorbudget - genutztesKorridorbudget;
}
// todo: Wenn das Item noch etwas Restbudget hat, aber es nicht ganz ausreicht, um alles abzudecken, muss eig ein Mischitem erzeugt werden.
// Wenn das Item noch etwas Restbudget hat, aber es nicht ganz ausreicht, um alles abzudecken, muss ein Mischitem erzeugt werden.
// Es muss also der Teil, der noch verfügbar ist, mit dem Leistungsstundensatz verrechnet werden und der Rest mit der Qualifikation
// Dazu werden der jeweilige UnitCount und der jeweilige Stundensatz in CustomNumbers gespeichert, um in CreateInvoiceItems ein zweites Item zu machen
if (rest > 0 && rest - Convert.ToDouble(iServiceRecord.RoundedDuration) / 60 < 0)
{
decimal zeitNormalAbrechenbar = Convert.ToDecimal(rest);
decimal zeitMitMitarbeiterStundensatz = iServiceRecord.RoundedDuration / 60 - Convert.ToDecimal(rest);
// Wenn Mischitem, dann UnitCounts zwischenspeichern, um später aufzuteilen
invoiceItem.CustomNumber1 = zeitMitMitarbeiterStundensatz;
invoiceItem.CustomNumber3 = zeitNormalAbrechenbar;
mischitem = true;
invoiceItem.CustomString5 = "Mischitem";
}
else if (rest <= 0)
// Wenn der Rest eh schon unter 0 ist, wird einfach mit dem MA-Stundensatz abgerechnet
else if (rest < 0)
korridorAufgebraucht = true;
if (korridorAufgebraucht) // Stundensatz der Mitarbeitendenqualifikation
if (korridorAufgebraucht || mischitem) // Stundensatz der Mitarbeitendenqualifikation
{
var qualifikation = iServiceRecord.Employee.ValueListEntries.FirstOrDefault(v => v.Value == ValueListEntryType.StaffQualificationsType);
if (qualifikation.Key != null)
{
qualiOid = qualifikation.Key;
if (qualiOid == 2118)
if (qualiOid == 2118) // QA 1
{
stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, qualiOid, iServiceRecord.Start);
invoiceItem.ItemDescription = "Qualifikationsniveau 1: ";
invoiceItem.ItemDescription = "QA 1: ";
}
else if (qualiOid == 2119)
else if (qualiOid == 2119) // QA 2
{
stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, qualiOid, iServiceRecord.Start);
invoiceItem.ItemDescription = "Qualifikationsniveau 2: ";
invoiceItem.ItemDescription = "QA 2: ";
}
else if (qualiOid == 2120)
else if (qualiOid == 2120) // UA 3
{
stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, qualiOid, iServiceRecord.Start);
invoiceItem.ItemDescription = "Qualifikationsniveau 3: ";
invoiceItem.ItemDescription = "UA 3: ";
}
else if (qualiOid == 2121)
else if (qualiOid == 2121) // UA 4
{
stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, qualiOid, iServiceRecord.Start);
invoiceItem.ItemDescription = "Qualifikationsniveau 4: ";
invoiceItem.ItemDescription = "UA 4: ";
}
if (mischitem) // Wenn Mischitem, dann Stundensatz der Person + dessen Qualifikation zwischenspeichern, um später aufzuteilen
{
invoiceItem.CustomNumber2 = stundensatz;
invoiceItem.CustomString2 = invoiceItem.ItemDescription;
}
}
}
else // Stundensatz, der der dokumentierten Leistung entspricht
if (!korridorAufgebraucht || mischitem) // Stundensatz, der der dokumentierten Leistung entspricht
{
var leistung = iServiceRecord.ServiceDescription.Name;
switch (leistung)
{
case "QA 1": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2118, iServiceRecord.Start); invoiceItem.ItemDescription = "Qualifikationsniveau 1: "; break;
case "QA 2": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2119, iServiceRecord.Start); invoiceItem.ItemDescription = "Qualifikationsniveau 2: "; break;
case "UA 3": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2120, iServiceRecord.Start); invoiceItem.ItemDescription = "Qualifikationsniveau 3: "; break;
case "UA 4": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2121, iServiceRecord.Start); invoiceItem.ItemDescription = "Qualifikationsniveau 4: "; break;
case "QA 1": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2118, iServiceRecord.Start); invoiceItem.ItemDescription = "QA 1: "; break;
case "QA 2": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2119, iServiceRecord.Start); invoiceItem.ItemDescription = "QA 2: "; break;
case "UA 3": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2120, iServiceRecord.Start); invoiceItem.ItemDescription = "UA 3: "; break;
case "UA 4": stundensatz = GetStundensatz(scap.CostBearer2SupportConcept, 2121, iServiceRecord.Start); invoiceItem.ItemDescription = "UA 4: "; break;
}
if (mischitem) // Wenn Mischitem, dann Stundensatz der Leistung + Leistungsbezeichnung zwischenspeichern, um später aufzuteilen
{
invoiceItem.CustomNumber4 = stundensatz;
invoiceItem.CustomString4 = invoiceItem.ItemDescription;
}
}
}
@@ -221,7 +291,6 @@ namespace TeamPfeil.Invoicing
invoiceItem.ItemPeriodEnd = bd.BillingPeriodEnd;
invoiceItem.RateFactor = bd.RateFactor;
invoiceItem.AccountingInterval = bd.AccountingInterval;
//iServiceRecord.Employee + " - " + iServiceRecord.ServiceDescription.Name;
invoiceItem.ServiceRecord = DAOFactory.GenericDAO.LoadByID<ServiceRecord>(iServiceRecord.ServiceRecordOid.Value);
return invoiceItem;
@@ -249,7 +318,29 @@ namespace TeamPfeil.Invoicing
return 0;
}
private InvoiceItem CopyInvoiceItem(InvoiceItem invoiceItem)
{
return new InvoiceItem()
{
AccountingInterval = invoiceItem.AccountingInterval,
AmountPerUnit = invoiceItem.AmountPerUnit,
AmountTotal = invoiceItem.AmountTotal,
ApprovalUnit = invoiceItem.ApprovalUnit,
ApprovedPerUnit = invoiceItem.ApprovedPerUnit,
GrossAmountTotal = invoiceItem.GrossAmountTotal,
ItemDescription = invoiceItem.ItemDescription,
IsActive = invoiceItem.IsActive,
ItemPeriodEnd = invoiceItem.ItemPeriodEnd,
ItemPeriodStart = invoiceItem.ItemPeriodStart,
MaxApprovedAmount = invoiceItem.MaxApprovedAmount,
MaxApprovedUnitCount = invoiceItem.MaxApprovedAmount,
RateFactor = invoiceItem.RateFactor,
ServiceRecord = invoiceItem.ServiceRecord,
SupportConceptApprovalPeriodOid = invoiceItem.SupportConceptApprovalPeriodOid,
UnitCount = invoiceItem.UnitCount,
UnitDescription = invoiceItem.UnitDescription,
};
}
public override string GetSummaryItemKey(SupportConceptApprovalPeriod scap, InvoiceItem iitem, bool summaryPerMonth)
{
String key = String.Format("{0}_{1}", iitem.AmountPerUnit, iitem.ItemDescription);

View File

@@ -45,7 +45,7 @@ namespace TeamPfeil.Korridorberechnung
double genutzteStunden = 0;
var serviceRecords = DAOFactory.SearchDAO.FindServiceRecordsForSupportConcept(supportConceptOid.Value)
.Where(sr => sr.ServiceDescription.ServiceCategory.Oid == serviceCategory.ServiceCategoryOid && sr.Start.Value.Date <= reportEndDate)
.Where(sr => sr.ServiceDescription.ServiceCategory.Oid == serviceCategory.ServiceCategoryOid && sr.Start.Value <= reportEndDate)
.ToList();
if (serviceRecords != null && serviceRecords.Count > 0)

View File

@@ -0,0 +1,22 @@
using BeWo.Service.Plugins;
using BS.Shared.DataContracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TeamPfeil.Statistics
{
public class CustomServiceRecordStatisticFactory : ServiceRecordStatisticFactory
{
public override SupportConceptStatisticsDC CreateSupportConceptStatisticsImpl(long costBearer2SupportConceptOid, DateTime statisticsDate, long? ignoreServiceRecordOid)
{
var stats = base.CreateSupportConceptStatisticsImpl(costBearer2SupportConceptOid, statisticsDate, ignoreServiceRecordOid);
stats.PeriodStatistics = stats.PeriodStatistics.OrderByDescending(s => s.SupportConceptApprovalPeriod.StartDate).ToList();
return stats;
}
}
}

View File

@@ -114,6 +114,7 @@
<Compile Include="SettlementReport.Designer.cs">
<DependentUpon>SettlementReport.cs</DependentUpon>
</Compile>
<Compile Include="Statistics\CustomServiceRecordStatisticFactory.cs" />
<Compile Include="Validation\ServiceRecordValidator.cs" />
</ItemGroup>
<ItemGroup>