Files
BeWoPlaner/ReportImp/PerspektivenEV/Invoicing/CollectiveInvoiceCreationBthg.cs

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);
}
}
}
}