741 lines
38 KiB
C#
741 lines
38 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using BeWo.Data.Access;
|
|
using BeWo.Data.Entities;
|
|
using BeWo.Report.ReportObjects;
|
|
using BeWo.Service.DCEntityMapper;
|
|
using BeWo.Service.Invoicing;
|
|
using BeWo.Service.Plugins;
|
|
using BS.Shared;
|
|
using BS.Shared.Core;
|
|
using BS.Shared.DataContracts;
|
|
using BS.Shared.DataContracts.Compact;
|
|
using BS.Shared.Extensions;
|
|
using BS.Shared.Services;
|
|
|
|
namespace PerspektivenEV.Invoicing
|
|
{
|
|
public class CollectiveInvoiceCreationBthg : InvoiceCreation
|
|
{
|
|
private DateTime? accountingPeriodStart = null;
|
|
private DateTime? accountingPeriodEnd = null;
|
|
private decimal AMOUNT_PER_KMQA = 0.0231m;
|
|
private decimal AMOUNT_PER_KMKA = 0.0108m;
|
|
private static readonly DateTime KA_NEU_STICHTAG = new DateTime(2026, 7, 1);
|
|
|
|
public override void SetInvoiceId(ServiceInvoice invoice)
|
|
{
|
|
invoice.InvoiceBase.InvoiceId = "SammelrechnungBthg";
|
|
//invoice.InvoiceBase.InvoiceTypeText = "Sammelrechnung LWV";
|
|
}
|
|
|
|
public override List<ServiceInvoiceDC> GenerateServiceInvoices(long costBearerOid, DateTimeSpan invoicePeriod, Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> supportConcepts2ServiceRecords)
|
|
{
|
|
var costBearer = DAOFactory.GenericDAO.LoadByID<CostBearer>(costBearerOid);
|
|
var invoiceList = new List<ServiceInvoice>();
|
|
|
|
var invoiceCounter = 0;
|
|
ServiceInvoice invoice;
|
|
//auf ausdrücklichen Wunsch von Persepektiven werden zwei separate Rechnungen für Bewilligte und Unbewillige Hilfepläne erstellt
|
|
Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> bewilligteHPs = new Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>>();
|
|
Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> unbewilligteHPs = new Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>>();
|
|
|
|
foreach (var sc in supportConcepts2ServiceRecords.Keys)
|
|
{
|
|
if (sc.IsApproved)
|
|
bewilligteHPs.Add(sc, supportConcepts2ServiceRecords[sc]);
|
|
else
|
|
unbewilligteHPs.Add(sc, supportConcepts2ServiceRecords[sc]);
|
|
}
|
|
|
|
invoice = CreateCollectiveBilling(costBearer, invoicePeriod, bewilligteHPs);
|
|
if (invoice != null)
|
|
{
|
|
invoiceList.Add(invoice);
|
|
invoiceCounter++;
|
|
}
|
|
|
|
invoice = CreateCollectiveBilling(costBearer, invoicePeriod, unbewilligteHPs);
|
|
if (invoice != null)
|
|
{
|
|
invoiceList.Add(invoice);
|
|
}
|
|
|
|
var result = invoiceList.Select(item => MapperFactory.ServiceInvoiceDC_ServiceInvoice.MapToNewDC(item)).ToList();
|
|
return result.Count > 1 ? result.OrderBy(i => i.InvoiceBase.CustomerLastName).ToList() : result;
|
|
}
|
|
|
|
public override ServiceInvoice CreateCollectiveBilling(CostBearer costBearer, DateTimeSpan invoicePeriod, Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> supportConcepts2ServiceRecords)
|
|
{
|
|
var invoice = new ServiceInvoice();
|
|
var newDict = new Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>>();
|
|
|
|
foreach (var sc2sr in supportConcepts2ServiceRecords)
|
|
{
|
|
if (!sc2sr.Key.IsDeleted || !sc2sr.Key.IsArchived)
|
|
{
|
|
if (sc2sr.Key.Customer == null)
|
|
{
|
|
newDict.Add(sc2sr.Key, sc2sr.Value);
|
|
}
|
|
if (sc2sr.Key.Customer != null && (sc2sr.Key.Customer.TerminationDate == null || sc2sr.Key.Customer.TerminationDate >= invoicePeriod.EndDate))
|
|
{
|
|
newDict.Add(sc2sr.Key, sc2sr.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
SetRecipientInformation(invoice, costBearer);
|
|
SetSenderInformation(invoice);
|
|
SetInvoiceBaseData(0, invoice, costBearer, invoicePeriod, newDict.Keys.ToList());
|
|
SetCollectiveBillingBaseData(0, invoice, costBearer, invoicePeriod, newDict);
|
|
SetCollectiveBillingServiceInvoicePeriods(invoice, invoicePeriod, newDict);
|
|
SetServiceInvoicePeriodAmounts(invoice);
|
|
return invoice;
|
|
}
|
|
|
|
public override void SetInvoiceBaseData(int invoiceCounter, ServiceInvoice invoice, CostBearer costBearer, DateTimeSpan invoicePeriod,
|
|
IList<CompactSupportConceptDC> supportConceptList)
|
|
{
|
|
if (invoicePeriod != null)
|
|
{
|
|
accountingPeriodStart = invoicePeriod.StartDate;
|
|
accountingPeriodEnd = invoicePeriod.EndDate;
|
|
|
|
//Einmaliges Laden der Preise um nicht bei jeder Bewilligung neu zu starten - bei denen sind Preise für jede Kostenstelle vorgehalten, im Moment aber überall ein Preis
|
|
var preise = DAOFactory.GenericDAO.GetAllActive<Preis>();
|
|
var pauschVar = GetPreis(preise, "BW HTK QA Fahrzeit pro Minute", (DateTime)accountingPeriodStart);
|
|
if (pauschVar != 0)
|
|
{
|
|
AMOUNT_PER_KMQA = pauschVar;
|
|
}
|
|
pauschVar = 0;
|
|
pauschVar = GetPreis(preise, "BW HTK KA Fahrzeit pro Minute", (DateTime)accountingPeriodStart);
|
|
if (pauschVar != 0)
|
|
{
|
|
AMOUNT_PER_KMKA = pauschVar;
|
|
}
|
|
}
|
|
base.SetInvoiceBaseData(invoiceCounter, invoice, costBearer, invoicePeriod, supportConceptList);
|
|
}
|
|
|
|
public override void SetInvoiceItems(ServiceInvoice invoice,
|
|
ServiceInvoicePeriod serviceInvoicePeriod,
|
|
SupportConceptApprovalPeriodDC supportConceptApprovalPeriod,
|
|
CostBearer2SupportConcept cb2sc,
|
|
DateTimeSpan invoicePeriod,
|
|
List<ServiceRecordDC> serviceRecords)
|
|
{
|
|
BS.Shared.Services.Calculations calc = PluginLoader.FindClass<BS.Shared.Services.Calculations>(_SpecificID) ?? BS.Shared.Services.Calculations.GetInstance(_SpecificID);
|
|
var catDisp = supportConceptApprovalPeriod.ServiceCategory;
|
|
bool istNeu = accountingPeriodStart.HasValue && accountingPeriodStart.Value >= KA_NEU_STICHTAG;
|
|
bool istKA = catDisp != null && catDisp.Abbreviation != null && catDisp.Abbreviation.Length >= 2 && catDisp.Abbreviation.Substring(catDisp.Abbreviation.Length - 2) == "KA";
|
|
bool istTS = catDisp != null && catDisp.Abbreviation != null && catDisp.Abbreviation.StartsWith("TS");
|
|
bool istKaFz = catDisp != null && catDisp.Abbreviation != null && catDisp.Abbreviation.Length >= 4 && catDisp.Abbreviation.Substring(catDisp.Abbreviation.Length - 4) == "KAFZ";
|
|
|
|
// Ab 01.07.2026: KA im BeWo (BW/ieH) nach tatsaechlich erbrachten Stunden pro Monat.
|
|
if (istNeu && istKA && !istTS)
|
|
{
|
|
CreateKaBwActualHours(serviceInvoicePeriod, supportConceptApprovalPeriod, cb2sc, serviceRecords);
|
|
}
|
|
// Ab 01.07.2026: Fahrtkosten der KA im BeWo nach tatsaechlich erbrachten KA-Minuten (nicht mehr tageweise pauschal).
|
|
else if (istNeu && istKaFz && !istTS)
|
|
{
|
|
CreateKaBwFahrtkosten(serviceInvoicePeriod, supportConceptApprovalPeriod, cb2sc, serviceRecords);
|
|
}
|
|
else
|
|
{
|
|
CreateFixedAmounts(serviceInvoicePeriod, supportConceptApprovalPeriod, cb2sc);
|
|
}
|
|
}
|
|
|
|
|
|
public override void CreateFixedAmounts(ServiceInvoicePeriod serviceInvoicePeriod, SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, CostBearer2SupportConcept cb2sc)
|
|
{
|
|
var calc = PluginLoader.FindClass<BS.Shared.Services.Calculations>(_SpecificID) ?? BS.Shared.Services.Calculations.GetInstance(_SpecificID);
|
|
var data = calc.GetFixedBillingData(supportConceptApprovalPeriod, serviceInvoicePeriod.Start, serviceInvoicePeriod.End);
|
|
DateTime start = accountingPeriodStart.Value;
|
|
DateTime end = accountingPeriodEnd.Value;
|
|
if (supportConceptApprovalPeriod.StartDate > start)
|
|
{
|
|
start = supportConceptApprovalPeriod.StartDate.Value;
|
|
}
|
|
if (supportConceptApprovalPeriod.EndDate < end)
|
|
{
|
|
end = supportConceptApprovalPeriod.EndDate.Value;
|
|
}
|
|
|
|
decimal LG = 1;
|
|
var cat = supportConceptApprovalPeriod.ServiceCategory;
|
|
if (cat != null && cat.Abbreviation != null && cat.Abbreviation.Length > 3)
|
|
{
|
|
var ii = new InvoiceItem
|
|
{
|
|
ItemPeriodStart = serviceInvoicePeriod.Start,
|
|
ItemPeriodEnd = serviceInvoicePeriod.End,
|
|
SupportConceptApprovalPeriodOid = supportConceptApprovalPeriod.SupportConceptApprovalPeriodOid,
|
|
AccountingInterval = AccountingIntervalType.Daily,
|
|
UnitCount = (decimal)(end - start).TotalDays + 1,
|
|
UnitDescription = "0,00 €"
|
|
};
|
|
|
|
// Fahrtzeiten - Bewilligte Minuten * Fahrtkostenpauschale
|
|
if (cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "FZ" || supportConceptApprovalPeriod.ServiceCategory.Name.Contains("Fahrzeit"))
|
|
{
|
|
if (cat.Abbreviation.Substring(cat.Abbreviation.Length - 4) == "KAFZ")
|
|
{
|
|
ii.AmountPerUnit = 0;
|
|
ii.Notice = "";
|
|
ii.ItemDescription = "Fahrzeit KA";
|
|
|
|
if (data != null && data.Count != 0)
|
|
{
|
|
ii.AmountPerUnit = data[0].AmountPerUnit;
|
|
}
|
|
else if (supportConceptApprovalPeriod.ApprovedBEPerInterval != null)
|
|
{
|
|
ii.AmountPerUnit = supportConceptApprovalPeriod.ApprovedBEPerInterval * AMOUNT_PER_KMKA;
|
|
ii.Notice = String.Format("{0:n0}", supportConceptApprovalPeriod.ApprovedBEPerInterval);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ii.ItemDescription = "Fahrzeit QA";
|
|
|
|
if (supportConceptApprovalPeriod.ApprovedBEPerInterval != null)
|
|
{
|
|
ii.AmountPerUnit = supportConceptApprovalPeriod.ApprovedBEPerInterval * AMOUNT_PER_KMQA;
|
|
ii.Notice = String.Format("{0:n0}", supportConceptApprovalPeriod.ApprovedBEPerInterval);
|
|
}
|
|
else if (data != null && data.Count != 0)
|
|
{
|
|
ii.AmountPerUnit = data[0].AmountPerUnit;
|
|
}
|
|
}
|
|
}
|
|
// Gesondert Vorgehaltene Flächen - Extrabetrag aus Leistungskategorie pro Kalendertag
|
|
else if (cat.Abbreviation != null && cat.Abbreviation.Length > 3 && cat.Abbreviation.Substring(cat.Abbreviation.Length - 3) == "GVF")
|
|
{
|
|
ii.Notice = "1";
|
|
ii.AmountPerUnit = data[0].AmountTotal;
|
|
ii.ItemDescription = "Ges. vorg. Flächen";
|
|
}
|
|
// Tagesstätten erhalten auch QA - für QAAlt mit genaunem Minutenwert pro Tag umgerechnet
|
|
else if (cat.Abbreviation.StartsWith("TS") && (cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "QA" || cat.Abbreviation.ToLower().Substring(cat.Abbreviation.Length - 5) == "qaalt"))
|
|
{
|
|
if (cat.Abbreviation.ToLower().Substring(cat.Abbreviation.Length - 5) == "qaalt")
|
|
{
|
|
LG = (decimal)Math.Round((double)supportConceptApprovalPeriod.ApprovedBEPerInterval, 0);
|
|
ii.AmountPerUnit = LG * (decimal)Math.Round((double)data[0].AmountTotal / 60, 4, MidpointRounding.AwayFromZero);
|
|
}
|
|
else
|
|
{
|
|
// der LWV hat sich von der Idee des Minutenpreises verabschiedet, er geht auf ein Vielfaches des Stundenpreises
|
|
LG = (decimal)GetLeistungsgruppe(supportConceptApprovalPeriod.ApprovedBEPerInterval, cat);
|
|
ii.AmountPerUnit = (decimal)Math.Round((double)data[0].AmountTotal * Math.Round((double)LG / 60, 2, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero);
|
|
}
|
|
ii.Notice = LG.ToString();
|
|
ii.ItemDescription = "Qualifizierte Assistenz";
|
|
}
|
|
// Kompensatorische u Qualifzierte Assistenz im BeWo in Leistungsgruppen gemäß LWV
|
|
else
|
|
{
|
|
bool istNeuLg = accountingPeriodStart.HasValue && accountingPeriodStart.Value >= KA_NEU_STICHTAG;
|
|
bool tsKaNeu = istNeuLg && cat.Abbreviation.StartsWith("TS") && cat.Abbreviation.Length >= 2 && cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "KA";
|
|
LG = (decimal)GetLeistungsgruppe(supportConceptApprovalPeriod.ApprovedBEPerInterval, cat, useGenericLg: tsKaNeu);
|
|
ii.Notice = LG.ToString();
|
|
if (cat.Abbreviation.ToLower().Substring(cat.Abbreviation.Length - 3) == "alt")
|
|
{
|
|
// Der LWV hat jetzt mehrfach die Berechnung der Minuten/Stundenpreise geändert. In Absprache mit Frau Wehrheim in 2024 Sonderl sung LWV und dann in 2025 neu entscheiden
|
|
if (start > new DateTime(2023, 12, 31) && (cb2sc.CostBearer.Organisation.Name.Contains("Landeswohlfahrtsverband Hessen") || cb2sc.CostBearer.Organisation.Name.Contains("LWV")))
|
|
{
|
|
ii.AmountPerUnit = LG * (decimal)Math.Truncate((double)data[0].AmountTotal / 60 * 10000) / 10000;
|
|
|
|
}
|
|
else
|
|
{
|
|
ii.AmountPerUnit = LG * (decimal)Math.Round((double)data[0].AmountTotal / 60, 4, MidpointRounding.AwayFromZero);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (data.Count > 0 && data[0].AmountTotal != null)
|
|
{
|
|
ii.AmountPerUnit = (decimal)Math.Round((double)data[0].AmountTotal * Math.Round((double)LG / 60, 2, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero);
|
|
}
|
|
else
|
|
{
|
|
ii.AmountPerUnit = 999;
|
|
ii.ItemDescription = "FEHLER Leistungskategorie";
|
|
}
|
|
|
|
//}
|
|
}
|
|
// Leistung
|
|
if (cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "QA" || cat.Abbreviation.Length > 4 && cat.Abbreviation.Substring(cat.Abbreviation.Length - 5) == "QAAlt")
|
|
{
|
|
ii.ItemDescription = "Qualifizierte Assistenz";
|
|
}
|
|
if (cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "KA" || cat.Abbreviation.Length > 4 && cat.Abbreviation.Substring(cat.Abbreviation.Length - 5) == "KAAlt")
|
|
{
|
|
ii.ItemDescription = "Kompensatorische Assistenz";
|
|
}
|
|
}
|
|
|
|
SetzeItemDescription(cat, ii);
|
|
|
|
ii.AmountTotal = 0;
|
|
//Rundung erfolgt auf Tagespreisebene auf 2 Nachkommastellen
|
|
if (ii.AmountPerUnit != null)
|
|
{
|
|
// Problem, LWV will QA als eine Leistung haben, intern brauchen sie den Unterschied nach TS und nach BW - das führt in einigen Fällen zu Rundungsdifferenzen
|
|
// Errechnung des summierten Preises und dann Korrektur für TS
|
|
if (cat.Abbreviation.StartsWith("TS") && (cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "QA" || cat.Abbreviation.Substring(cat.Abbreviation.Length - 5) == "QAALT") && cb2sc.ApprovalPeriodList.Any(a => a.ServiceCategory.Abbreviation.Contains("QA") && !a.ServiceCategory.Abbreviation.Contains("TS")))
|
|
{
|
|
GetKombiniertenPreisQA(serviceInvoicePeriod, supportConceptApprovalPeriod, cat, cb2sc, LG, data[0].AmountTotal, ii);
|
|
}
|
|
ii.AmountPerUnit = Math.Round((decimal)ii.AmountPerUnit, 2, MidpointRounding.AwayFromZero);
|
|
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
|
|
}
|
|
|
|
if (cb2sc.SupportConcept.CostBearer2SupportConceptList.Count > 1)
|
|
{
|
|
var eAnteil = cb2sc.SupportConcept.CostBearer2SupportConceptList.Where(s => s.CostBearer.Organisation.Name == "EIGENANTEIL").FirstOrDefault();
|
|
if (eAnteil != null)
|
|
{
|
|
var app = eAnteil.ApprovalPeriodList.Where(a => a.StartDate <= start && a.EndDate >= end).FirstOrDefault();
|
|
if (app != null && app.ServiceCategory.Name == cat.Name)
|
|
{
|
|
decimal eigenanteil = 0;
|
|
if (app.ApprovedFixedAmount != null)
|
|
{
|
|
eigenanteil = app.ApprovedFixedAmount.Value;
|
|
}
|
|
ii.UnitDescription = String.Format("{0:0.00} €", eigenanteil);
|
|
ii.AmountTotal = ii.AmountTotal - eigenanteil;
|
|
}
|
|
}
|
|
}
|
|
|
|
ii.GrossAmountTotal = ii.AmountTotal;
|
|
serviceInvoicePeriod.InvoiceItemList.Add(ii);
|
|
if (supportConceptApprovalPeriod.StartDate.HasValue)
|
|
{
|
|
var test = supportConceptApprovalPeriod.StartDate.Value.AddMonths(13);
|
|
}
|
|
|
|
// Vor 01.07.2026 wurde alle 12 Monate und letzter Monat bei Kompensatorischer Assistenz spitzabgerechnet!!!
|
|
if (accountingPeriodStart.Value < KA_NEU_STICHTAG && cat.Abbreviation.Substring(cat.Abbreviation.Length - 2) == "KA" && (supportConceptApprovalPeriod.EndDate.Value <= accountingPeriodEnd.Value.AddMonths(1)
|
|
&& supportConceptApprovalPeriod.EndDate.Value >= accountingPeriodStart.Value || supportConceptApprovalPeriod.StartDate.Value.AddMonths(11) <= accountingPeriodEnd.Value
|
|
&& supportConceptApprovalPeriod.StartDate.Value.AddMonths(11) >= accountingPeriodStart.Value))
|
|
{
|
|
var ro = LwvKASpitzabrechnungRO.Create(cb2sc.Oid.Value, end, supportConceptApprovalPeriod.SupportConceptApprovalPeriodOid, null);
|
|
foreach (var pz in ro.Preiszeitraeume)
|
|
{
|
|
var jahr = serviceInvoicePeriod.Start.Value.Year;
|
|
if (pz.Index == 1)
|
|
{
|
|
jahr = serviceInvoicePeriod.Start.Value.AddYears(-1).Year;
|
|
}
|
|
var iiKa = new InvoiceItem
|
|
{
|
|
ItemPeriodStart = serviceInvoicePeriod.Start,
|
|
ItemPeriodEnd = serviceInvoicePeriod.End,
|
|
SupportConceptApprovalPeriodOid = supportConceptApprovalPeriod.SupportConceptApprovalPeriodOid,
|
|
AccountingInterval = AccountingIntervalType.Daily,
|
|
UnitCount = pz.SummeStundenDifferenz.Value,
|
|
AmountPerUnit = pz.Stundensatz.Value,
|
|
AmountTotal = Math.Round(pz.SummeBetragDifferenz.Value, 2, MidpointRounding.AwayFromZero),
|
|
GrossAmountTotal = Math.Round(pz.SummeBetragDifferenz.Value, 2, MidpointRounding.AwayFromZero),
|
|
ItemDescription = String.Format("Differenz erbrachter Stunden KA {0}", jahr),
|
|
UnitDescription = "0,00 €"
|
|
};
|
|
SetzeItemDescription(cat, iiKa);
|
|
serviceInvoicePeriod.InvoiceItemList.Add(iiKa);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// KA im BeWo ab 01.07.2026: Abrechnung nach tatsaechlich erbrachten Stunden pro Monat. Die Monatssumme wird kaufmaennisch auf die halbe Stunde gerundet und
|
|
// mit dem Stundensatz der Leistungskategorie multipliziert
|
|
public void CreateKaBwActualHours(ServiceInvoicePeriod serviceInvoicePeriod, SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, CostBearer2SupportConcept cb2sc, List<ServiceRecordDC> serviceRecords)
|
|
{
|
|
var cat = supportConceptApprovalPeriod.ServiceCategory;
|
|
if (cat == null || cat.Abbreviation == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var calc = PluginLoader.FindClass<BS.Shared.Services.Calculations>(_SpecificID) ?? BS.Shared.Services.Calculations.GetInstance(_SpecificID);
|
|
var data = calc.GetFixedBillingData(supportConceptApprovalPeriod, serviceInvoicePeriod.Start, serviceInvoicePeriod.End);
|
|
|
|
DateTime start = accountingPeriodStart.Value;
|
|
DateTime end = accountingPeriodEnd.Value;
|
|
if (supportConceptApprovalPeriod.StartDate > start)
|
|
{
|
|
start = supportConceptApprovalPeriod.StartDate.Value;
|
|
}
|
|
if (supportConceptApprovalPeriod.EndDate < end)
|
|
{
|
|
end = supportConceptApprovalPeriod.EndDate.Value;
|
|
}
|
|
|
|
// Stundensatz = Stundenpreis der Leistungskategorie (wie in der bisherigen Pauschal-Logik)
|
|
decimal stundensatz = 0;
|
|
if (data != null && data.Count > 0 && data[0].AmountTotal.HasValue)
|
|
{
|
|
stundensatz = data[0].AmountTotal.Value * 7;
|
|
}
|
|
|
|
// Summe der tatsaechlich erbrachten KA-Minuten dieser Leistungskategorie im Abrechnungsmonat. Datensaetze werden als abgerechnet markiert, damit sie nicht mehrfach gezaehlt werden.
|
|
decimal minutenSumme = SummiereErbrachteKaMinuten(serviceRecords, r => r.ServiceDescription.Category.ServiceCategoryOid == cat.ServiceCategoryOid, serviceInvoicePeriod.Start, serviceInvoicePeriod.End, true);
|
|
|
|
// kaufmaennische Rundung der Monatssumme auf die halbe Stunde
|
|
decimal stundenSumme = minutenSumme / 60m;
|
|
decimal stundenGerundet = Math.Round(stundenSumme * 2, MidpointRounding.AwayFromZero) / 2;
|
|
|
|
var ii = new InvoiceItem
|
|
{
|
|
ItemPeriodStart = serviceInvoicePeriod.Start,
|
|
ItemPeriodEnd = serviceInvoicePeriod.End,
|
|
SupportConceptApprovalPeriodOid = supportConceptApprovalPeriod.SupportConceptApprovalPeriodOid,
|
|
AccountingInterval = AccountingIntervalType.Hourly,
|
|
UnitCount = stundenGerundet,
|
|
AmountPerUnit = Math.Round(stundensatz, 2, MidpointRounding.AwayFromZero),
|
|
ItemDescription = "Kompensatorische Assistenz",
|
|
UnitDescription = "0,00 \u20ac"
|
|
};
|
|
|
|
SetzeItemDescription(cat, ii);
|
|
|
|
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
|
|
|
|
// Eigenanteil (analog CreateFixedAmounts)
|
|
if (cb2sc.SupportConcept.CostBearer2SupportConceptList.Count > 1)
|
|
{
|
|
var eAnteil = cb2sc.SupportConcept.CostBearer2SupportConceptList.Where(s => s.CostBearer.Organisation.Name == "EIGENANTEIL").FirstOrDefault();
|
|
if (eAnteil != null)
|
|
{
|
|
var app = eAnteil.ApprovalPeriodList.Where(a => a.StartDate <= start && a.EndDate >= end).FirstOrDefault();
|
|
if (app != null && app.ServiceCategory.Name == cat.Name)
|
|
{
|
|
decimal eigenanteil = 0;
|
|
if (app.ApprovedFixedAmount != null)
|
|
{
|
|
eigenanteil = app.ApprovedFixedAmount.Value;
|
|
}
|
|
ii.UnitDescription = String.Format("{0:0.00} \u20ac", eigenanteil);
|
|
ii.AmountTotal = ii.AmountTotal - eigenanteil;
|
|
}
|
|
}
|
|
}
|
|
|
|
ii.GrossAmountTotal = ii.AmountTotal;
|
|
serviceInvoicePeriod.InvoiceItemList.Add(ii);
|
|
}
|
|
|
|
// Fahrtkosten der kompensatorischen Assistenz im BeWo ab 01.07.2026: nicht mehr tageweise pauschal, sondern tatsaechlich erbrachte KA-Minuten des Monats * Fahrtkostensatz pro Minute.
|
|
public void CreateKaBwFahrtkosten(ServiceInvoicePeriod serviceInvoicePeriod, SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, CostBearer2SupportConcept cb2sc, List<ServiceRecordDC> serviceRecords)
|
|
{
|
|
var catFz = supportConceptApprovalPeriod.ServiceCategory;
|
|
if (catFz == null || catFz.Abbreviation == null || catFz.Abbreviation.Length < 4)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Zugehoerige KA-Leistungskategorie = Fahrzeit-Abkuerzung ohne die Endung "FZ" (z.B. HTKKAFZ -> HTKKA)
|
|
string kaAbbr = catFz.Abbreviation.Substring(0, catFz.Abbreviation.Length - 2);
|
|
|
|
// Summe der tatsaechlich erbrachten KA-Minuten im Abrechnungsmonat (ohne AlreadyAccounted zu setzen, da hier nur ein abgeleiteter Wert berechnet und der Datensatz nicht "verbraucht" wird)
|
|
decimal minutenSumme = SummiereErbrachteKaMinuten(serviceRecords, r => r.ServiceDescription.Category.Abbreviation == kaAbbr, serviceInvoicePeriod.Start, serviceInvoicePeriod.End, false);
|
|
|
|
var ii = new InvoiceItem
|
|
{
|
|
ItemPeriodStart = serviceInvoicePeriod.Start,
|
|
ItemPeriodEnd = serviceInvoicePeriod.End,
|
|
SupportConceptApprovalPeriodOid = supportConceptApprovalPeriod.SupportConceptApprovalPeriodOid,
|
|
AccountingInterval = AccountingIntervalType.Hourly,
|
|
UnitCount = minutenSumme,
|
|
AmountPerUnit = AMOUNT_PER_KMKA,
|
|
Notice = "",
|
|
ItemDescription = "Fahrzeit KA",
|
|
UnitDescription = "0,00 \u20ac"
|
|
};
|
|
|
|
SetzeItemDescription(catFz, ii);
|
|
|
|
ii.AmountTotal = Math.Round(minutenSumme * AMOUNT_PER_KMKA, 2, MidpointRounding.AwayFromZero);
|
|
ii.GrossAmountTotal = ii.AmountTotal;
|
|
serviceInvoicePeriod.InvoiceItemList.Add(ii);
|
|
}
|
|
|
|
// Summiert die tatsaechlich erbrachten (abrechenbaren) Minuten der KA-Datensaetze im Zeitraum. istPassendeKaKategorie waehlt die relevanten Datensaetze aus; bei alsAbgerechnetMarkieren werden bereits
|
|
// abgerechnete Datensaetze uebersprungen und die gezaehlten als abgerechnet markiert.
|
|
private decimal SummiereErbrachteKaMinuten(List<ServiceRecordDC> serviceRecords, Func<ServiceRecordDC, bool> istPassendeKaKategorie, DateTime? von, DateTime? bis, bool alsAbgerechnetMarkieren)
|
|
{
|
|
var calc = PluginLoader.FindClass<BS.Shared.Services.Calculations>(_SpecificID) ?? BS.Shared.Services.Calculations.GetInstance(_SpecificID);
|
|
decimal minuten = 0;
|
|
foreach (var record in serviceRecords)
|
|
{
|
|
if (record.ServiceDescription == null || record.ServiceDescription.Category == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (!record.ServiceDescription.Category.IsBillable)
|
|
{
|
|
continue;
|
|
}
|
|
if (!istPassendeKaKategorie(record))
|
|
{
|
|
continue;
|
|
}
|
|
if (!record.Start.HasValue || !record.Start.Value.InBetween(von.Value, bis.Value, true))
|
|
{
|
|
continue;
|
|
}
|
|
if (alsAbgerechnetMarkieren && record.AlreadyAccounted)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
minuten += calc.GetBillableDurationInMinutes(record, true);
|
|
if (alsAbgerechnetMarkieren)
|
|
{
|
|
record.AlreadyAccounted = true;
|
|
}
|
|
}
|
|
return minuten;
|
|
}
|
|
|
|
private void SetzeItemDescription(ServiceCategoryDC cat, InvoiceItem ii)
|
|
{
|
|
// + Ort - Tagesstätte oder BeWo mit Ort - wichtig für Kostenstellen und Erlöskonten
|
|
if (cat.Abbreviation.StartsWith("TS"))
|
|
{
|
|
ii.ItemDescription += " TS HTK";
|
|
if (cat.Abbreviation.StartsWith("TSKÖ"))
|
|
{
|
|
ii.ItemDescription += " Kö";
|
|
}
|
|
else if (cat.Abbreviation.StartsWith("TSSTB"))
|
|
{
|
|
ii.ItemDescription += " Stb";
|
|
}
|
|
else if (cat.Abbreviation.StartsWith("TSOU"))
|
|
{
|
|
ii.ItemDescription += " OU";
|
|
}
|
|
}
|
|
else if (cat.Abbreviation.StartsWith("HTK"))
|
|
{
|
|
ii.ItemDescription += " ieH HTK";
|
|
}
|
|
else if (cat.Abbreviation.StartsWith("BWTr"))
|
|
{
|
|
ii.ItemDescription += " ieH Trauma";
|
|
}
|
|
else if (cat.Abbreviation.StartsWith("FFM"))
|
|
{
|
|
ii.ItemDescription += " ieH FFM";
|
|
}
|
|
else if (cat.Abbreviation.StartsWith("KB"))
|
|
{
|
|
ii.ItemDescription += " ieH KB";
|
|
}
|
|
}
|
|
|
|
private void GetKombiniertenPreisQA(ServiceInvoicePeriod period, SupportConceptApprovalPeriodDC scapTS, ServiceCategoryDC cat, CostBearer2SupportConcept cb2sc, decimal LG, decimal? stdPreis, InvoiceItem ii)
|
|
{
|
|
var qaAps = cb2sc.ApprovalPeriodList.Where(a => a.ServiceCategory.Abbreviation.Contains("QA") && !a.ServiceCategory.Abbreviation.Contains("TS")).ToList();
|
|
foreach (var ap in qaAps)
|
|
{
|
|
if (scapTS.StartDate == ap.StartDate && scapTS.EndDate == ap.EndDate || ap.StartDate < period.End && ap.EndDate > period.Start)
|
|
{
|
|
var catQA = MapperFactory.ServiceCategoryDC_ServiceCategory.MapToNewDC(ap.ServiceCategory);
|
|
var lgQA = (decimal)GetLeistungsgruppe(ap.ApprovedBEPerInterval, catQA);
|
|
if (LG == 240 && cb2sc.CustomerReferenceNumber == "206305 - 2648759")
|
|
{
|
|
LG = scapTS.ApprovedBEPerInterval.Value;
|
|
}
|
|
decimal? minPreis;
|
|
decimal? minGes;
|
|
decimal preisGes;
|
|
decimal preisQa;
|
|
|
|
if (catQA.Abbreviation.ToLower().Substring(catQA.Abbreviation.Length - 5) == "qaalt")
|
|
{
|
|
minPreis = (decimal)Math.Round((double)stdPreis / 60, 4, MidpointRounding.AwayFromZero);
|
|
if (period.Start > new DateTime(2023, 12, 31) && (cb2sc.CostBearer.Organisation.Name.Contains("Landeswohlfahrtsverband Hessen") || cb2sc.CostBearer.Organisation.Name.Contains("LWV")))
|
|
{
|
|
minPreis = (decimal)Math.Truncate((double)stdPreis / 60 * 10000) / 10000;
|
|
}
|
|
preisGes = Math.Round((decimal)((LG + lgQA) * minPreis), 2, MidpointRounding.AwayFromZero);
|
|
preisQa = Math.Round((decimal)(lgQA * minPreis), 2, MidpointRounding.AwayFromZero);
|
|
minGes = lgQA + scapTS.ApprovedBEPerInterval;
|
|
ii.Notice = String.Format("{0:n0}", minGes);
|
|
}
|
|
else
|
|
{
|
|
minGes = ap.ApprovedBEPerInterval + scapTS.ApprovedBEPerInterval;
|
|
var lgGes = (decimal)GetLeistungsgruppe(minGes, catQA);
|
|
preisGes = (decimal)Math.Round((double)stdPreis * (double)lgGes / 60, 2, MidpointRounding.AwayFromZero);
|
|
|
|
// der LWV hat sich nach 6 Monaten von der Idee des Minutenpreises verabschiedet, er geht auf ein Vielfaches des Stundenpreises
|
|
preisQa = (decimal)Math.Round((double)stdPreis * (double)lgQA / 60, 2, MidpointRounding.AwayFromZero);
|
|
ii.Notice = lgGes.ToString();
|
|
}
|
|
ii.AmountPerUnit = Math.Round((decimal)(preisGes - preisQa), 2, MidpointRounding.AwayFromZero);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private object GetLeistungsgruppe(decimal? approvedBEPerInterval, ServiceCategoryDC cat, bool useGenericLg = false)
|
|
{
|
|
// alle Leistungen werden in Stufen - sogenannten Leistungsgruppen abgerechnet -
|
|
// diese sind für Kompensatorische und Qualifizierte Assistenz sowie die Tagesstätte unterschiedlich
|
|
// leider auch nochmal für Altfälle -> diese sollten Ende 2025 durch sein und können dann gelöscht werden
|
|
decimal lg = 60;
|
|
if (!useGenericLg && cat.Abbreviation.Length > 1 && cat.Abbreviation.ToLower().Substring(cat.Abbreviation.Length - 2) == "ka")
|
|
{
|
|
if (approvedBEPerInterval == null || approvedBEPerInterval.Value < 15)
|
|
{
|
|
lg = 0;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 45)
|
|
{
|
|
lg = 30;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 75)
|
|
{
|
|
lg = 60;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 105)
|
|
{
|
|
lg = 90;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 135)
|
|
{
|
|
lg = 120;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 165)
|
|
{
|
|
lg = 150;
|
|
}
|
|
}
|
|
else if (!useGenericLg && cat.Abbreviation.StartsWith("TS"))
|
|
{
|
|
if (approvedBEPerInterval == null || approvedBEPerInterval.Value < 91)
|
|
{
|
|
lg = 60;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 151)
|
|
{
|
|
lg = 120;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 211)
|
|
{
|
|
lg = 180;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 271)
|
|
{
|
|
lg = 240;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 391)
|
|
{
|
|
lg = 330;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 511)
|
|
{
|
|
lg = 450;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 751)
|
|
{
|
|
lg = 630;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 1051)
|
|
{
|
|
lg = 900;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Die Schwellen passen nicht zur LWV-Tabelle -> da geht es um interne Minuten (TS = offizielle LWV-Werte)
|
|
if (approvedBEPerInterval == null || approvedBEPerInterval.Value < 84) // 8 - 83 min entspricht 60 bzw. LG 1
|
|
{
|
|
lg = 60;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 132) // 84 - 131 min entspricht 120 bzw. LG 2
|
|
{
|
|
lg = 120;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 183) // 132 - 182 min entspricht 180 bzw. LG 3
|
|
{
|
|
lg = 180;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 271) // 183 - 270 min entspricht 240 bzw. LG 4
|
|
{
|
|
lg = 240;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 325) // 271 - 324 min entspricht 330 bzw. LG 5
|
|
{
|
|
lg = 330;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 511) // 325 - 510 min entspricht 450 bzw. LG 6 mit Faktor 7.5
|
|
{
|
|
lg = 450;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 751) // 511 - 750 min entspricht 240 bzw. LG 7 mit Faktor 9
|
|
{
|
|
lg = 630;
|
|
}
|
|
else if (approvedBEPerInterval.Value < 1050) // 751 - 1050 min entspricht 240 bzw. LG 8 mit Faktor 15
|
|
{
|
|
lg = 900;
|
|
}
|
|
}
|
|
|
|
return lg;
|
|
}
|
|
|
|
private static decimal GetPreis(IList<Preis> allePreise, String name, DateTime datum)
|
|
{
|
|
var pausch = allePreise.Where(p => p.Name.Contains(name)).FirstOrDefault();
|
|
if (pausch != null)
|
|
{
|
|
var crlist = MapperFactory.CostRatePeriodDC_CostRatePeriod.MapToNewDCs(pausch.CostRatePeriods);
|
|
var cr = crlist.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney, datum);
|
|
if (cr != null && cr.CostRateValue.HasValue)
|
|
{
|
|
return cr.CostRateValue.Value;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public override void CheckMaxApprovedAmount(ServiceInvoice invoice, ServiceInvoicePeriod sip, SupportConceptApprovalPeriodDC scap, CostBearer2SupportConcept costBearer2SupportConcept, DateTimeSpan invoicePeriod, List<ServiceRecordDC> serviceRecords)
|
|
{
|
|
//do nothing
|
|
}
|
|
|
|
public override void SetServiceInvoicePeriodAmounts(ServiceInvoice invoice)
|
|
{
|
|
foreach (ServiceInvoicePeriod iPeriod in invoice.ServiceInvoicePeriodList)
|
|
{
|
|
//Dictionary<decimal, decimal> rate2UnitCount = new Dictionary<decimal, decimal>();
|
|
|
|
iPeriod.Claim = 0;
|
|
foreach (InvoiceItem item in iPeriod.InvoiceItemList)
|
|
{
|
|
if (item.GrossAmountTotal.HasValue)
|
|
{
|
|
iPeriod.Claim += item.GrossAmountTotal;
|
|
}
|
|
}
|
|
iPeriod.Claim = Math.Round(iPeriod.Claim.Value, 2, MidpointRounding.AwayFromZero);
|
|
}
|
|
}
|
|
}
|
|
} |