MH: VSDHammReports CustomSettlementInvoiceCreation Claude Vorbereitung

This commit is contained in:
2026-06-23 14:28:25 +02:00
parent 7f68983d47
commit 9ec68c3161

View File

@@ -1,14 +1,18 @@
using BeWo.Data.Access;
using BeWo.Data.Entities;
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.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static BeWo.Report.ReportObjects.SettlementRO;
using BS.Shared.Extensions;
namespace VSDHammReports
{
@@ -22,11 +26,7 @@ namespace VSDHammReports
public override bool SchneideBeiErreichenDesBudgetsAb { get { return true; } }
public override void CreateInvoiceDetailsTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s)
{
base.CreateInvoiceDetailsTheOldWay(si, c2s);
}
public override List<InvoiceItemDC> CreateInvoiceItems(Settlement2DC si, IList<ApprovalPeriod> apList, bool schneideBeiMaxAb)
{
//var items = base.CreateInvoiceItems(si, apList, true);
@@ -127,7 +127,237 @@ namespace VSDHammReports
}
public override void CreateInvoiceDetailsTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s)
{
String id = c2s.CostBearer.ID;
var calc = PluginLoader.FindClass<Calculations>(id);
if (calc == null)
{
calc = Calculations.GetInstance(id);
}
var relDc = c2s.MapToNewDC();
var apListFehlkontakt = new List<ApprovalPeriod>();
var apListFLS = CreateApprovalPeriods(si, c2s);
foreach (var ap in apListFLS)
{
var fkAp = new ApprovalPeriod();
fkAp.PeriodStartDate = ap.PeriodStartDate;
fkAp.PeriodEndDate = ap.PeriodEndDate;
fkAp.AmountPerUnit = ap.AmountPerUnit;
fkAp.BillingInterval = ap.BillingInterval;
fkAp.RateFactor = 0;
fkAp.Notice = "Fehlkontakte";
apListFehlkontakt.Add(fkAp);
}
si.ApprovedFLS = CalculateApprovedTotalFLS(apListFLS);
DateTime periodStart = si.AccountingPeriodStart.Value.Date;
DateTime periodEnd = si.AccountingPeriodEnd.Value.Date;
//Prüfe ob vorher abgemeldet
if (c2s.SupportConcept.Customer.TerminationDate.HasValue &&
c2s.SupportConcept.Customer.TerminationDate.Value < periodEnd)
{
periodEnd = c2s.SupportConcept.Customer.TerminationDate.Value;
si.ErrorNotice =
String.Format(
"Achtung: Die Betreuung endete am {0:dd.MM.yyyy}. Alle nachträglich eingegebenen Stunden werden nicht abgerechnet!",
periodEnd);
}
var hourlyRatesInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate, periodStart, periodEnd).ToList();
si.DifferentHourlyRateCount = hourlyRatesInSpanList.Count;
if (si.DifferentHourlyRateCount == 0)
{
si.DifferentHourlyRateCount = 1;
}
var rateFactorsInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, periodStart, periodEnd).ToList();
if (rateFactorsInSpanList.Count > 0)
{
var cdc = rateFactorsInSpanList.Last().Key;
if (cdc.CostRateValue.HasValue)
si.RateFactor = cdc.CostRateValue.Value;
}
decimal rf = GetRateFactor(si);
var records = c2s.ServiceRecords;
records = records.Where(i => i.Start.HasValue && i.Start.Value.InBetween(periodStart, periodEnd, true)).ToList();
var recordDCS = records.MapToNewDCs();
var absenceTimes = MapperFactory.AbsenceTimeDC_AbsenceTime.MapToNewDCs(c2s.SupportConcept.Customer.AbsenceTimes);
Dictionary<DateTimeSpan, decimal> span2Hours = calc.GetAbsencesTimesNotBillable(absenceTimes);
si.RecordedBillableFLSNotBillableCustomerAbsent
= calc.GetHoursNotBillableDueToAbsenceTimes(relDc, recordDCS,
span2Hours, false, false);
si.RecordedBillableFLSNotBillableOutOfApprovedSpan = calc.GetHoursNotBillableDueToOutOfSpan(relDc, recordDCS);
//calc.GetHoursNotBillableDueToMoreThanApproved(relDC, recordDCS, false, false);
Dictionary<long, bool> groupBookingDict = new Dictionary<long, bool>();
//decimal flmCleared = 0;
records = records.OrderBy<ServiceRecord, DateTime>(sr => sr.Start.HasValue ? sr.Start.Value : DateTime.MinValue).ToList<ServiceRecord>();
decimal maxApprovedMinutes = (si.ApprovedFLS ?? 0) * 60;
decimal totalMinutesGeleistet = 0;
foreach (var iRecord in records)
{
if (iRecord.ServiceDescription.ServiceCategory.IsBillable)
{
if (iRecord.GroupOid == null || !groupBookingDict.ContainsKey(iRecord.GroupOid.Value))
{
decimal dauerAbrechenbar = iRecord.RoundedDuration;
decimal dauerBudget = iRecord.RoundedDuration;
if (ReduziereBetragBeiAbwesenheiten())
{
//Die Liste recordDCS enthält nach Aufruf von GetHoursNotBillableDueToAbsenceTimes nur noch die Einträge, die auch während Abwesenheiten abgerechnet werden dürfen
var dcRecord = recordDCS.Where(r => r.ServiceRecordOid == iRecord.Oid).FirstOrDefault();
if (dcRecord == null)
{
dauerAbrechenbar = 0;
dauerBudget = 0;
}
else
{
dauerAbrechenbar = dcRecord.RoundedDuration;
dauerBudget = dcRecord.RoundedDuration;
}
}
//var prozent = iRecord.ServiceDescription.ServiceCategory.ProzentAbrechnung;
//if (iRecord.ServiceDescription.ProzentAbrechnung.HasValue)
//{
// prozent = iRecord.ServiceDescription.ProzentAbrechnung.Value;
//}
//dauerAbrechenbar *= prozent / 100;
var prozent = iRecord.ServiceDescription.ServiceCategory.ProzentBudget ?? 100;
if (iRecord.ServiceDescription.ProzentBudget.HasValue)
{
prozent = iRecord.ServiceDescription.ProzentBudget.Value;
}
dauerBudget *= prozent / 100;
var apList = apListFLS;
if (iRecord.ServiceDescription.Name.ToLower().Contains("fehlkontakt") || (iRecord.ServiceDescription.ProzentAbrechnung.HasValue && iRecord.ServiceDescription.ProzentAbrechnung.Value < 100))
{
apList = apListFehlkontakt;
}
var ap = apList.FirstOrDefault(
iap =>
{
return iap.PeriodStartDate <= iRecord.Start &&
(iap.PeriodEndDate >= DateTime.MaxValue.AddDays(-1) || iRecord.Start < iap.PeriodEndDate.AddDays(1));
});
if (ap != null)
{
//Christian wars:-)
var dauerBudgetMitRf = dauerBudget * ((ap.RateFactor ?? 0) + 100) / 100;
if (SchneideBeiErreichenDesBudgetsAb)
{
if (totalMinutesGeleistet + dauerBudgetMitRf > maxApprovedMinutes)
{
dauerBudget = maxApprovedMinutes - totalMinutesGeleistet;
dauerBudget /= ((ap.RateFactor ?? 0) + 100) / 100;
if (dauerBudget < 0)
{
dauerBudget = 0;
}
if (dauerAbrechenbar > dauerBudget)
{
dauerAbrechenbar = dauerBudget;
}
}
}
totalMinutesGeleistet += dauerBudgetMitRf;
ap.RecordedMinutes += dauerAbrechenbar;
ap.BudgetMinutes += dauerBudget;
}
if (iRecord.GroupOid != null && !groupBookingDict.ContainsKey(iRecord.GroupOid.Value))
{
si.HasGroupRecords = true;
groupBookingDict.Add(iRecord.GroupOid.Value, true);
}
}
}
}
si.RecordedMinutesTotal = totalMinutesGeleistet;
//decimal? maxAmount = CalculateMaxAmount(si, apList);
var gesamtListe = new List<ApprovalPeriod>();
gesamtListe.AddRange(apListFLS.Where(a => a.RecordedMinutes > 0));
gesamtListe.AddRange(apListFehlkontakt.Where(a => a.RecordedMinutes > 0));
si.InvoiceItems = CreateInvoiceItems(si, gesamtListe);
decimal totalAmount = 0;
decimal rateFactorAmount = 0;
foreach (var ii in si.InvoiceItems)
{
decimal grossAmount = ii.GrossAmountTotal ?? 0;
totalAmount += grossAmount;
if (ii.RateFactor.HasValue && ii.RateFactor.Value > 0)
{
rateFactorAmount += (grossAmount - (grossAmount / ((100 + ii.RateFactor.Value) / 100)));
}
}
if (!si.RecordedBillableFLSBrutto.HasValue)
{
si.RecordedBillableFLSNetto = si.InvoiceItems.Sum(ii => (ii.UnitCount ?? 0)); // * rf; //eigentlich falsch dass netto = brutto ist.
si.RecordedBillableFLSBrutto = si.RecordedBillableFLSNetto;
}
si.AmountBillableTotal = totalAmount;
si.AmountWithRateFactor = rateFactorAmount;
//AdvancedPayments
CalculateAdvancedPayments(si, c2s, null, null, hourlyRatesInSpanList);
//EquityContribution (Eigenanteil)
CalculateEquityContribution(si, c2s.SupportConcept, null, null);
si.Claim = si.AmountBillableTotal - (si.AmountAdvancedPayments ?? 0) - si.EquityContribution;
}
private decimal? CalculateApprovedTotalFLS(IList<ApprovalPeriod> apList)
{
decimal flsTotal = 0;
foreach (var ap in apList)
{
flsTotal += ap.ApprovedTotal;
}
return flsTotal;
}
private decimal GetRateFactor(Settlement2DC si)
{
decimal rf = 1;
if (si.RateFactor.HasValue && si.RateFactor.Value != 0)
{
rf = si.RateFactor.Value;
rf = (100 + rf) / 100;
}
return rf;
}
// Abwesenheitsstunden werden hier auch in Berechnung ausgeklammert, Calc auch anpassen
public override bool ReduziereBetragBeiAbwesenheiten()
{
return true;