MH: VSD Hamm Sammelrechnung Kappung

This commit is contained in:
2026-07-21 08:16:47 +02:00
parent 3418d56dce
commit bb0a227a00
2 changed files with 113 additions and 86 deletions

View File

@@ -1,4 +1,11 @@
using BeWo.Service.Invoicing;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BeWo.Service.Invoicing;
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.Extensions;
using BS.Shared.Services;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,6 +16,107 @@ namespace VSDHammReports.Invoicing
{
public class CustomInvoiceCreation : InvoiceCreation
{
// Claude: Kappung analog zur CustomSettlementInvoiceCreation:
// Bei Bewilligungen mit Intervall Weekly/Monthly wird nicht auf Gesamt-Ebene gekappt,
// sondern je Kalenderwoche (Mo-So) bzw. Kalendermonat gegen den bewilligten Wert pro Intervall.
// Der RateFactor wird beim Budgetverbrauch mitgerechnet.
// Claude: Kappt die Einzel-Items (eine Position je Leistung) vor der Zusammenfassung,
// da nur hier noch die Wochen-/Monatszuordnung über ItemPeriodStart möglich ist.
public override IList<InvoiceItem> CreateSummaryInvoiceItems(IList<InvoiceItem> itemList, DateTimeSpan invoicePeriod, SupportConceptApprovalPeriod scap, Calculations calc)
{
KappeBeiUeberschreitungDesIntervallBudgets(itemList, scap, calc);
return base.CreateSummaryInvoiceItems(itemList, invoicePeriod, scap, calc, false, true);
}
// Claude: Die Gesamt-Kappung der Basisklasse entfällt bei Weekly/Monthly,
// weil dort bereits je Intervall gekappt wurde (siehe CreateSummaryInvoiceItems).
public override void CheckMaxApprovedAmount(ServiceInvoice invoice, ServiceInvoicePeriod sip, SupportConceptApprovalPeriodDC scap, CostBearer2SupportConcept costBearer2SupportConcept, DateTimeSpan invoicePeriod, List<ServiceRecordDC> serviceRecords)
{
if (IstIntervallKappung(scap.ApprovedBEInterval, scap.ApprovedBEPerInterval, scap.IsApprovedBEShifting))
{
return;
}
base.CheckMaxApprovedAmount(invoice, sip, scap, costBearer2SupportConcept, invoicePeriod, serviceRecords);
}
private bool IstIntervallKappung(SupportConceptApprovalInterval? approvedBEInterval, decimal? approvedBEPerInterval, bool isApprovedBEShifting)
{
return !isApprovedBEShifting &&
approvedBEInterval.HasValue && approvedBEPerInterval.HasValue &&
(approvedBEInterval.Value == SupportConceptApprovalInterval.Weekly ||
approvedBEInterval.Value == SupportConceptApprovalInterval.Monthly);
}
private void KappeBeiUeberschreitungDesIntervallBudgets(IList<InvoiceItem> itemList, SupportConceptApprovalPeriod scap, Calculations calc)
{
if (scap == null || itemList == null || itemList.Count == 0)
{
return;
}
var scapDC = MapperFactory.SupportConceptApprovalPeriodDC_SupportConceptApprovalPeriod.MapToNewDC(scap);
if (!IstIntervallKappung(scapDC.ApprovedBEInterval, scapDC.ApprovedBEPerInterval, scapDC.IsApprovedBEShifting))
{
return;
}
var interval = scapDC.ApprovedBEInterval.Value;
var costRates = MapperFactory.CostRatePeriodDC_CostRatePeriod.MapToNewDCs(scap.CostBearer2SupportConcept.CostBearer.CostRatePeriods);
// Claude: Je Intervall (Wochenmontag bzw. Monatserster) die verbrauchten Stunden (inkl. RF) sammeln.
var intervalStundenGeleistet = new Dictionary<DateTime, decimal>();
var intervalMaxStunden = new Dictionary<DateTime, decimal>();
foreach (var ii in itemList.Where(i => i.UnitCount.HasValue && i.ItemPeriodStart.HasValue).OrderBy(i => i.ItemPeriodStart.Value))
{
DateTime intervalKey = interval == SupportConceptApprovalInterval.Weekly
? ii.ItemPeriodStart.Value.GetLast(DayOfWeek.Monday).Date
: ii.ItemPeriodStart.Value.GetFirstOfMonth();
if (!intervalMaxStunden.ContainsKey(intervalKey))
{
// Claude: Bewilligte Stunden für das volle Intervall über dieselbe Berechnung
// ermitteln, mit der die Basisklasse auch ApprovedHours bestimmt (inkl. BE-Umrechnung).
DateTime intervalEnde = interval == SupportConceptApprovalInterval.Weekly
? intervalKey.AddDays(6)
: intervalKey.AddMonths(1).AddDays(-1);
intervalMaxStunden[intervalKey] = calc.GetApprovedHoursForPeriod(scapDC, costRates, intervalKey, intervalEnde) ?? 0;
}
decimal maxStunden = intervalMaxStunden[intervalKey];
decimal stundenGeleistet = intervalStundenGeleistet.ContainsKey(intervalKey)
? intervalStundenGeleistet[intervalKey]
: 0;
decimal rfFaktor = ((ii.RateFactor ?? 0) + 100) / 100;
decimal stundenMitRf = ii.UnitCount.Value * rfFaktor;
if (stundenGeleistet + stundenMitRf > maxStunden)
{
decimal restStunden = (maxStunden - stundenGeleistet) / rfFaktor;
if (restStunden < 0)
{
restStunden = 0;
}
if (ii.UnitCount.Value > restStunden)
{
ii.UnitCount = restStunden;
// Claude: Beträge nachziehen; die Summenbildung in BerechneSummaryItemsSummen
// rechnet ohnehin nur mit UnitCount weiter.
ii.AmountTotal = (ii.AmountPerUnit ?? 0) * ii.UnitCount;
ii.GrossAmountTotal = ii.AmountTotal;
if (ii.RateFactor.HasValue)
{
ii.GrossAmountTotal *= (ii.RateFactor.Value + 100) / 100;
}
}
}
intervalStundenGeleistet[intervalKey] = stundenGeleistet + stundenMitRf;
}
}
}
}

View File

@@ -106,8 +106,6 @@ namespace VSDHammReports
this.xrTableCell37 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell35 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell33 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell36 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell38 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell34 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrLabel11 = new DevExpress.XtraReports.UI.XRLabel();
this.xrTable3 = new DevExpress.XtraReports.UI.XRTable();
@@ -129,8 +127,6 @@ namespace VSDHammReports
this.xrTableCell42 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell43 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell44 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell45 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell46 = new DevExpress.XtraReports.UI.XRTableCell();
this.xrTableCell47 = new DevExpress.XtraReports.UI.XRTableCell();
this.bindingSource1 = new System.Windows.Forms.BindingSource(this.components);
this.ReportFooter = new DevExpress.XtraReports.UI.ReportFooterBand();
@@ -862,16 +858,16 @@ namespace VSDHammReports
this.xrLabel11,
this.xrTable3,
this.xrLabel10});
this.Detail1.HeightF = 264F;
this.Detail1.HeightF = 264.0001F;
this.Detail1.Name = "Detail1";
//
// xrTable5
//
this.xrTable5.LocationFloat = new DevExpress.Utils.PointFloat(0F, 239F);
this.xrTable5.LocationFloat = new DevExpress.Utils.PointFloat(0F, 239.0001F);
this.xrTable5.Name = "xrTable5";
this.xrTable5.Rows.AddRange(new DevExpress.XtraReports.UI.XRTableRow[] {
this.xrTableRow23});
this.xrTable5.SizeF = new System.Drawing.SizeF(677F, 25F);
this.xrTable5.SizeF = new System.Drawing.SizeF(650.3333F, 25F);
//
// xrTableRow23
//
@@ -880,8 +876,6 @@ namespace VSDHammReports
this.xrTableCell37,
this.xrTableCell35,
this.xrTableCell33,
this.xrTableCell36,
this.xrTableCell38,
this.xrTableCell34});
this.xrTableRow23.Name = "xrTableRow23";
this.xrTableRow23.Weight = 1D;
@@ -958,42 +952,6 @@ namespace VSDHammReports
this.xrTableCell33.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleRight;
this.xrTableCell33.Weight = 0.2893277257020575D;
//
// xrTableCell36
//
this.xrTableCell36.BackColor = System.Drawing.Color.Gainsboro;
this.xrTableCell36.BorderColor = System.Drawing.Color.DarkGray;
this.xrTableCell36.Borders = ((DevExpress.XtraPrinting.BorderSide)((((DevExpress.XtraPrinting.BorderSide.Left | DevExpress.XtraPrinting.BorderSide.Top)
| DevExpress.XtraPrinting.BorderSide.Right)
| DevExpress.XtraPrinting.BorderSide.Bottom)));
this.xrTableCell36.Name = "xrTableCell36";
this.xrTableCell36.Padding = new DevExpress.XtraPrinting.PaddingInfo(2, 2, 2, 2, 100F);
this.xrTableCell36.StylePriority.UseBackColor = false;
this.xrTableCell36.StylePriority.UseBorderColor = false;
this.xrTableCell36.StylePriority.UseBorders = false;
this.xrTableCell36.StylePriority.UsePadding = false;
this.xrTableCell36.StylePriority.UseTextAlignment = false;
this.xrTableCell36.Text = "Betrag";
this.xrTableCell36.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleRight;
this.xrTableCell36.Weight = 0.26310971709438763D;
//
// xrTableCell38
//
this.xrTableCell38.BackColor = System.Drawing.Color.Gainsboro;
this.xrTableCell38.BorderColor = System.Drawing.Color.DarkGray;
this.xrTableCell38.Borders = ((DevExpress.XtraPrinting.BorderSide)((((DevExpress.XtraPrinting.BorderSide.Left | DevExpress.XtraPrinting.BorderSide.Top)
| DevExpress.XtraPrinting.BorderSide.Right)
| DevExpress.XtraPrinting.BorderSide.Bottom)));
this.xrTableCell38.Name = "xrTableCell38";
this.xrTableCell38.Padding = new DevExpress.XtraPrinting.PaddingInfo(2, 2, 2, 2, 100F);
this.xrTableCell38.StylePriority.UseBackColor = false;
this.xrTableCell38.StylePriority.UseBorderColor = false;
this.xrTableCell38.StylePriority.UseBorders = false;
this.xrTableCell38.StylePriority.UsePadding = false;
this.xrTableCell38.StylePriority.UseTextAlignment = false;
this.xrTableCell38.Text = "pausch Fakt.";
this.xrTableCell38.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleRight;
this.xrTableCell38.Weight = 0.42042083402151886D;
//
// xrTableCell34
//
this.xrTableCell34.BackColor = System.Drawing.Color.Gainsboro;
@@ -1128,7 +1086,7 @@ namespace VSDHammReports
this.xrTable6.Name = "xrTable6";
this.xrTable6.Rows.AddRange(new DevExpress.XtraReports.UI.XRTableRow[] {
this.xrTableRow25});
this.xrTable6.SizeF = new System.Drawing.SizeF(677F, 25F);
this.xrTable6.SizeF = new System.Drawing.SizeF(650.3333F, 25F);
this.xrTable6.StylePriority.UseFont = false;
//
// xrTableRow25
@@ -1138,8 +1096,6 @@ namespace VSDHammReports
this.xrTableCell42,
this.xrTableCell43,
this.xrTableCell44,
this.xrTableCell45,
this.xrTableCell46,
this.xrTableCell47});
this.xrTableRow25.Name = "xrTableRow25";
this.xrTableRow25.Weight = 1D;
@@ -1210,39 +1166,6 @@ namespace VSDHammReports
this.xrTableCell44.TextFormatString = "{0:0.00}";
this.xrTableCell44.Weight = 0.2893277257020575D;
//
// xrTableCell45
//
this.xrTableCell45.BorderColor = System.Drawing.Color.DarkGray;
this.xrTableCell45.Borders = ((DevExpress.XtraPrinting.BorderSide)(((DevExpress.XtraPrinting.BorderSide.Left | DevExpress.XtraPrinting.BorderSide.Right)
| DevExpress.XtraPrinting.BorderSide.Bottom)));
this.xrTableCell45.DataBindings.AddRange(new DevExpress.XtraReports.UI.XRBinding[] {
new DevExpress.XtraReports.UI.XRBinding("Text", null, "ServiceInvoicePeriods.ServiceInvoiceItems.NetAmount")});
this.xrTableCell45.Name = "xrTableCell45";
this.xrTableCell45.Padding = new DevExpress.XtraPrinting.PaddingInfo(2, 2, 2, 2, 100F);
this.xrTableCell45.StylePriority.UseBorderColor = false;
this.xrTableCell45.StylePriority.UseBorders = false;
this.xrTableCell45.StylePriority.UsePadding = false;
this.xrTableCell45.StylePriority.UseTextAlignment = false;
this.xrTableCell45.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleRight;
this.xrTableCell45.TextFormatString = "{0:c}";
this.xrTableCell45.Weight = 0.26310971709438763D;
//
// xrTableCell46
//
this.xrTableCell46.BorderColor = System.Drawing.Color.DarkGray;
this.xrTableCell46.Borders = ((DevExpress.XtraPrinting.BorderSide)(((DevExpress.XtraPrinting.BorderSide.Left | DevExpress.XtraPrinting.BorderSide.Right)
| DevExpress.XtraPrinting.BorderSide.Bottom)));
this.xrTableCell46.DataBindings.AddRange(new DevExpress.XtraReports.UI.XRBinding[] {
new DevExpress.XtraReports.UI.XRBinding("Text", null, "ServiceInvoicePeriods.ServiceInvoiceItems.RateFactor")});
this.xrTableCell46.Name = "xrTableCell46";
this.xrTableCell46.Padding = new DevExpress.XtraPrinting.PaddingInfo(2, 2, 2, 2, 100F);
this.xrTableCell46.StylePriority.UseBorderColor = false;
this.xrTableCell46.StylePriority.UseBorders = false;
this.xrTableCell46.StylePriority.UsePadding = false;
this.xrTableCell46.StylePriority.UseTextAlignment = false;
this.xrTableCell46.TextAlignment = DevExpress.XtraPrinting.TextAlignment.MiddleRight;
this.xrTableCell46.Weight = 0.42042083402151886D;
//
// xrTableCell47
//
this.xrTableCell47.BorderColor = System.Drawing.Color.DarkGray;
@@ -1387,8 +1310,6 @@ namespace VSDHammReports
private DevExpress.XtraReports.UI.XRTableCell xrTableCell37;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell35;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell33;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell36;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell38;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell34;
private DevExpress.XtraReports.UI.ReportFooterBand ReportFooter;
private DevExpress.XtraReports.UI.DetailReportBand DetailReport2;
@@ -1399,8 +1320,6 @@ namespace VSDHammReports
private DevExpress.XtraReports.UI.XRTableCell xrTableCell42;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell43;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell44;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell45;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell46;
private DevExpress.XtraReports.UI.XRTableCell xrTableCell47;
private DevExpress.XtraReports.UI.XRLabel xrLabel7;
private DevExpress.XtraReports.UI.XRTableRow xrTableRow26;