217 lines
10 KiB
C#
217 lines
10 KiB
C#
using BeWo.Data.Entities;
|
|
using BeWo.Service.Invoicing;
|
|
using BS.Shared;
|
|
using BS.Shared.DataContracts;
|
|
using BS.Shared.Services;
|
|
using DevExpress.Data;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace EndartFabrik.Invoicing
|
|
{
|
|
public class CustomSettlementInvoiceCreation : SettlementInvoiceCreation
|
|
{
|
|
public override bool CanCreateInvoiceTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override List<InvoiceItemDC> CreateInvoiceItemsForAccountingPeriods(Settlement2DC si, List<AccountingPeriod> accountingPeriods,
|
|
List<CostRatePeriodDC> costRatePeriods, Calculations calc)
|
|
{
|
|
var list = base.CreateInvoiceItemsForAccountingPeriods(si, accountingPeriods, costRatePeriods, calc);
|
|
|
|
foreach (var item in list)
|
|
{
|
|
if (item.CustomString1 != null && item.CustomString1.Contains("gekappte, zu viel geleistete Stunden:"))
|
|
{
|
|
item.ItemDescription += item.CustomString1;
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
public override InvoiceItemDC CreateInvoiceItemForAccountingPeriod(AccountingPeriod accountingPeriod)
|
|
{
|
|
InvoiceItemDC ii = new InvoiceItemDC();
|
|
ii.AccountingInterval = accountingPeriod.AccountingInterval;
|
|
ii.ItemPeriodStart = accountingPeriod.PeriodStart;
|
|
ii.ItemPeriodEnd = accountingPeriod.PeriodEnd;
|
|
ii.RateFactor = accountingPeriod.RateFactor;
|
|
ii.AmountPerUnit = accountingPeriod.Amount;
|
|
ii.UnitDescription = accountingPeriod.UnitName;
|
|
ii.MaxApprovedAmount = accountingPeriod.MaxApprovedAmount;
|
|
ii.MaxApprovedUnitCount = accountingPeriod.MaxApprovedUnitCount;
|
|
ii.ApprovedPerUnit = accountingPeriod.ApprovedPerUnit;
|
|
ii.ApprovalUnit = accountingPeriod.ApprovalUnit;
|
|
ii.ProzentAbrechenbar = accountingPeriod.ProzentAbrechenbar;
|
|
|
|
if (accountingPeriod.ServiceCategory != null)
|
|
ii.ItemDescription = accountingPeriod.ServiceCategory.Name;
|
|
|
|
if (!String.IsNullOrEmpty(accountingPeriod.Notice))
|
|
{
|
|
ii.ItemDescription = accountingPeriod.Notice;
|
|
}
|
|
if (accountingPeriod.AccountingInterval == AccountingIntervalType.FlatRate)
|
|
{
|
|
ii.UnitCount = accountingPeriod.ServiceRecordsInPeriod.Count;
|
|
|
|
}
|
|
else if (accountingPeriod.AccountingInterval == AccountingIntervalType.Hourly)
|
|
{
|
|
decimal minutesTotal = 0;
|
|
decimal minutesTotalBudget = 0;
|
|
|
|
if (accountingPeriod.ServiceRecordsInPeriod != null)
|
|
{
|
|
// Claude: Kappung nur wenn wöchentliche Bewilligung ohne Stundenübertragung konfiguriert ist
|
|
bool weeklyKappung = accountingPeriod.ApprovalUnit != null
|
|
&& accountingPeriod.ApprovalUnit == AccountingIntervalType.Weekly
|
|
&& accountingPeriod.ApprovedPerUnit.HasValue
|
|
&& !accountingPeriod.SupportConceptApprovalPeriod.IsApprovedBEShifting;
|
|
|
|
if (weeklyKappung)
|
|
{
|
|
// Claude: Wochenlimit in Minuten umrechnen (ApprovedPerUnit ist in Stunden)
|
|
decimal approvedMinutesPerWeek = accountingPeriod.ApprovedPerUnit.Value * 60;
|
|
var weeklyRaw = new Dictionary<DateTime, decimal>();
|
|
var weeklyBudget = new Dictionary<DateTime, decimal>();
|
|
|
|
foreach (var ar in accountingPeriod.ServiceRecordsInPeriod)
|
|
{
|
|
if (!ar.Start.HasValue) continue;
|
|
|
|
// Claude: Montag der jeweiligen Woche als Dictionary-Schlüssel
|
|
int daysSinceMonday = ((int)ar.Start.Value.DayOfWeek + 6) % 7;
|
|
var weekStart = ar.Start.Value.Date.AddDays(-daysSinceMonday);
|
|
|
|
var prozent = ar.ServiceDescription.Category.Percentage;
|
|
if (ar.ServiceDescription.ProzentAbrechnung.HasValue)
|
|
prozent = ar.ServiceDescription.ProzentAbrechnung.Value;
|
|
ii.ProzentAbrechenbar = prozent;
|
|
|
|
// Claude: ProzentBudget nur für das Budget-Feld berücksichtigen, nicht für die Rohminuten
|
|
decimal budgetProzent = ar.ServiceDescription.Category.ProzentBudget ?? 100;
|
|
if (ar.ServiceDescription.ProzentBudget.HasValue)
|
|
budgetProzent = ar.ServiceDescription.ProzentBudget.Value;
|
|
|
|
if (!weeklyRaw.ContainsKey(weekStart)) weeklyRaw[weekStart] = 0;
|
|
if (!weeklyBudget.ContainsKey(weekStart)) weeklyBudget[weekStart] = 0;
|
|
|
|
weeklyRaw[weekStart] += ar.RoundedDuration;
|
|
weeklyBudget[weekStart] += ar.RoundedDuration * budgetProzent / 100;
|
|
}
|
|
|
|
// Claude: Jede Woche einzeln auf das Wochenlimit kappen, dann summieren
|
|
foreach (var kvp in weeklyRaw)
|
|
minutesTotal += Math.Min(kvp.Value, approvedMinutesPerWeek);
|
|
|
|
foreach (var kvp in weeklyBudget)
|
|
minutesTotalBudget += Math.Min(kvp.Value, approvedMinutesPerWeek);
|
|
|
|
if (minutesTotal < accountingPeriod.ServiceRecordsInPeriod.Sum(s => s.RoundedDuration))
|
|
{
|
|
ii.CustomString1 = string.Format("gekappte, zu viel geleistete Stunden: {0:0.00}", (accountingPeriod.ServiceRecordsInPeriod.Sum(s => s.RoundedDuration) - minutesTotal) / 60);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var ar in accountingPeriod.ServiceRecordsInPeriod)
|
|
{
|
|
minutesTotal += ar.RoundedDuration;
|
|
|
|
var prozent = ar.ServiceDescription.Category.Percentage;
|
|
if (ar.ServiceDescription.ProzentAbrechnung.HasValue)
|
|
{
|
|
prozent = ar.ServiceDescription.ProzentAbrechnung.Value;
|
|
}
|
|
ii.ProzentAbrechenbar = prozent;
|
|
|
|
prozent = ar.ServiceDescription.Category.ProzentBudget ?? 100;
|
|
if (ar.ServiceDescription.ProzentBudget.HasValue)
|
|
{
|
|
prozent = ar.ServiceDescription.ProzentBudget.Value;
|
|
}
|
|
minutesTotalBudget += (ar.RoundedDuration * prozent) / 100;
|
|
}
|
|
}
|
|
}
|
|
ii.UnitCount = minutesTotal / 60;
|
|
ii.UnitCountBudget = minutesTotalBudget / 60;
|
|
|
|
if (String.IsNullOrEmpty(ii.UnitDescription))
|
|
ii.UnitDescription = ii.UnitCount.Value == 1 ? "Stunde" : "Stunden";
|
|
}
|
|
else if (accountingPeriod.PeriodStart > DateTime.MinValue && accountingPeriod.PeriodEnd < DateTime.MaxValue)
|
|
{
|
|
DateTime intervalStart = accountingPeriod.PeriodStart;
|
|
DateTime intervalEnd = intervalStart;
|
|
int count = 0;
|
|
while (intervalStart < accountingPeriod.PeriodEnd)
|
|
{
|
|
count++;
|
|
switch (accountingPeriod.AccountingInterval)
|
|
{
|
|
case AccountingIntervalType.Daily:
|
|
intervalEnd = intervalStart.AddDays(1);
|
|
break;
|
|
case AccountingIntervalType.Weekly:
|
|
intervalEnd = intervalStart.AddDays(7);
|
|
break;
|
|
case AccountingIntervalType.Fortnightly:
|
|
intervalEnd = intervalStart.AddDays(14);
|
|
break;
|
|
case AccountingIntervalType.Monthly:
|
|
intervalEnd = intervalStart.AddMonths(1);
|
|
break;
|
|
case AccountingIntervalType.Quarterly:
|
|
intervalEnd = intervalStart.AddMonths(3);
|
|
break;
|
|
case AccountingIntervalType.HalfYearly:
|
|
intervalEnd = intervalStart.AddMonths(6);
|
|
break;
|
|
case AccountingIntervalType.Yearly:
|
|
intervalEnd = intervalStart.AddYears(1);
|
|
break;
|
|
}
|
|
intervalStart = intervalEnd;
|
|
}
|
|
ii.UnitCount = count;
|
|
ii.UnitCountBudget = count;
|
|
|
|
switch (accountingPeriod.AccountingInterval)
|
|
{
|
|
case AccountingIntervalType.Daily:
|
|
ii.UnitDescription = count == 1 ? "Tag" : "Tage";
|
|
break;
|
|
case AccountingIntervalType.Weekly:
|
|
ii.UnitDescription = count == 1 ? "Woche" : "Wochen";
|
|
break;
|
|
case AccountingIntervalType.Fortnightly:
|
|
ii.UnitDescription = "2 Wochen";
|
|
break;
|
|
case AccountingIntervalType.Monthly:
|
|
ii.UnitDescription = count == 1 ? "Monat" : "Monate";
|
|
break;
|
|
case AccountingIntervalType.Quarterly:
|
|
ii.UnitDescription = count == 1 ? "Quartal" : "Quartale";
|
|
break;
|
|
case AccountingIntervalType.HalfYearly:
|
|
ii.UnitDescription = "Halbjährlich";
|
|
break;
|
|
case AccountingIntervalType.Yearly:
|
|
ii.UnitDescription = count == 1 ? "Jahr" : "Jahre";
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
return ii;
|
|
}
|
|
|
|
}
|
|
}
|