2016-06-27 01:45:38 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using BeWo.Data.Entities ;
using BeWo.Service.DCEntityMapper ;
2023-11-14 17:10:04 +01:00
using BeWo.Service.Plugins ;
2016-06-27 01:45:38 +02:00
using BS.Shared ;
using BS.Shared.Core ;
using BS.Shared.DataContracts ;
using BS.Shared.Extensions ;
using BS.Shared.Services ;
using DevExpress.Utils ;
namespace BeWo.Service.Invoicing
{
[IDSpecificClass(Identifier = "LWLNEU")]
2017-02-01 13:53:59 +01:00
public class LWLSettlementInvoiceCreation : SettlementInvoiceCreation
2016-06-27 01:45:38 +02:00
{
2024-08-08 14:02:10 +02:00
public virtual bool SchneideBeiErreichenDesBudgetsAb { get { return false ; } }
public override bool CanCreateInvoiceTheOldWay ( Settlement2DC si , CostBearer2SupportConcept c2s )
2016-06-27 01:45:38 +02:00
{
return true ;
}
public override void CreateInvoiceDetailsTheOldWay ( Settlement2DC si , CostBearer2SupportConcept c2s )
{
String id = c2s . CostBearer . ID ;
2023-11-14 17:10:04 +01:00
var calc = PluginLoader . FindClass < Calculations > ( id ) ;
2023-11-23 02:16:27 +01:00
if ( calc = = null )
{
calc = Calculations . GetInstance ( id ) ;
}
2016-06-27 01:45:38 +02:00
var relDc = c2s . MapToNewDC ( ) ;
2023-04-06 11:43:53 +02:00
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 ;
2023-09-08 15:52:55 +02:00
fkAp . Notice = "Fehlkontakte" ;
2023-04-06 11:43:53 +02:00
apListFehlkontakt . Add ( fkAp ) ;
}
si . ApprovedFLS = CalculateApprovedTotalFLS ( apListFLS ) ;
2016-06-27 01:45:38 +02:00
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 ;
2017-02-01 13:53:59 +01:00
si . ErrorNotice =
String . Format (
"Achtung: Die Betreuung endete am {0:dd.MM.yyyy}. Alle nachträglich eingegebenen Stunden werden nicht abgerechnet!" ,
periodEnd ) ;
}
2016-06-27 01:45:38 +02:00
var hourlyRatesInSpanList = relDc . CostBearer . CostRatePeriods . GetSpans ( CostRatePeriodType . HourlyRate , periodStart , periodEnd ) . ToList ( ) ;
si . DifferentHourlyRateCount = hourlyRatesInSpanList . Count ;
2017-02-01 13:53:59 +01:00
if ( si . DifferentHourlyRateCount = = 0 )
{
si . DifferentHourlyRateCount = 1 ;
}
var rateFactorsInSpanList = relDc . CostBearer . CostRatePeriods . GetSpans ( CostRatePeriodType . RateFactor , periodStart , periodEnd ) . ToList ( ) ;
2016-06-27 01:45:38 +02:00
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 ( ) ;
2017-08-14 11:59:31 +02:00
var absenceTimes = MapperFactory . AbsenceTimeDC_AbsenceTime . MapToNewDCs ( c2s . SupportConcept . Customer . AbsenceTimes ) ;
Dictionary < DateTimeSpan , decimal > span2Hours = calc . GetAbsencesTimesNotBillable ( absenceTimes ) ;
si . RecordedBillableFLSNotBillableCustomerAbsent
2016-06-27 01:45:38 +02:00
= calc . GetHoursNotBillableDueToAbsenceTimes ( relDc , recordDCS ,
2017-08-14 11:59:31 +02:00
span2Hours , false , false ) ;
2016-06-27 01:45:38 +02:00
si . RecordedBillableFLSNotBillableOutOfApprovedSpan = calc . GetHoursNotBillableDueToOutOfSpan ( relDc , recordDCS ) ;
//calc.GetHoursNotBillableDueToMoreThanApproved(relDC, recordDCS, false, false);
Dictionary < long , bool > groupBookingDict = new Dictionary < long , bool > ( ) ;
//decimal flmCleared = 0;
2024-08-08 14:02:10 +02:00
2016-06-27 01:45:38 +02:00
records = records . OrderBy < ServiceRecord , DateTime > ( sr = > sr . Start . HasValue ? sr . Start . Value : DateTime . MinValue ) . ToList < ServiceRecord > ( ) ;
2024-09-01 20:47:02 +02:00
decimal maxApprovedMinutes = ( si . ApprovedFLS ? ? 0 ) * 60 ;
decimal totalMinutesGeleistet = 0 ;
2016-06-27 01:45:38 +02:00
foreach ( var iRecord in records )
{
if ( iRecord . ServiceDescription . ServiceCategory . IsBillable )
{
if ( iRecord . GroupOid = = null | | ! groupBookingDict . ContainsKey ( iRecord . GroupOid . Value ) )
{
2023-11-14 17:10:04 +01:00
2023-04-06 11:43:53 +02:00
decimal dauerAbrechenbar = iRecord . RoundedDuration ;
decimal dauerBudget = iRecord . RoundedDuration ;
2023-11-14 17:10:04 +01:00
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 ;
}
}
2023-06-05 14:36:23 +02:00
//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 ;
2023-04-06 11:43:53 +02:00
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 ;
}
2016-06-27 01:45:38 +02:00
var ap = apList . FirstOrDefault (
2023-04-06 11:43:53 +02:00
iap = >
2016-06-27 01:45:38 +02:00
{
2023-04-06 11:43:53 +02:00
return iap . PeriodStartDate < = iRecord . Start & &
( iap . PeriodEndDate > = DateTime . MaxValue . AddDays ( - 1 ) | | iRecord . Start < iap . PeriodEndDate . AddDays ( 1 ) ) ;
} ) ;
2016-06-27 01:45:38 +02:00
2023-04-06 11:43:53 +02:00
if ( ap ! = null )
{
2024-09-01 20:47:02 +02:00
//Christian wars:-)
var dauerBudgetMitRf = dauerBudget * ( ( ap . RateFactor ? ? 0 ) + 100 ) / 100 ;
if ( SchneideBeiErreichenDesBudgetsAb )
2024-08-08 14:02:10 +02:00
{
2024-09-01 20:47:02 +02:00
if ( totalMinutesGeleistet + dauerBudgetMitRf > maxApprovedMinutes )
2024-08-08 14:02:10 +02:00
{
2024-09-01 20:47:02 +02:00
dauerBudget = maxApprovedMinutes - totalMinutesGeleistet ;
dauerBudget / = ( ( ap . RateFactor ? ? 0 ) + 100 ) / 100 ;
if ( dauerBudget < 0 )
2024-08-08 14:02:10 +02:00
{
dauerBudget = 0 ;
}
if ( dauerAbrechenbar > dauerBudget )
{
dauerAbrechenbar = dauerBudget ;
}
}
2024-09-01 20:47:02 +02:00
}
totalMinutesGeleistet + = dauerBudgetMitRf ;
2023-04-06 11:43:53 +02:00
ap . RecordedMinutes + = dauerAbrechenbar ;
ap . BudgetMinutes + = dauerBudget ;
2016-06-27 01:45:38 +02:00
}
if ( iRecord . GroupOid ! = null & & ! groupBookingDict . ContainsKey ( iRecord . GroupOid . Value ) )
{
2019-07-12 17:36:37 +02:00
si . HasGroupRecords = true ;
2016-06-27 01:45:38 +02:00
groupBookingDict . Add ( iRecord . GroupOid . Value , true ) ;
}
}
}
}
2024-09-01 20:47:02 +02:00
si . RecordedMinutesTotal = totalMinutesGeleistet ;
2016-06-27 01:45:38 +02:00
//decimal? maxAmount = CalculateMaxAmount(si, apList);
2023-04-06 11:43:53 +02:00
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 ) ;
2016-06-27 01:45:38 +02:00
decimal totalAmount = 0 ;
decimal rateFactorAmount = 0 ;
2023-04-06 11:43:53 +02:00
2016-06-27 01:45:38 +02:00
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 ) ) ) ;
}
}
2022-04-06 11:49:20 +02:00
if ( ! si . RecordedBillableFLSBrutto . HasValue )
{
si . RecordedBillableFLSNetto = si . InvoiceItems . Sum ( ii = > ( ii . UnitCount ? ? 0 ) ) ; // * rf; //eigentlich falsch dass netto = brutto ist.
si . RecordedBillableFLSBrutto = si . RecordedBillableFLSNetto ;
}
2016-06-27 01:45:38 +02:00
si . AmountBillableTotal = totalAmount ;
si . AmountWithRateFactor = rateFactorAmount ;
2022-04-06 11:49:20 +02:00
2016-06-27 01:45:38 +02:00
//AdvancedPayments
CalculateAdvancedPayments ( si , c2s , null , null , hourlyRatesInSpanList ) ;
//EquityContribution (Eigenanteil)
CalculateEquityContribution ( si , c2s . SupportConcept , null , null ) ;
si . Claim = si . AmountBillableTotal - ( si . AmountAdvancedPayments ? ? 0 ) - si . EquityContribution ;
}
2017-02-01 13:53:59 +01:00
private decimal GetRateFactor ( Settlement2DC si )
{
2016-06-27 01:45:38 +02:00
decimal rf = 1 ;
if ( si . RateFactor . HasValue & & si . RateFactor . Value ! = 0 )
{
rf = si . RateFactor . Value ;
rf = ( 100 + rf ) / 100 ;
}
return rf ;
}
2017-02-01 13:53:59 +01:00
public virtual List < InvoiceItemDC > CreateInvoiceItems ( Settlement2DC si , IList < ApprovalPeriod > apList )
{
2021-05-24 21:40:02 +02:00
return CreateInvoiceItems ( si , apList , false ) ;
2017-02-01 13:53:59 +01:00
}
public virtual List < InvoiceItemDC > CreateInvoiceItems ( Settlement2DC si , IList < ApprovalPeriod > apList , bool schneideBeiMaxAb )
{
List < InvoiceItemDC > iiList = new List < InvoiceItemDC > ( ) ;
2016-06-27 01:45:38 +02:00
var apsByHourlyRate = new Dictionary < String , IList < ApprovalPeriod > > ( ) ;
foreach ( var ap in apList )
{
IList < ApprovalPeriod > list = null ;
String key = String . Format ( "{0}_{1}" , ap . AmountPerUnit , ap . RateFactor ) ;
if ( apsByHourlyRate . ContainsKey ( key ) )
{
list = apsByHourlyRate [ key ] ;
}
else
{
list = new List < ApprovalPeriod > ( ) ;
apsByHourlyRate [ key ] = list ;
}
list . Add ( ap ) ;
}
2017-02-01 13:53:59 +01:00
decimal maxApprovedFls = si . ApprovedFLS ? ? 0 ;
decimal totalHours = 0 ;
2016-06-27 01:45:38 +02:00
foreach ( var key in apsByHourlyRate . Keys )
{
IList < ApprovalPeriod > list = apsByHourlyRate [ key ] ;
var ii = new InvoiceItemDC ( ) ;
decimal minutesTotal = 0 ;
2023-04-06 11:43:53 +02:00
decimal minutesTotalBudget = 0 ;
2016-06-27 01:45:38 +02:00
DateTime start = DateTime . MaxValue ;
DateTime end = DateTime . MinValue ;
decimal rf = si . RateFactor ? ? 0 ;
decimal hr = 0 ;
foreach ( var approvalPeriod in list )
{
minutesTotal + = approvalPeriod . RecordedMinutes ;
2023-04-06 11:43:53 +02:00
minutesTotalBudget + = approvalPeriod . BudgetMinutes ;
2016-06-27 01:45:38 +02:00
if ( approvalPeriod . PeriodStartDate < start )
start = approvalPeriod . PeriodStartDate ;
if ( approvalPeriod . PeriodEndDate > end )
end = approvalPeriod . PeriodEndDate ;
hr = approvalPeriod . AmountPerUnit ;
if ( approvalPeriod . RateFactor . HasValue )
{
rf = approvalPeriod . RateFactor . Value ;
}
2023-09-08 15:52:55 +02:00
if ( ! String . IsNullOrEmpty ( approvalPeriod . Notice ) & & approvalPeriod . Notice = = "Fehlkontakte" )
2023-06-05 14:36:23 +02:00
{
hr * = 0.8 m ;
2024-03-18 17:12:43 +01:00
ii . ItemDescription = "Fehlkontakte" ;
}
2016-06-27 01:45:38 +02:00
}
ii . ItemPeriodStart = start ;
ii . ItemPeriodEnd = end ;
ii . RateFactor = rf ;
ii . AmountPerUnit = hr ;
decimal hours = Math . Round ( minutesTotal / 60 m , 2 , MidpointRounding . AwayFromZero ) ;
hours * = ( 100 + rf ) / 100 ;
hours = Math . Round ( hours , 2 , MidpointRounding . AwayFromZero ) ;
2023-04-06 11:43:53 +02:00
decimal hoursBudget = Math . Round ( minutesTotalBudget / 60 m , 2 , MidpointRounding . AwayFromZero ) ;
hoursBudget * = ( 100 + rf ) / 100 ;
hoursBudget = Math . Round ( hoursBudget , 2 , MidpointRounding . AwayFromZero ) ;
2024-08-08 14:02:10 +02:00
//if (schneideBeiMaxAb)
//{
// if (totalHours + hoursBudget > maxApprovedFls)
// {
// hoursBudget = maxApprovedFls - totalHours;
// if (hoursBudget < 0)
// {
// hoursBudget = 0;
// }
// if (hours > hoursBudget)
// {
// hours = hoursBudget;
// }
// }
//}
2023-04-06 11:43:53 +02:00
totalHours + = hoursBudget ;
2016-06-27 01:45:38 +02:00
2023-04-06 11:43:53 +02:00
ii . UnitCount = hours ;
ii . UnitCountBudget = hoursBudget ;
2016-06-27 01:45:38 +02:00
ii . AmountTotal = Math . Round ( hr * hours , 2 , MidpointRounding . AwayFromZero ) ;
ii . GrossAmountTotal = ii . AmountTotal ;
iiList . Add ( ii ) ;
}
return iiList . OrderBy ( ii = > ii . ItemPeriodStart ) . ToList ( ) ;
}
private decimal? CalculateApprovedTotalFLS ( IList < ApprovalPeriod > apList )
{
decimal flsTotal = 0 ;
foreach ( var ap in apList )
{
flsTotal + = ap . ApprovedTotal ;
}
return flsTotal ;
}
//private decimal? CalculateMaxAmount(Settlement2DC si, IList<ApprovalPeriod> apList)
//{
// var apsByHourlyRate = new Dictionary<decimal, IList<ApprovalPeriod>>();
// foreach (var ap in apList)
// {
// IList<ApprovalPeriod> list = null;
// if (apsByHourlyRate.ContainsKey(ap.AmountPerUnit))
// {
// list = apsByHourlyRate[ap.AmountPerUnit];
// }
// else
// {
// list = new List<ApprovalPeriod>();
// apsByHourlyRate[ap.AmountPerUnit] = list;
// }
// list.Add(ap);
// }
// decimal amount = 0;
// foreach (var hr in apsByHourlyRate.Keys)
// {
// IList<ApprovalPeriod> list = apsByHourlyRate[hr];
// decimal flsTotal = 0;
// foreach (var approvalPeriod in list)
// {
// var fls = Math.Round(approvalPeriod.Weeks * approvalPeriod.ApprovedPerUnit, 2);
// flsTotal += fls;
// }
// decimal a = Math.Round(flsTotal, 2) * hr;
// amount += Math.Round(a, 2);
// }
// return amount;
//}
public override void InitApprovalPeriod ( Settlement2DC si , ApprovalPeriod approvalPeriod )
{
base . InitApprovalPeriod ( si , approvalPeriod ) ;
approvalPeriod . Weeks = Math . Round ( approvalPeriod . Days * 0.1427 m , 2 ) ;
if ( approvalPeriod . ApprovalUnit = = AccountingIntervalType . Weekly )
{
approvalPeriod . ApprovedTotal = Math . Round ( approvalPeriod . Weeks * approvalPeriod . ApprovedPerUnit , 2 ) ;
}
approvalPeriod . ApprovedAmountTotal = Math . Round ( approvalPeriod . ApprovedTotal * approvalPeriod . AmountPerUnit , 2 ) ;
}
}
}