using System;
using System.Collections.Generic;
using System.Linq;

using BeWo.Data;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BeWo.Service.Plugins;
using BeWo.Service.ServiceImplementations;

using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;
using BS.Shared.Extensions;
using BS.Shared.Services;

namespace BeWo.Service.Invoicing
{
    public class InvoiceCreation : AbstractIDSpecificDefaultClass<InvoiceCreation>
    {
        public virtual List<ServiceInvoiceDC> GenerateServiceInvoices(long costBearerOid,
            DateTimeSpan invoicePeriod,
            Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> supportConcepts2ServiceRecords)
        {
            var costBearer = DAOFactory.GenericDAO.LoadByID<CostBearer>(costBearerOid);
            var invoiceList = new List<ServiceInvoice>();

            var invoiceCounter = 0;
            ServiceInvoice invoice;

            if (costBearer.IsSingleInvoicePerCustomer)
            {
                invoice = CreateCollectiveBilling(costBearer, invoicePeriod, supportConcepts2ServiceRecords);
                invoiceList.Add(invoice);
            }
            else
            {
                foreach (var iSupportConcept2ServiceRecords in supportConcepts2ServiceRecords)
                {
                    invoice = CreateSingleInvoice(invoiceCounter, costBearer, invoicePeriod, iSupportConcept2ServiceRecords.Key, iSupportConcept2ServiceRecords.Value);
                    invoiceList.Add(invoice);

                    invoiceCounter++;
                }
            }

            var result = invoiceList.Select(item => MapperFactory.ServiceInvoiceDC_ServiceInvoice.MapToNewDC(item)).ToList();

            return result.Count > 1 ? result.OrderBy(i => i.InvoiceBase.CustomerLastName).ToList() : result;
        }

        public virtual void SetInvoiceId(ServiceInvoice invoice)
        {
            //override
        }

        public virtual ServiceInvoice CreateSingleInvoice(int invoiceCounter, CostBearer costBearer, DateTimeSpan invoicePeriod, CompactSupportConceptDC supportConcept, List<ServiceRecordDC> serviceRecords)
        {
            var invoice = new ServiceInvoice();

            var supportConceptList = new List<CompactSupportConceptDC> {supportConcept};


            SetSenderInformation(invoice);
            SetInvoiceBaseData(invoiceCounter, invoice, costBearer, invoicePeriod, supportConceptList);
            SetSingleInvoiceBaseData(invoiceCounter, invoice, costBearer, invoicePeriod, supportConcept);

            SetRecipientInformation(invoice, costBearer);

            SetServiceInvoicePeriods(invoice, invoice.InvoiceBase.CostBearer2SupportConcept, invoicePeriod, serviceRecords);

            SetServiceInvoicePeriodAmounts(invoice);

            // Spitzabrechnung
            if (invoicePeriod == null)
            {
                SetAmountAdvancePayments(invoice);
                SetAmountEquityContribution(invoice);
            }
            return invoice;
        }

        private ServiceInvoice CreateCollectiveBilling(CostBearer costBearer, DateTimeSpan invoicePeriod, Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> supportConcepts2ServiceRecords)
        {
            var invoice = new ServiceInvoice();

            SetRecipientInformation(invoice, costBearer);
            SetSenderInformation(invoice);
            SetInvoiceBaseData(0, invoice, costBearer, invoicePeriod, supportConcepts2ServiceRecords.Keys.ToList());
            SetCollectiveBillingBaseData(0, invoice, costBearer, invoicePeriod, supportConcepts2ServiceRecords);

            SetCollectiveBillingServiceInvoicePeriods(invoice, invoicePeriod, supportConcepts2ServiceRecords);

            SetServiceInvoicePeriodAmounts(invoice);

            // Spitzabrechnung
            if (invoicePeriod == null)
            {
                SetAmountAdvancePayments(invoice);
            }
            return invoice;
        }


        public virtual void SetServiceInvoicePeriodAmounts(ServiceInvoice invoice)
        {
            foreach (ServiceInvoicePeriod iPeriod in invoice.ServiceInvoicePeriodList)
            {
                //Dictionary<decimal, decimal> rate2UnitCount = new Dictionary<decimal, decimal>();

                iPeriod.Claim = 0;
                if (iPeriod.ApprovedFixedAmount.HasValue && iPeriod.ApprovedFixedAmount.Value != 0)
                    iPeriod.Claim = iPeriod.ApprovedFixedAmount;
                else
                {
                    foreach (InvoiceItem item in iPeriod.InvoiceItemList)
                    {
                        //if (item.UnitCount.HasValue && item.AmountPerUnit.HasValue)
                        //{

                        //    if (!rate2UnitCount.ContainsKey(item.AmountPerUnit.Value))
                        //        rate2UnitCount.Add(item.AmountPerUnit.Value, item.UnitCount.Value);
                        //    else
                        //        rate2UnitCount[item.AmountPerUnit.Value] += item.UnitCount.Value;
                        //}
                        if (item.GrossAmountTotal.HasValue)
                            iPeriod.Claim += item.GrossAmountTotal;
                    }

                    //foreach (decimal rate in rate2UnitCount.Keys)
                    //{
                    //    decimal unitCount = rate2UnitCount[rate];
                    //    iPeriod.Claim += rate * Math.Round(unitCount, 2, MidpointRounding.AwayFromZero);
                    //}
                }
                iPeriod.Claim = Math.Round(iPeriod.Claim.Value, 2, MidpointRounding.AwayFromZero);
                //iPeriod.Claim = iPeriod.InvoiceItemList.Sum(i => i.GrossAmountTotal);

                //invoice.InvoiceBase.InvoiceItems.AddRange(iPeriod.InvoiceItemList);
            }
        }

        public virtual void SetAmountAdvancePayments(ServiceInvoice invoice)
        {
            decimal payments = 0;

            var processedBookings = new Dictionary<long, bool>();
            foreach (ServiceInvoicePeriod period in invoice.ServiceInvoicePeriodList)
            {
                if (period.SupportConceptApprovalPeriod != null)
                {
                    IList<AccountingTransaction> bookingList = period.SupportConceptApprovalPeriod.CostBearer2SupportConcept.AccountingTransactions;

                    bookingList = bookingList.Where(i => i.BookingDate.HasValue
                                                         && i.BookingDate.Value.InBetween(period.Start.Value, period.End.Value, true)).ToList();

                    foreach (AccountingTransaction item in bookingList)
                    {
                        if (!processedBookings.ContainsKey(item.Oid.Value))
                        {
                            payments += item.Amount;
                            processedBookings.Add(item.Oid.Value, true);
                        }
                    }
                }
            }


            invoice.AmountAdvancePayments = payments;
        }

        public virtual void SetAmountEquityContribution(ServiceInvoice invoice)
        {
            decimal equity = 0;

            IList<InvoiceBase> EquityInvoiceList =
                DAOFactory.SearchDAO.GetInvoiceBaseByTypeAndSupportConceptOid(InvoiceType.CustomerEquity, invoice.InvoiceBase.SupportConceptOid.Value);

            EquityInvoiceList = EquityInvoiceList.Where(i => i.InvoiceDate.HasValue &&
                                                             i.InvoiceDate.Value.InBetween(invoice.InvoiceBase.AccountingPeriodStart.Value, invoice.InvoiceBase.AccountingPeriodEnd.Value, true)).ToList();

            foreach (InvoiceBase iEquityInvoice in EquityInvoiceList)
            {
                foreach (InvoiceItem item in iEquityInvoice.InvoiceItems)
                {
                    if (item.AmountTotal != null)
                    {
                        equity += item.AmountTotal.Value;
                    }
                }
            }

            invoice.AmountEquityContribution = equity;
        }

        public virtual void SetInvoiceBaseData(int invoiceCounter, ServiceInvoice invoice, CostBearer costBearer, DateTimeSpan invoicePeriod, IList<CompactSupportConceptDC> supportConceptList)
        {
            DateTime start = DateTime.MaxValue;
            DateTime end = DateTime.MinValue;
            if (invoicePeriod != null)
            {
                start = invoicePeriod.StartDateTime;
                end = invoicePeriod.EndDateTime;
            }
            else
            {
                foreach (CompactSupportConceptDC iSupportConcept in supportConceptList)
                {
                    if (iSupportConcept.CostBearerList != null && iSupportConcept.CostBearerList.Count > 0)
                    {
                        DateTime? dt = iSupportConcept.CostBearerList[0].ApprovedStartDate;
                        if (dt.HasValue && dt < start)
                            start = dt.Value;
                        dt = iSupportConcept.CostBearerList[0].ApprovedEndDate;
                        if (dt.HasValue && dt > end)
                            end = dt.Value;
                    }
                }
            }
            invoice.InvoiceBase.AccountingPeriodStart = start;
            invoice.InvoiceBase.AccountingPeriodEnd = end;

            invoice.InvoiceBase.InvoiceDate = DateTime.Now;
            invoice.InvoiceBase.Type = InvoiceType.Service;
            invoice.InvoiceBase.DueDate = DateTime.Now.AddDays(14);

            SetInvoiceId(invoice);

            if (String.IsNullOrEmpty(invoice.InvoiceBase.InvoiceNumber))
            {
                var inc = PluginLoader.FindClass<InvoiceNumberGenerator>();
                invoice.InvoiceBase.InvoiceNumber = inc.GetNextInvoiceNumber(invoiceCounter, invoice.InvoiceBase);
            }
            if (costBearer.CostRatePeriods != null)
            {
                List<CostRatePeriodDC> crpList = MapperFactory.CostRatePeriodDC_CostRatePeriod.MapToNewDCs(costBearer.CostRatePeriods);
                CostRatePeriodDC crp = crpList.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now);

                if (crp != null)
                    invoice.ServiceUnitOid = crp.CostRatePeriodOid;
            }
        }

        public virtual void SetSingleInvoiceBaseData(int invoiceCounter, ServiceInvoice invoice, CostBearer costBearer, DateTimeSpan invoicePeriod, CompactSupportConceptDC supportConcept)
        {
            //Nur SingleInvoice und nicht Collective
            invoice.InvoiceBase.SupportConceptOid = supportConcept.SupportConceptOid;
            invoice.InvoiceBase.CostBearer2SupportConcept =
                DAOFactory.GenericDAO.LoadByID<CostBearer2SupportConcept>(supportConcept.CostBearerRelOids[0]);

            if (supportConcept.CustomerReferenceNumbers.Count > 0)
            {
                invoice.InvoiceBase.CustomerReferenceNumber = supportConcept.CustomerReferenceNumbers[0];
            }
            Person p = invoice.InvoiceBase.CostBearer2SupportConcept.SupportConcept.Customer.Person;
            DateTimeSpan span = invoice.InvoiceBase.CostBearer2SupportConcept.GetApprovedSpan();

            invoice.InvoiceBase.InvoiceTitle = String.Format("{0}, {1}, {2:dd.MM.yyy}-{3:dd.MM.yy}", p.LastName, p.FirstName, span.StartDate, span.EndDate);
        }

        public virtual void SetInvoiceItems(ServiceInvoice invoice,
            ServiceInvoicePeriod serviceInvoicePeriod,
            SupportConceptApprovalPeriodDC supportConceptApprovalPeriod,
            CostBearer2SupportConcept cb2sc,
            DateTimeSpan invoicePeriod,
            List<ServiceRecordDC> serviceRecords)
        {
            Calculations calc = PluginLoader.FindClass<Calculations>(_SpecificID) ?? Calculations.GetInstance(_SpecificID);

            if (ShouldCreateFixedAmounts(serviceInvoicePeriod, supportConceptApprovalPeriod, cb2sc))
            {
                CreateFixedAmounts(serviceInvoicePeriod, supportConceptApprovalPeriod, cb2sc);
            }
            else
            {
                List<ServiceRecordDC> serviceRecordsInPeriod = serviceRecords.Where(
                    i =>
                    {
                        bool valid = i.Start.Value.InBetween(serviceInvoicePeriod.Start.Value,
                            serviceInvoicePeriod.End.Value, true);

                        if (valid && serviceInvoicePeriod.SupportConceptApprovalPeriod != null && serviceInvoicePeriod.SupportConceptApprovalPeriod.ServiceCategory != null)
                            valid = serviceInvoicePeriod.SupportConceptApprovalPeriod.ServiceCategory.Oid ==
                                    i.ServiceDescription.Category.ServiceCategoryOid;
                        return valid;
                    }).ToList();

                if (serviceRecordsInPeriod.Count > 0)
                {
                    //var approvalPeriodDC = DAOFactory.GenericDAO
                    //    .LoadByID<SupportConceptApprovalPeriod>(serviceInvoicePeriod.SupportConceptApprovalPeriodOid.Value)
                    //    .MapToNewDC();

                    serviceInvoicePeriod.HoursNotBillableAbsence +=
                        calc.GetHoursNotBillableDueToAbsenceTimes(supportConceptApprovalPeriod,
                            cb2sc.CostBearer.CostRatePeriods.MapToNewDCs(),
                            serviceRecordsInPeriod,
                            cb2sc.SupportConcept.Customer.GetAbsencesTimesNotBillableWholeWeeks(),
                            true, true);

                    serviceInvoicePeriod.HoursNotBillableNotApproved +=
                        calc.GetHoursNotBillableDueToMoreThanApproved(supportConceptApprovalPeriod,
                            cb2sc.CostBearer.CostRatePeriods.MapToNewDCs(),
                            serviceRecordsInPeriod,
                            true, true);

                    var itemList = CreateInvoiceItems(serviceRecordsInPeriod, invoicePeriod, serviceInvoicePeriod.SupportConceptApprovalPeriod, calc);
                    serviceInvoicePeriod.InvoiceItemList.AddRange(itemList);
                }
            }
        }

        private static bool ShouldCreateFixedAmounts(ServiceInvoicePeriod serviceInvoicePeriod, SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, CostBearer2SupportConcept cb2sc)
        {
            if (serviceInvoicePeriod.ApprovedFixedAmount.HasValue)
                return true;
            if (supportConceptApprovalPeriod.ServiceCategory != null)
            {
                List<CostRatePeriodDC> crList = supportConceptApprovalPeriod.ServiceCategory.CostRatePeriods;
                AccountingIntervalType? ai = supportConceptApprovalPeriod.ServiceCategory.AccountingInterval;

                if (ai.HasValue && crList.Count(c => c.CostRateValue.HasValue) > 0)
                {
                    return ai.Value != AccountingIntervalType.Hourly;
                }
            }
            return false;
        }

        public virtual void CreateFixedAmounts(ServiceInvoicePeriod serviceInvoicePeriod, SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, CostBearer2SupportConcept cb2sc)
        {
            var calc = PluginLoader.FindClass<Calculations>(_SpecificID) ?? Calculations.GetInstance(_SpecificID);
            var data = calc.GetFixedBillingData(supportConceptApprovalPeriod, serviceInvoicePeriod.Start, serviceInvoicePeriod.End);

            foreach (var billingData in data)
            {
                serviceInvoicePeriod.InvoiceItemList.Add(new InvoiceItem
                {
                    AmountTotal        = billingData.AmountTotal,
                    GrossAmountTotal   = billingData.GrossAmountTotal,
                    ItemPeriodStart    = billingData.BillingPeriodStart,
                    ItemPeriodEnd      = billingData.BillingPeriodEnd,
                    AccountingInterval = billingData.AccountingInterval,
                    AmountPerUnit      = billingData.AmountPerUnit,
                    UnitCount          = billingData.UnitCount
                });
            }
        }


        public virtual void SetRecipientInformation(ServiceInvoice serviceInvoice, CostBearer costBearer)
        {
            if (costBearer.Organisation.InvoiceAddress != null)
            {
                serviceInvoice.InvoiceBase.RecipientAddress = costBearer.Organisation.InvoiceAddress.CopyToNew();
            }
            else if (costBearer.Organisation.Address != null)
            {
                serviceInvoice.InvoiceBase.RecipientAddress = costBearer.Organisation.Address.CopyToNew();
            }

            serviceInvoice.InvoiceBase.RecipientDivision        = "";
            serviceInvoice.InvoiceBase.RecipientOrganisationOid = costBearer.Organisation.Oid;
            serviceInvoice.InvoiceBase.RecipientOrganisation    = costBearer.Organisation.Name;
            serviceInvoice.InvoiceBase.RecipientContacts        = costBearer.Organisation.Contacts.Select(i => i.CopyToNew()).ToList();
            serviceInvoice.InvoiceBase.RecipientCostBearerOid   = costBearer.Oid;

            //serviceInvoice.InvoiceBase. = c2s.CostBearer.Organisation.Contacts.FirstOrDefault(f => f.Type.Equals(ContactType.business_POBox)) != null ? c2s.CostBearer.Organisation.Contacts.FirstOrDefault(f => f.Type.Equals(ContactType.business_POBox)).Value : String.Empty;

            if (serviceInvoice.InvoiceBase.CostBearer2SupportConcept != null &&
                serviceInvoice.InvoiceBase.CostBearer2SupportConcept.CostBearerContactPerson != null)
            {
                serviceInvoice.InvoiceBase.RecipientPersonFirstName = serviceInvoice.InvoiceBase.CostBearer2SupportConcept.CostBearerContactPerson.FirstName;
                serviceInvoice.InvoiceBase.RecipientPersonLastName  = serviceInvoice.InvoiceBase.CostBearer2SupportConcept.CostBearerContactPerson.LastName;
            }
        }

        public virtual void SetSenderInformation(ServiceInvoice serviceInvoice)
        {
            var lLoggedInEmployee = LoggedInUserOperationContextExt.Current.User.Employee;
            var mandator          = new OperationsServiceImp().GetMandator();

            if (lLoggedInEmployee != null)
            {
                serviceInvoice.InvoiceBase.CreatorFirstName = serviceInvoice.InvoiceBase.SenderFirstName = lLoggedInEmployee.Person.FirstName;
                serviceInvoice.InvoiceBase.CreatorLastName  = serviceInvoice.InvoiceBase.SenderLastName = lLoggedInEmployee.Person.LastName;
                serviceInvoice.InvoiceBase.SenderContacts   = lLoggedInEmployee.Person.Contacts.Select(i => i.CopyToNew()).ToList();
                serviceInvoice.InvoiceBase.SenderAddress    = new Address
                {
                    Street     = mandator.Street,
                    PostalCode = mandator.PostalCode,
                    Town       = mandator.Town,
                    State      = mandator.State
                };
            }
        }

        public virtual void SetServiceInvoicePeriods(ServiceInvoice invoice, CostBearer2SupportConcept costBearer2SupportConcept, DateTimeSpan invoicePeriod, List<ServiceRecordDC> serviceRecords)
        {
            var invoiceAccountingPeriodSpan = new DateTimeSpan
            {
                StartDateTime = invoice.InvoiceBase.AccountingPeriodStart.Value,
                EndDateTime   = invoice.InvoiceBase.AccountingPeriodEnd.Value
            };

            foreach (SupportConceptApprovalPeriod iPeriod in costBearer2SupportConcept.ApprovalPeriodList)
            {
                SupportConceptApprovalPeriodDC approvalPeriodDC = MapperFactory.SupportConceptApprovalPeriodDC_SupportConceptApprovalPeriod.MapToNewDC(iPeriod);


                if (approvalPeriodDC.Span.EndDateTime < invoiceAccountingPeriodSpan.StartDateTime || approvalPeriodDC.Span.StartDateTime > invoiceAccountingPeriodSpan.EndDate)
                {
                    continue;
                }

                ServiceInvoicePeriod serviceInvoicePeriod         = CreateServiceInvoicePeriod(approvalPeriodDC, invoiceAccountingPeriodSpan);
                serviceInvoicePeriod.SupportConceptApprovalPeriod = iPeriod;
                serviceInvoicePeriod.Customer                     = costBearer2SupportConcept.SupportConcept.Customer;

                CostBearer costBearer            = costBearer2SupportConcept.CostBearer;
                List<CostRatePeriodDC> costRates = MapperFactory.CostRatePeriodDC_CostRatePeriod.MapToNewDCs(costBearer.CostRatePeriods);

                CostRatePeriodDC serviceUnit         = costRates.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now);
                serviceInvoicePeriod.ServiceUnitName = serviceUnit.UnitName;

                Calculations calc = PluginLoader.FindClass<Calculations>(_SpecificID) ?? Calculations.GetInstance(_SpecificID);

                decimal amountPerUnit;
                decimal unitCount;

                serviceInvoicePeriod.ApprovedFixedAmount = calc.GetApprovedFixedAmountForPeriod(approvalPeriodDC, serviceInvoicePeriod.Start.Value, serviceInvoicePeriod.End.Value, out amountPerUnit, out unitCount);

                if (!serviceInvoicePeriod.ApprovedFixedAmount.HasValue)
                {
                    serviceInvoicePeriod.ApprovedHours = calc.GetApprovedHoursForPeriod(approvalPeriodDC,
                        costRates,
                        serviceInvoicePeriod.Start.Value,
                        serviceInvoicePeriod.End.Value);
                    if (serviceInvoicePeriod.ApprovedHours.HasValue)
                        serviceInvoicePeriod.ApprovedBE = serviceUnit.GetHoursInBE(serviceInvoicePeriod.ApprovedHours.Value);

                    serviceInvoicePeriod.ApprovedAmountDefaultHourlyRate = calc.GetApprovedAmountDefaultHourlyRateForPeriod(approvalPeriodDC,
                        costRates,
                        serviceInvoicePeriod.Start,
                        serviceInvoicePeriod.End);
                }
                SetInvoiceItems(invoice, serviceInvoicePeriod, approvalPeriodDC, costBearer2SupportConcept, invoicePeriod, serviceRecords);

                CheckMaxApprovedAmount(invoice, serviceInvoicePeriod, approvalPeriodDC, costBearer2SupportConcept, invoicePeriod, serviceRecords);

                if (serviceInvoicePeriod.InvoiceItemList.Count > 0)
                    invoice.ServiceInvoicePeriodList.Add(serviceInvoicePeriod);
            }
        }

        public virtual void CheckMaxApprovedAmount(ServiceInvoice invoice, ServiceInvoicePeriod sip, SupportConceptApprovalPeriodDC scap, CostBearer2SupportConcept costBearer2SupportConcept, DateTimeSpan invoicePeriod, List<ServiceRecordDC> serviceRecords)
        {
            if (sip.ApprovedHours.HasValue && !scap.IsApprovedBEShifting)
            {
                decimal totalHours = 0;

                foreach (var ii in sip.InvoiceItemList.Where(ii => ii.UnitCount.HasValue))
                {
                    if (totalHours + ii.UnitCount.Value > sip.ApprovedHours)
                    {
                        ii.UnitCount = sip.ApprovedHours.Value - totalHours;
                        if (ii.AmountPerUnit.HasValue)
                        {
                            ii.AmountTotal = ii.AmountPerUnit.Value*ii.UnitCount;
                            ii.GrossAmountTotal = ii.AmountTotal;
                        }
                        totalHours = sip.ApprovedHours.Value;
                    }
                    else
                    {
                        totalHours += ii.UnitCount.Value;
                    }
                }
            }
        }

        public virtual ServiceInvoicePeriod CreateServiceInvoicePeriod(SupportConceptApprovalPeriodDC approvalPeriodDC, DateTimeSpan invoiceAccountingPeriodSpan)
        {
            var sip = new ServiceInvoicePeriod
            {
                Start = approvalPeriodDC.Span.StartDateTime > invoiceAccountingPeriodSpan.StartDateTime
                    ? approvalPeriodDC.Span.StartDateTime
                    : invoiceAccountingPeriodSpan.StartDateTime,
                End = approvalPeriodDC.Span.EndDateTime < invoiceAccountingPeriodSpan.EndDateTime
                    ? approvalPeriodDC.Span.EndDateTime
                    : invoiceAccountingPeriodSpan.EndDateTime,
            };

            return sip;
        }

        public virtual void SetCollectiveBillingBaseData(int invoiceCounter, ServiceInvoice invoice, CostBearer costBearer, DateTimeSpan invoicePeriod, Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> supportConcepts2ServiceRecords)
        {
            invoice.InvoiceBase.InvoiceTitle = "Rechnung über Betreuungleistungen";
        }


        private void SetCollectiveBillingServiceInvoicePeriods(ServiceInvoice invoice, DateTimeSpan invoicePeriod, Dictionary<CompactSupportConceptDC, List<ServiceRecordDC>> supportConcepts2ServiceRecords)
        {
            foreach (var supportConcepts2ServiceRecord in supportConcepts2ServiceRecords)
            {
                var c2s = DAOFactory.GenericDAO.LoadByID<CostBearer2SupportConcept>(
                    supportConcepts2ServiceRecord.Key.CostBearerRelOids[0]);

                SetServiceInvoicePeriods(invoice, c2s, invoicePeriod, supportConcepts2ServiceRecord.Value);
            }
        }

        public virtual IList<InvoiceItem> CreateInvoiceItems(List<ServiceRecordDC> serviceRecordsInPeriod, DateTimeSpan invoicePeriod, SupportConceptApprovalPeriod scap, Calculations calc)
        {
            var list = new List<InvoiceItem>();
            foreach (ServiceRecordDC iServiceRecord in serviceRecordsInPeriod)
            {
                if (iServiceRecord.ServiceDescription.Category.IsBillable)
                {
                    if (!iServiceRecord.AlreadyAccounted)
                    {
                        InvoiceItem invoiceItem = CreateInvoiceItem(iServiceRecord, scap, calc);
                        if (invoiceItem != null)
                        {
                            list.Add(invoiceItem);
                        }
                        iServiceRecord.AlreadyAccounted = true;
                    }
                }
            }

            return CreateSummaryInvoiceItems(list, invoicePeriod, scap, calc);
        }

        public virtual IList<InvoiceItem> CreateSummaryInvoiceItems(IList<InvoiceItem> itemList, DateTimeSpan invoicePeriod, SupportConceptApprovalPeriod scap, Calculations calc)
        {
            //Fasse InvoiceItems mit gleichem Stundensatz zusammen:

            var newlist = new List<InvoiceItem>();

            var rateToItem = new Dictionary<String, InvoiceItem>();
            foreach (InvoiceItem iitem in itemList)
            {
                InvoiceItem item;
                String key = String.Format("{0}_{1}", iitem.AmountPerUnit, iitem.RateFactor);

                if (rateToItem.ContainsKey(key))
                {
                    item = rateToItem[key];
                }
                else
                {
                    item = new InvoiceItem();
                    newlist.Add(item);

                    item.AmountPerUnit = iitem.AmountPerUnit;
                    if (invoicePeriod != null)
                    {
                        item.ItemPeriodStart = invoicePeriod.StartDate;
                        item.ItemPeriodEnd = invoicePeriod.EndDate;
                    }
                    else
                    {
                        item.ItemPeriodStart = scap.StartDate;
                        item.ItemPeriodEnd = scap.EndDate;
                    }
                    item.RateFactor = iitem.RateFactor;

                    item.AccountingInterval = iitem.AccountingInterval;
                    item.ItemDescription = iitem.ItemDescription;
                    item.SupportConceptApprovalPeriodOid = scap.Oid;


                    rateToItem[key] = item;
                }


                if (!item.UnitCount.HasValue)
                {
                    item.UnitCount = 0;
                }
                item.UnitCount += iitem.UnitCount ?? 0;
            }


            foreach (InvoiceItem invoiceItem in newlist)
            {
                if (invoiceItem.AmountPerUnit.HasValue)
                {
                    invoiceItem.AmountPerUnit = Math.Round(invoiceItem.AmountPerUnit.Value, 2, MidpointRounding.AwayFromZero);
                }
                else
                {
                    invoiceItem.AmountPerUnit = 0;
                }

                if (invoiceItem.UnitCount.HasValue)
                {
                    invoiceItem.UnitCount = Math.Round(invoiceItem.UnitCount.Value, 2, MidpointRounding.AwayFromZero);
                }
                else
                {
                    invoiceItem.UnitCount = 0;
                }

                invoiceItem.AmountTotal = invoiceItem.AmountPerUnit*invoiceItem.UnitCount;
                invoiceItem.AmountTotal = Math.Round(invoiceItem.AmountTotal.Value, 2, MidpointRounding.AwayFromZero);

                if (invoiceItem.RateFactor.HasValue)
                {
                    invoiceItem.GrossAmountTotal = invoiceItem.AmountTotal*((invoiceItem.RateFactor + 100)/100);
                }
                else
                {
                    invoiceItem.GrossAmountTotal = invoiceItem.AmountTotal;
                }
            }
            return newlist;
        }

        public virtual IList<InvoiceItem> CreateInvoiceItemsForEachServiceRecord(List<ServiceRecordDC> serviceRecordsInPeriod, SupportConceptApprovalPeriod scap, Calculations calc)
        {
            var list = new List<InvoiceItem>();
            foreach (ServiceRecordDC iServiceRecord in serviceRecordsInPeriod)
            {
                if (iServiceRecord.ServiceDescription.Category.IsBillable)
                {
                    if (!iServiceRecord.AlreadyAccounted)
                    {
                        InvoiceItem invoiceItem = CreateInvoiceItem(iServiceRecord, scap, calc);
                        list.Add(invoiceItem);

                        iServiceRecord.AlreadyAccounted = true;
                    }
                }
            }
            return list;
        }

        public virtual InvoiceItem CreateInvoiceItem(ServiceRecordDC iServiceRecord, SupportConceptApprovalPeriod scap, Calculations calc)
        {
            var invoiceItem = new InvoiceItem();

            BillingData bd = calc.GetBillingData(iServiceRecord);

            invoiceItem.AmountPerUnit = bd.AmountPerUnit;
            invoiceItem.UnitCount = bd.UnitCount;
            invoiceItem.AmountTotal = bd.AmountTotal;
            invoiceItem.ItemPeriodStart = bd.BillingPeriodStart;
            invoiceItem.ItemPeriodEnd = bd.BillingPeriodEnd;
            invoiceItem.RateFactor = bd.RateFactor;
            invoiceItem.GrossAmountTotal = bd.GrossAmountTotal;
            invoiceItem.AccountingInterval = bd.AccountingInterval;
            invoiceItem.ItemDescription = iServiceRecord.ServiceDescription.Name; //iServiceRecord.Employee + " - " + iServiceRecord.ServiceDescription.Name;
            invoiceItem.ServiceRecord = DAOFactory.GenericDAO.LoadByID<ServiceRecord>(iServiceRecord.ServiceRecordOid.Value);
            if (scap != null)
                invoiceItem.SupportConceptApprovalPeriodOid = scap.Oid;

            //var crs = iServiceRecord.CostBearer.CostRatePeriods;

            //List<long> costRatePeriodOids = new List<long>();

            //if (cdc != null)
            //    costRatePeriodOids.Add(cdc.CostRatePeriodOid.Value);
            //cdc = crs.GetCostRatePeriodForDate(CostRatePeriodType.MinutesIntervall, iServiceRecord.Start.Value);
            //if (cdc != null)
            //    costRatePeriodOids.Add(cdc.CostRatePeriodOid.Value);
            //cdc = crs.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, iServiceRecord.Start.Value);
            //if (cdc != null)
            //    costRatePeriodOids.Add(cdc.CostRatePeriodOid.Value);
            //cdc = crs.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, iServiceRecord.Start.Value);
            //if (cdc != null)
            //    costRatePeriodOids.Add(cdc.CostRatePeriodOid.Value);
            //cdc = crs.GetCostRatePeriodForDate(CostRatePeriodType.ServiceTolerance, iServiceRecord.Start.Value);
            //if (cdc != null)
            //    costRatePeriodOids.Add(cdc.CostRatePeriodOid.Value);

            //invoiceItem.CostRatePeriodList = DAOFactory.GenericDAO.LoadByIDs<CostRatePeriod>(costRatePeriodOids);

            //var rateFactor = invoiceItem.CostRatePeriodList.SingleOrDefault(i => i.CostRateType == CostRatePeriodType.RateFactor);
            //if (rateFactor != null && !iServiceRecord.CostBearer.IsCalculatingWithFactor)
            //{
            //    invoiceItem.RateFactor = rateFactor.CostRateValue;
            //    invoiceItem.GrossAmountTotal = invoiceItem.AmountTotal
            //                                   + (invoiceItem.AmountTotal * rateFactor.CostRateValue / 100);
            //}
            //else
            //{
            //    invoiceItem.GrossAmountTotal = invoiceItem.AmountTotal;
            //}

            return invoiceItem;
        }


        // public virtual void RemoveOrCropInvoiceItemsNotBillable(ServiceInvoicePeriod iPeriod)
        // {
        // this.RemoveOrCropInvoiceItemsNotBillableDueToAbsenceTimes(iPeriod);
        // this.RemoveOrCropInvoiceItemsNotBillableDueToMoreThanApproved(iPeriod);
        // }

        // public virtual void RemoveOrCropInvoiceItemsNotBillableDueToAbsenceTimes(ServiceInvoicePeriod iPeriod)
        // {
        // if (iPeriod.ApprovedFixedAmount.HasValue)
        // {
        // return;
        // }

        // var absenceTimes = DAOFactory.GenericDAO.LoadByID<SupportConceptApprovalPeriod>(iPeriod.SupportConceptApprovalPeriodOid.Value)
        // .CostBearer2SupportConcept.SupportConcept.Customer.GetAbsencesTimesNotBillableWholeWeeks();

        // foreach (var iInvoiceItem in iPeriod.InvoiceItemList.ToList())
        // {
        // var absenceWeek = absenceTimes.SingleOrDefault(i => iInvoiceItem.ItemPeriodStart.Value.InBetween(i.Key)).Key;

        // if (absenceWeek != null)
        // {
        // if (absenceTimes[absenceWeek] == 0)
        // {
        // iPeriod.InvoiceItemList.Remove(iInvoiceItem);
        // iPeriod.HoursNotBillableAbsence += iInvoiceItem.UnitCount.Value;
        // }
        // else
        // {
        // var minutes = iInvoiceItem.UnitCount.Value * 60m;

        // if (absenceTimes[absenceWeek] - minutes >= 0)
        // {
        // absenceTimes[absenceWeek] -= minutes;
        // }
        // else
        // {
        // iPeriod.HoursNotBillableAbsence += (minutes - absenceTimes[absenceWeek]) / 60;

        // iInvoiceItem.UnitCount = absenceTimes[absenceWeek] / 60m;
        // iInvoiceItem.ItemPeriodEnd = iInvoiceItem.ItemPeriodStart.Value.AddHours(Convert.ToDouble(iInvoiceItem.UnitCount));
        // iInvoiceItem.AmountTotal = iInvoiceItem.AmountPerUnit * iInvoiceItem.UnitCount;

        // if (iInvoiceItem.RateFactor != null)
        // {
        // iInvoiceItem.GrossAmountTotal = iInvoiceItem.AmountTotal + (iInvoiceItem.AmountTotal * iInvoiceItem.RateFactor.Value / 100);
        // }
        // else
        // {
        // iInvoiceItem.GrossAmountTotal = iInvoiceItem.AmountTotal;
        // }

        // absenceTimes[absenceWeek] = 0;
        // }
        // }
        // }
        // }
        // }

        // public virtual void RemoveOrCropInvoiceItemsNotBillableDueToMoreThanApproved(ServiceInvoicePeriod iPeriod)
        // {
        // if (iPeriod.ApprovedFixedAmount.HasValue)
        // {
        // return;
        // }

        // var approvedAmountCounter = iPeriod.ApprovedAmountDefaultHourlyRate;

        // foreach (var iInvoiceItem in iPeriod.InvoiceItemList.ToList())
        // {
        // if (approvedAmountCounter - iInvoiceItem.GrossAmountTotal >= 0)
        // {
        // approvedAmountCounter -= iInvoiceItem.GrossAmountTotal;
        // }
        // else if (approvedAmountCounter == 0)
        // {
        // iPeriod.InvoiceItemList.Remove(iInvoiceItem);
        // iPeriod.HoursNotBillableNotApproved += iInvoiceItem.UnitCount.Value;
        // }
        // else
        // {
        // iInvoiceItem.GrossAmountTotal = approvedAmountCounter;

        // if (iInvoiceItem.RateFactor != null)
        // {
        // iInvoiceItem.AmountTotal = (iInvoiceItem.GrossAmountTotal / (100 + iInvoiceItem.RateFactor.Value)) * 100;
        // }
        // else
        // {
        // iInvoiceItem.AmountTotal = iInvoiceItem.GrossAmountTotal;
        // }

        // var oldUnitCount = iInvoiceItem.UnitCount.Value;
        // iInvoiceItem.UnitCount = iInvoiceItem.AmountTotal / iInvoiceItem.AmountPerUnit;
        // iPeriod.HoursNotBillableNotApproved += oldUnitCount - iInvoiceItem.UnitCount.Value;
        // iInvoiceItem.ItemPeriodEnd = iInvoiceItem.ItemPeriodStart.Value.AddHours(Convert.ToDouble(iInvoiceItem.UnitCount));

        // approvedAmountCounter = 0;
        // }
        // }
        // }
    }
}