Files
BeWoPlaner/ReportImp/VereinBeratungTherapie/Invoicing/CollectiveInvoiceCreation.cs

463 lines
24 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 VereinBeratungTherapie.Invoicing
{
public class CollectiveInvoiceCreation : InvoiceCreation
{
// Beginn April 2024 - Umstellung nach BTHG
private DateTime? accountingPeriodStart = null;
private DateTime? accountingPeriodEnd = null;
private decimal AMOUNT_PER_KMQA = 0.0231m;
private decimal AMOUNT_PER_KMKA = 0.0108m;
public override void SetInvoiceId(ServiceInvoice invoice)
{
invoice.InvoiceBase.InvoiceId = "Sammelrechnung";
//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;
Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> alleHPs = 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
alleHPs.Add(sc, supportConcepts2ServiceRecords[sc]);
}
invoice = CreateCollectiveBilling(costBearer, invoicePeriod, alleHPs);
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
var preise = DAOFactory.GenericDAO.GetAllActive<Preis>();
var pauschVar = GetPreis(preise, "Fahrtkostenwert QA", (DateTime)accountingPeriodStart);
if (pauschVar != 0)
{
AMOUNT_PER_KMQA = pauschVar;
}
pauschVar = 0;
pauschVar = GetPreis(preise, "Fahrtkostenwert KA", (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);
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 lgMin = 1;
decimal LG = 1;
var cat = supportConceptApprovalPeriod.ServiceCategory;
if (cat != null)
{
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 €",
Notice = String.Format("{0:n0}", supportConceptApprovalPeriod.ApprovedBEPerInterval),
ItemDescription = cat.Name,
};
if (supportConceptApprovalPeriod.ServiceCategory != null)
{
if(serviceInvoicePeriod.Customer.Person.LastNameFirstName.Contains("Kaisinger"))
{
//do nothing
}
// Gesondert Vorgehaltene Flächen - Extrabetrag aus Leistungskategorie pro Kalendertag
if (cat.Abbreviation == "GVF" && data != null && data.Count > 0)
{
ii.Notice = "1";
ii.AmountPerUnit = data[0].AmountTotal;
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
ii.ItemDescription = "Ges. vorg. Flächen";
}
// Fahrtzeiten - Bewilligte Minuten * Fahrtkostenpauschale
else if (supportConceptApprovalPeriod.ApprovedBEPerInterval != null && cat.Abbreviation != null && cat.Abbreviation.StartsWith("FZ") || (supportConceptApprovalPeriod.ServiceCategory.Name != null && supportConceptApprovalPeriod.ServiceCategory.Name.Contains("Fahrtzeit")))
{
if (cat.Abbreviation.Contains("KA") || supportConceptApprovalPeriod.ServiceCategory.Name.Contains("KA"))
{
ii.AmountPerUnit = Math.Round((decimal)(supportConceptApprovalPeriod.ApprovedBEPerInterval * AMOUNT_PER_KMKA), 2, MidpointRounding.AwayFromZero);
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
}
else
{
ii.AmountPerUnit = Math.Round((decimal)(supportConceptApprovalPeriod.ApprovedBEPerInterval * AMOUNT_PER_KMQA), 2, MidpointRounding.AwayFromZero);
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
}
}
else
{
if (supportConceptApprovalPeriod.ApprovedBEPerInterval.HasValue)
{
decimal preis = 0;
decimal preisLG = 0;
if (supportConceptApprovalPeriod.ApprovedBEPerInterval.HasValue)
{
lgMin = supportConceptApprovalPeriod.ApprovedBEPerInterval.Value;
}
if (data != null && data.Count > 0)
{
// LWV rundet den Preis bei KA und alten Bewilligungen QA auf Minutenwert und bei neuer QA auf Stunden :-P
if (cat.Abbreviation.Contains("QA") || cat.Name.Contains("Qualifizierte"))
//&& new List<decimal> { 60, 120, 180, 240, 330, 450, 630, 900 }.Contains(LG))
{
if (supportConceptApprovalPeriod.Notice != null && supportConceptApprovalPeriod.Notice.ToLower().Contains("alt"))
{
LG = lgMin;
}
if (supportConceptApprovalPeriod.Notice != null && supportConceptApprovalPeriod.Notice.ToLower().Contains("kombiniert"))
{
LG = lgMin;
if (!cat.Abbreviation.Contains("TS"))
{
LG = GetKombinierteLG(serviceInvoicePeriod, supportConceptApprovalPeriod, cat, cb2sc, lgMin, preisLG);
}
}
else
{
LG = GetLeistungsgruppe(lgMin, cat);
}
ii.Notice = String.Format("{0:n0}", LG);
LG = Math.Round(LG / 60, 2, MidpointRounding.AwayFromZero);
if (data != null && data.Count > 0 && supportConceptApprovalPeriod.ApprovedBEPerInterval != null)
{
preisLG = data[0].AmountTotal.Value;
preis = preisLG;
}
ii.AmountPerUnit = Math.Round(preis * LG, 2);
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
}
else
{
// KA wird auf nächste halbe Stunde gerundet...
lgMin = Math.Round(lgMin / 30) * 30;
preisLG = data[0].AmountTotal.Value;
//preis = Math.Round(preisLG / 60, 4, MidpointRounding.AwayFromZero);
//ii.AmountPerUnit = Math.Round(preis * lgMin, 2, MidpointRounding.AwayFromZero);
ii.AmountPerUnit = Math.Round(lgMin / 60 * preisLG, 2, MidpointRounding.AwayFromZero);
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
}
}
// 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 - Auf 4 Nachkommstellen gerundeter Preis mit summe der Min
if (cat.Abbreviation.Contains("TS")
&& cb2sc.ApprovalPeriodList.Any(a => a.ServiceCategory.Abbreviation.Contains(cat.Abbreviation.Substring(0, 2)) && !a.ServiceCategory.Abbreviation.Contains("TS")))
{
GetKombiniertenPreisQA(serviceInvoicePeriod, supportConceptApprovalPeriod, cat, cb2sc, lgMin, LG, preisLG, ii);
}
}
}
}
ii.GrossAmountTotal = ii.AmountTotal;
serviceInvoicePeriod.InvoiceItemList.Add(ii);
}
}
private void GetKombiniertenPreisQA(ServiceInvoicePeriod period, SupportConceptApprovalPeriodDC scapTS, ServiceCategoryDC cat, CostBearer2SupportConcept cb2sc, decimal lgMin, decimal LG, decimal? preis, InvoiceItem ii)
{
// BeckerA, 26.05.2025: Anpassung in Umrechnung der LG, ABER das war nur für QA nötig, allerdings scheint es noch Altfälle zu gebenm die ohnehin Minutenweise umgerechnet wurden
// z.B. Babadagi
// BeckerA, 26.05.2025: Programmierung soweit angepasst, GetKombiniertenPreis fehlt noch, warte da auf ihre Rückmeldung
// Errechnung des summierten Preises und dann Korrektur für TS
var scapBws = cb2sc.ApprovalPeriodList.Where(a => a.ServiceCategory != null && a.ServiceCategory.Abbreviation.Contains(cat.Abbreviation.Substring(0, 2))
&& a.ServiceCategory.Abbreviation.Substring(2, 2) == "BW").ToList();
if (scapBws != null)
{
foreach (var ap in scapBws)
{
if (scapTS.StartDate == ap.StartDate && scapTS.EndDate == ap.EndDate || ap.StartDate < period.End && ap.EndDate > period.Start)
{
var catBW = MapperFactory.ServiceCategoryDC_ServiceCategory.MapToNewDC(ap.ServiceCategory);
decimal minPreis = Math.Round(preis.Value / 60, 4, MidpointRounding.AwayFromZero);
decimal preisGes;
decimal preisBw = 0;
decimal lgBw = 0;
decimal lgGes = 0;
if (ap.ApprovedBEPerInterval != null)
{
lgBw = ap.ApprovedBEPerInterval.Value;
}
// LWV rundet den Prei bei KA und alten Bewilligungen QA auf Minutenwert und bei neuer QA auf Stunden :-P
if (cat.Abbreviation.Contains("QA") || cat.Name.Contains("Qualifizierte"))
{
if (ap.Notice == null || !ap.Notice.ToLower().Contains("alt"))
{
lgGes = GetLeistungsgruppe(lgMin + lgBw, cat);
var dif = lgGes - lgMin - lgBw;
if (dif != 0)
{
dif /= 2;
}
lgMin += dif;
lgBw += dif;
ii.Notice = Math.Round((lgGes - Math.Round(lgBw, 0, MidpointRounding.AwayFromZero)),0, MidpointRounding.ToEven).ToString();
}
else
{
lgGes = lgMin + lgBw;
preisGes = Math.Round(minPreis * lgGes, 2, MidpointRounding.AwayFromZero);
}
lgBw = Math.Round(lgBw / 60, 2, MidpointRounding.AwayFromZero);
preisBw = Math.Round(preis.Value * lgBw, 2, MidpointRounding.AwayFromZero);
if (new List<decimal> { 60, 120, 180, 240, 330, 450, 630, 900 }.Contains(lgGes))
{
preisGes = Math.Round(preis.Value * lgGes / 60, 2, MidpointRounding.AwayFromZero);
}
else
{
preisGes = Math.Round(minPreis * lgGes, 2, MidpointRounding.AwayFromZero);
}
ii.AmountPerUnit = preisGes - preisBw;
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
break;
}
else
{
preisBw = Math.Round(minPreis * lgBw, 2, MidpointRounding.AwayFromZero);
preisGes = Math.Round(minPreis * (LG + lgBw), 2, MidpointRounding.AwayFromZero);
ii.AmountPerUnit = preisGes - preisBw;
ii.AmountTotal = ii.UnitCount * ii.AmountPerUnit;
break;
}
}
}
}
}
private decimal GetKombinierteLG(ServiceInvoicePeriod period, SupportConceptApprovalPeriodDC scapBW, ServiceCategoryDC cat, CostBearer2SupportConcept cb2sc, decimal lgMin, decimal? preis)
{
// BeckerA, 26.05.2025: Anpassung in Umrechnung der LG, ABER das war nur für QA nötig, allerdings scheint es noch Altfälle zu gebenm die ohnehin Minutenweise umgerechnet wurden
// z.B. Babadagi
// BeckerA, 26.05.2025: Programmierung soweit angepasst, GetKombiniertenPreis fehlt noch, warte da auf ihre Rückmeldung
// Errechnung des summierten Preises und Einzelpreise TS und BW
var scapTSs = cb2sc.ApprovalPeriodList.Where(a => a.ServiceCategory != null && a.ServiceCategory.Abbreviation.Contains(cat.Abbreviation.Substring(0, 2))
&& a.ServiceCategory.Abbreviation.Substring(2, 2) == "TS").ToList();
if (scapTSs != null)
{
foreach (var ap in scapTSs)
{
if (scapBW.StartDate == ap.StartDate && scapBW.EndDate == ap.EndDate || ap.StartDate < period.End && ap.EndDate > period.Start)
{
var catTs = MapperFactory.ServiceCategoryDC_ServiceCategory.MapToNewDC(ap.ServiceCategory);
decimal minPreis = Math.Round(preis.Value / 60, 4, MidpointRounding.AwayFromZero);
decimal lgTs = 0;
decimal lgGes = 0;
if (ap.ApprovedBEPerInterval != null)
{
lgTs = ap.ApprovedBEPerInterval.Value;
}
// LWV rundet den Prei bei KA und alten Bewilligungen QA auf Minutenwert und bei neuer QA auf Stunden :-P
if (cat.Abbreviation.Contains("QA") || cat.Name.Contains("Qualifizierte"))
{
if (ap.Notice == null || !ap.Notice.ToLower().Contains("alt"))
{
lgGes = GetLeistungsgruppe(lgMin + lgTs, cat);
var dif = lgGes - lgMin - lgTs;
if (dif != 0)
{
dif /= 2;
}
lgMin += dif;
lgTs += dif;
}
else
{
lgGes = lgMin + lgTs;
}
}
return lgMin;
}
}
}
return lgMin;
}
private decimal GetLeistungsgruppe(decimal? approvedBEPerInterval, ServiceCategoryDC cat)
{
// Qualifizierte Assistenz wird in Stufen - sogenannten Leistungsgruppen abgerechnet -
decimal lg = 60;
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;
}
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)
{
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);
}
}
}
}