MH: Endart Kappung in Spitzabrechnung Zwischenstand
This commit is contained in:
@@ -14,5 +14,118 @@ namespace EndartFabrik.Invoicing
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void CalculateInvoiceItemAmounts(Settlement2DC si)
|
||||
{
|
||||
// Claude: Gesamtbudget aus bewilligten Stunden – kein Limit wenn CheckMaxApprovedHours() false zurückgibt
|
||||
decimal flsRestBudget = si.ApprovedFLS ?? 0;
|
||||
if (!CheckMaxApprovedHours())
|
||||
flsRestBudget = decimal.MaxValue;
|
||||
|
||||
// Claude: Gekappt-Items sammeln und erst nach der Schleife einfügen, um die Item-Reihenfolge nicht zu stören
|
||||
var gekapptItems = new List<InvoiceItemDC>();
|
||||
|
||||
foreach (var ii in si.InvoiceItems)
|
||||
{
|
||||
if (!ii.UnitCount.HasValue)
|
||||
continue;
|
||||
|
||||
if (ii.UnitCount > 0)
|
||||
{
|
||||
// Claude: Rohstunden (UnitCount) und Budgetstunden (UnitCountBudget) trennen –
|
||||
// ProzentBudget der Leistung geht über die Relation in den Budgetverbrauch ein, RateFactor nicht
|
||||
var rawCount = Math.Round(ii.UnitCount.Value, 2, MidpointRounding.AwayFromZero);
|
||||
var unitCountBudget = Math.Round(ii.UnitCountBudget ?? rawCount, 2, MidpointRounding.AwayFromZero);
|
||||
var relation = unitCountBudget / rawCount;
|
||||
|
||||
// Claude: Gewichtete Stunden für den Budgetvergleich berechnen
|
||||
var gewichteteStunden = rawCount * relation;
|
||||
var cappedRawCount = rawCount;
|
||||
|
||||
if (gewichteteStunden > flsRestBudget)
|
||||
{
|
||||
// Claude: Budget reicht nicht → Rohstunden auf verbleibendes Budget herunterrechnen
|
||||
gewichteteStunden = flsRestBudget;
|
||||
cappedRawCount = Math.Round(gewichteteStunden / relation, 2, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
// Claude: Verbrauchte gewichtete Stunden vom Restbudget abziehen (RateFactor hat keinen Einfluss)
|
||||
flsRestBudget -= gewichteteStunden;
|
||||
|
||||
if (ii.AmountPerUnit.HasValue)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ii.ItemDescription) && ii.ItemDescription.ToLower().Contains("fehlkontakt") && ii.ProzentAbrechenbar.HasValue)
|
||||
{
|
||||
// Claude: Fehlkontakte: Stundensatz anteilig reduzieren statt aufzuteilen
|
||||
ii.AmountPerUnit = ii.AmountPerUnit * ii.ProzentAbrechenbar / 100;
|
||||
}
|
||||
else if (rawCount - cappedRawCount > 0 && cappedRawCount != 0)
|
||||
{
|
||||
// Claude: Teilkappung – separates Item für überschüssige Stunden anlegen (ohne Betrag / 0 EUR)
|
||||
gekapptItems.Add(CopyItemWithoutAmount(ii, rawCount - cappedRawCount));
|
||||
}
|
||||
|
||||
// Claude: UnitCount auf die abrechenbaren Rohstunden setzen (ohne Faktor)
|
||||
ii.UnitCount = cappedRawCount;
|
||||
|
||||
if (cappedRawCount != 0)
|
||||
{
|
||||
// Claude: Nettobetrag aus Rohstunden × Stundensatz, Faktor erst beim Bruttobetrag anwenden
|
||||
ii.AmountTotal = ii.UnitCount.Value * ii.AmountPerUnit.Value;
|
||||
ii.GrossAmountTotal = ii.RateFactor.HasValue && ii.RateFactor.Value != 0
|
||||
? Math.Round(ii.AmountTotal.Value * ((100m + ii.RateFactor.Value) / 100m), 2, MidpointRounding.AwayFromZero)
|
||||
: ii.AmountTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Claude: Vollständige Kappung – Rohstunden für Anzeige wiederherstellen, aber 0 EUR abrechnen
|
||||
ii.UnitCount = rawCount;
|
||||
ii.AmountTotal = null;
|
||||
ii.GrossAmountTotal = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ii.AmountPerUnit.HasValue)
|
||||
{
|
||||
// Claude: UnitCount = 0: kein Kappen nötig, Fehlkontakt-Reduktion ggf. anwenden und Betrag auf 0 setzen
|
||||
if (!string.IsNullOrEmpty(ii.ItemDescription) && ii.ItemDescription.ToLower().Contains("fehlkontakt") && ii.ProzentAbrechenbar.HasValue)
|
||||
ii.AmountPerUnit = ii.AmountPerUnit * ii.ProzentAbrechenbar / 100;
|
||||
ii.AmountTotal = 0;
|
||||
ii.GrossAmountTotal = 0;
|
||||
}
|
||||
|
||||
// Claude: MaxApprovedUnitCount aktualisieren, wird für Anzeige auf der Rechnung benötigt
|
||||
if (!ii.MaxApprovedUnitCount.HasValue || ii.MaxApprovedUnitCount < ii.UnitCount)
|
||||
ii.MaxApprovedUnitCount = ii.UnitCount;
|
||||
}
|
||||
|
||||
// Claude: Gekappt-Items am Ende einfügen, damit die Reihenfolge der regulären Items erhalten bleibt
|
||||
si.InvoiceItems.AddRange(gekapptItems);
|
||||
}
|
||||
|
||||
private InvoiceItemDC CopyItemWithoutAmount(InvoiceItemDC ii, decimal unitCount)
|
||||
{
|
||||
// Claude: Kopie eines Items für die weggekappten Stunden – identische Metadaten, aber kein Betrag (GrossAmountTotal = null → 0 EUR)
|
||||
return new InvoiceItemDC
|
||||
{
|
||||
AccountingInterval = ii.AccountingInterval,
|
||||
AmountPerUnit = ii.AmountPerUnit,
|
||||
ApprovalUnit = ii.ApprovalUnit,
|
||||
ApprovedPerUnit = ii.ApprovedPerUnit,
|
||||
GrossAmountTotal = 0,
|
||||
ItemDescription = ii.ItemDescription + " (zu viel geleistet)",
|
||||
ItemPeriodEnd = ii.ItemPeriodEnd,
|
||||
ItemPeriodStart = ii.ItemPeriodStart,
|
||||
MaxApprovedAmount = ii.MaxApprovedAmount,
|
||||
MaxApprovedUnitCount = ii.MaxApprovedUnitCount,
|
||||
Notice = ii.Notice,
|
||||
ProzentAbrechenbar = ii.ProzentAbrechenbar,
|
||||
RateFactor = ii.RateFactor,
|
||||
SupportConceptApprovalPeriodOid = ii.SupportConceptApprovalPeriodOid,
|
||||
UnitCount = unitCount,
|
||||
UnitCountBudget = unitCount,
|
||||
UnitDescription = ii.UnitDescription + " (zu viel geleistet)"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using BeWo.Report;
|
||||
using BeWo.Report.ReportObjects;
|
||||
using BS.Shared.Extensions;
|
||||
using DevExpress.XtraReports.UI;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using DevExpress.XtraReports.UI;
|
||||
using BeWo.Report.ReportObjects;
|
||||
using BeWo.Report;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using BS.Shared.Extensions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace EndartFabrik
|
||||
{
|
||||
@@ -47,7 +48,20 @@ namespace EndartFabrik
|
||||
{
|
||||
ii.ItemDescription = "Fachleistungsstunden";
|
||||
}
|
||||
else if (ii.ItemDescription.Contains("(zu viel geleistet)"))
|
||||
{
|
||||
ii.AbrechnungsText = "(zu viel geleistet) " + ii.AbrechnungsText;
|
||||
|
||||
}
|
||||
}
|
||||
// Claude: Items mit "(zu viel geleistet)" ans Ende sortieren, damit sie nach den regulären Positionen erscheinen
|
||||
pRO.InvoiceItems.Sort((a, b) =>
|
||||
{
|
||||
bool aGekapptes = a.ItemDescription != null && a.ItemDescription.Contains("(zu viel geleistet)");
|
||||
bool bGekapptes = b.ItemDescription != null && b.ItemDescription.Contains("(zu viel geleistet)");
|
||||
return aGekapptes.CompareTo(bGekapptes);
|
||||
});
|
||||
|
||||
pRO.RateFactor = (double)rateFactor;
|
||||
|
||||
if (pRO.Salutation1.IsNullOrEmpty() && pRO.Salutation2.IsNullOrEmpty())
|
||||
|
||||
Reference in New Issue
Block a user