Esempio n. 1
0
        public static CashRequestModel Create(DecisionHistoryDBModel item)
        {
            var setupFeeCalculator = new SetupFeeCalculator(item.ManualSetupFeePercent, item.BrokerSetupFeePercent);
            CashRequestOriginator originator;
            string originatorStr = item.Originator;

            if (Enum.TryParse(item.Originator, out originator))
            {
                originatorStr = originator.DescriptionAttr();
            }

            return(new CashRequestModel
            {
                Id = item.CashRequestID,
                Action = item.Action,
                Amount = item.ApprovedSum,
                StartDate = item.OfferStart,
                EndDate = item.OfferValidUntil,
                Comments = item.Comment,
                InterestRate = item.InterestRate,
                SetupFee = setupFeeCalculator.Calculate(item.ApprovedSum).Total,
                RepaymentPeriod = item.ApprovedRepaymentPeriod,
                UnderwriterDecision = item.UnderwriterDecision,
                LoanType = item.LoanType,
                DiscountPlan = item.DiscountPlan,
                LoanSourceName = item.LoanSourceName,
                Originator = originatorStr,
                IsOpenPlatform = string.IsNullOrEmpty(item.FundingType) ? "No" : "Yes"
            });
        }
Esempio n. 2
0
        public static DecisionHistoryModel Create(DecisionHistoryDBModel item)
        {
            CashRequestOriginator originator;
            string originatorStr = item.Originator;

            if (Enum.TryParse(item.Originator, out originator))
            {
                originatorStr = originator.DescriptionAttr();
            }

            var fees = new SetupFeeCalculator(item.ManualSetupFeePercent, item.BrokerSetupFeePercent)
                       .Calculate(item.ApprovedSum);

            return(new DecisionHistoryModel {
                Id = item.DecisionHistoryID,
                Action = item.Action,
                Comment = item.Comment,
                Date = item.Date,
                UnderwriterName = item.UnderwriterName,
                LoanType = item.LoanType,
                DiscountPlan = item.LoanType,
                LoanSourceName = item.LoanSourceName,
                RepaymentPeriod = item.RepaymentPeriod,
                InterestRate = item.InterestRate,
                ApprovedSum = item.ApprovedSum,
                IsLoanTypeSelectionAllowed = item.IsLoanTypeSelectionAllowed,
                Originator = originatorStr,
                TotalSetupFee = fees.Total,
                BrokerSetupFee = fees.Broker,
                IsOpenPlatform = string.IsNullOrEmpty(item.FundingType) ? "No" : "Yes"
            });
        } // Create
Esempio n. 3
0
        }         // BuildSuggestedAmountModel

        private void BuildSetupFee()
        {
            var fees = new SetupFeeCalculator(Result.ManualSetupFeePercent, Result.BrokerSetupFeePercent)
                       .Calculate(Result.OfferedCreditLine);

            Result.TotalSetupFee  = fees.Total;
            Result.BrokerSetupFee = fees.Broker;
        }         // BuildSetupFee
Esempio n. 4
0
        }         // class CashRequestRelevantData

        protected override void SetTemplateAndVariables()
        {
            var cashRequestRelevantData = DB.FillFirst <CashRequestRelevantData>(
                "GetCashRequestData",
                new QueryParameter("@CustomerId", CustomerData.Id)
                );


            SetupFeeCalculator sfCalculator = new SetupFeeCalculator(cashRequestRelevantData.ManualSetupFeePercent, cashRequestRelevantData.BrokerSetupFeePercent);
            var setupFeeAmount = sfCalculator.Calculate(cashRequestRelevantData.ManagerApprovedSum).Total;

            this.setupFeePercents     = (cashRequestRelevantData.ManualSetupFeePercent + cashRequestRelevantData.BrokerSetupFeePercent) * 100;
            this.interestRatePercents = cashRequestRelevantData.InterestRate * 100;
            this.setupFeePercents     = MathUtils.Round2DecimalDown(this.setupFeePercents);
            decimal remainingPercentsAfterSetupFee = 100 - this.setupFeePercents;

            Variables = new Dictionary <string, string> {
                { "FirstName", CustomerData.FirstName },
                { "LoanAmount", this.loanAmount.ToString("#,#") },
                { "ValidFor", this.validHours.ToString(CultureInfo.InvariantCulture) },
                { "AmountInUsd", MathUtils.Round2DecimalDown(this.amountInUsd).ToString("#,#.00") },
                { "AlibabaId", CustomerData.AlibabaId.ToString(CultureInfo.InvariantCulture) },
                { "InterestRate", MathUtils.Round2DecimalDown(this.interestRatePercents).ToString("#,#.00") },
                { "SetupFee", this.setupFeePercents.ToString("#,#.00") },
                { "RemainingPercentsAfterSetupFee", remainingPercentsAfterSetupFee.ToString(CultureInfo.InvariantCulture) },
                { "RefNum", CustomerData.RefNum.ToString(CultureInfo.InvariantCulture) },
                { "Surname", CustomerData.Surname.ToString(CultureInfo.InvariantCulture) },
                { "RequestedLoanAmount", CustomerData.RequestedLoanAmount.ToString("#,#") },
                { "ReportedAnnualTurnover", CustomerData.ReportedAnnualTurnover.ToString("#,#") }
            };

            if (CustomerData.IsAlibaba)
            {
                TemplateName = "Mandrill - Alibaba - Approval";
            }
            else if (CustomerData.IsCampaign)
            {
                TemplateName = "Mandrill - Approval Campaign (1st time)";
            }
            else if (this.isFirst)
            {
                TemplateName = "Mandrill - Approval (1st time)";
            }
            else
            {
                TemplateName = "Mandrill - Approval (not 1st time)";
            }
        }         // SetTemplateAndVariables
Esempio n. 5
0
        public RedirectResult GetTransactionId(decimal loan_amount, int loanType, int repaymentPeriod)
        {
            Customer customer = this.context.Customer;

            CheckCustomerStatus(customer);

            if (loan_amount < 0)
            {
                loan_amount = (int)Math.Floor(customer.CreditSum ?? 0);
            }

            var cr = customer.LastCashRequest;

            PayPointFacade payPointFacade = new PayPointFacade(customer.MinOpenLoanDate(), customer.CustomerOrigin.Name);

            if (customer.IsLoanTypeSelectionAllowed == 1)
            {
                var oDBHelper = ObjectFactory.GetInstance <IDatabaseDataHelper>() as DatabaseDataHelper;
                cr.RepaymentPeriod = repaymentPeriod;
                cr.LoanType        = oDBHelper.LoanTypeRepository.Get(loanType);
            } // if

            DateTime cardMinExpiryDate = DateTime.UtcNow.AddMonths(payPointFacade.PayPointAccount.CardExpiryMonths);

            var fee = new SetupFeeCalculator(cr.ManualSetupFeePercent, cr.BrokerSetupFeePercent).Calculate(loan_amount).Total;

            string callback = Url.Action("PayPointCallback", "GetCash",
                                         new {
                Area = "Customer",
                loan_amount,
                fee,
                username          = this.context.User.Name,
                cardMinExpiryDate = FormattingUtils.FormatDateToString(cardMinExpiryDate),
                origin            = customer.CustomerOrigin.Name
            },
                                         "https");

            string url = payPointFacade.GeneratePaymentUrl(customer, 5.00m, callback);

            this.logRepository.Log(this.context.UserId, DateTime.Now, "Paypoint GetCash Redirect to " + url, "Successful", "");
            return(Redirect(url));
        }
Esempio n. 6
0
		/**
			- loan
			- fees
			- history
			- schedules
			- broker comissions ? - update NLLoanID
			- fund transfer
			- pacnet transaction
			- agreements
			- loan options row
			*/
		public override void Execute() {

			if (!CurrentValues.Instance.NewLoanRun) {
				NL_AddLog(LogType.Info, "NL disabled by configuration", null, null, null, null);
				return;
			}

			NL_AddLog(LogType.Info, "Strategy Start", this.strategyArgs, Error, null, null);

			try {
				if (model.CustomerID == 0) {
					Error = NL_ExceptionCustomerNotFound.DefaultMessage;
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				if (model.Loan == null) {
					Error = NL_ExceptionRequiredDataNotFound.Loan;
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				if (model.Loan.OldLoanID == null) {
					Error = NL_ExceptionRequiredDataNotFound.OldLoan;
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				var history = model.Loan.LastHistory();

				if (history == null) {
					Error = NL_ExceptionRequiredDataNotFound.LastHistory;
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				if (history.Agreements == null || history.Agreements.Count == 0) {
					Error = string.Format("Expected input data not found (NL_Model initialized by: NLAgreementItem list). Customer {0}", model.CustomerID);
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				if (string.IsNullOrEmpty(history.AgreementModel)) {
					Error = string.Format("Expected input data not found (NL_Model initialized by: AgreementModel in JSON). Customer {0}", model.CustomerID);
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				BuildLoanFromOffer dataForLoan = new BuildLoanFromOffer(model);
				try {
					dataForLoan.Execute();
					// ReSharper disable once CatchAllClause
				} catch (Exception ex) {
					Log.Alert(ex.Message);
				}

				if (!string.IsNullOrEmpty(dataForLoan.Error)) {
					Error = dataForLoan.Error;
					NL_AddLog(LogType.DataExsistense, "Strategy failed - Failed to generate Schedule/fees", this.strategyArgs, null, Error, null);
					return;
				}

				model = dataForLoan.Result;

				// prevent to create the same loan (by refnum)
				if (!string.IsNullOrEmpty(model.Loan.Refnum) && !string.IsNullOrEmpty(dataForLoan.DataForLoan.ExistsRefnums) && dataForLoan.DataForLoan.ExistsRefnums.Contains(model.Loan.Refnum)) {
					Error = NL_ExceptionLoanExists.DefaultMessage;
					NL_AddLog(LogType.Info, "Strategy End", this.strategyArgs, null, Error, null);
					return;
				}

				// setup/distributed fees

				// for now: only one-time or "spreaded" setup fees supported
				// add full fees 2.0 support later

				var offerFees = model.Offer.OfferFees;

				// don't create LoanFees if OfferFees Percent == 0 or AbsoluteAmount == 0
				var setupFee = offerFees.FirstOrDefault(f => f.LoanFeeTypeID == (int)NLFeeTypes.SetupFee && (f.Percent > 0 || f.AbsoluteAmount > 0));
				var servicingFee = offerFees.FirstOrDefault(f => f.LoanFeeTypeID == (int)NLFeeTypes.ServicingFee && (f.Percent > 0 || f.AbsoluteAmount > 0)); // equal to "setup spreaded"
				decimal? brokerFeePercent = model.Offer.BrokerSetupFeePercent;
			 
				var feeCalculator = new SetupFeeCalculator(setupFee!=null ?setupFee.Percent: servicingFee.Percent, brokerFeePercent);
				SetupFeeCalculator.AbsoluteFeeAmount ff = feeCalculator.Calculate(history.Amount);
				decimal setupFeeAmount = ff.Total;
				model.BrokerComissions = ff.Broker;

				// send ot Calculator to distribute and attach to schedule planned dates
				history.DistributedFees = servicingFee == null ? 0: setupFeeAmount;
				
				ALoanCalculator nlCalculator = new LegacyLoanCalculator(model);
				// 2. Init Schedule and Fees
				try {
					// model should contain Schedule and Fees after this invocation
					nlCalculator.CreateSchedule(); // create primary dates/p/r/f distribution of schedules (P/n) and setup/servicing fees. 7 September - fully completed schedule + fee + amounts due, without payments.
				} catch (NoInitialDataException noDataException) {
					Error = noDataException.Message;
				} catch (InvalidInitialAmountException amountException) {
					Error = amountException.Message;
				} catch (InvalidInitialInterestRateException interestRateException) {
					Error = interestRateException.Message;
				} catch (InvalidInitialRepaymentCountException paymentsException) {
					Error = paymentsException.Message;
					// ReSharper disable once CatchAllClause
				} catch (Exception ex) {
					Error = string.Format("Failed to get calculator instance/Schedule. customer {0}, err: {1}", model.CustomerID, ex.Message);
					NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, null, Error, ex.StackTrace);
					return;
				}

				//// prevent creation of same loan
				//if (!string.IsNullOrEmpty(Error)) {
				//	Log.Info("Failed to calculate Schedule. customer {0}, err: {1}", model.CustomerID, Error);
				//	NL_AddLog(LogType.Error, "Strategy " + string.Format("Failed to calculate Schedule. customer {0}, err: {1}", model.CustomerID, Error), this.strategyArgs, null, Error, null);
				//	return;
				//}

				history.OutstandingInterest = nlCalculator.Interest;

				List<NL_LoanSchedules> nlSchedule = new List<NL_LoanSchedules>();
				List<NL_LoanFees> nlFees = new List<NL_LoanFees>();
				List<NL_LoanAgreements> nlAgreements = new List<NL_LoanAgreements>();

				// get updated history filled with Schedule
				history = model.Loan.LastHistory();

				// copy to local schedules list
				history.Schedule.ForEach(s => nlSchedule.Add(s));

				if (nlSchedule.Count == 0) {
					Error += "Failed to generate Schedule/fees";
					NL_AddLog(LogType.Info, "Strategy failed", this.strategyArgs, null, Error, null);
					return;
				}

				// 3. complete NL_Loans object data
				model.Loan = dataForLoan.Result.Loan;
				model.Loan.CreationTime = nowTime;
				model.Loan.LoanStatusID = (int)NLLoanStatuses.Live;
				model.Loan.Position += 1;
				
				ConnectionWrapper pconn = DB.GetPersistent();

				try {

					pconn.BeginTransaction();

					// 4. save loan
					LoanID = DB.ExecuteScalar<long>(pconn, "NL_LoansSave", CommandSpecies.StoredProcedure, DB.CreateTableParameter("Tbl", model.Loan));
					model.Loan.LoanID = LoanID;

					//Log.Debug("NL_LoansSave: LoanID: {0}", this.LoanID);

					// 5. fees
					// copy to local fees list
					model.Loan.Fees.ForEach(f => nlFees.Add(f));

					foreach (NL_LoanFees f in nlFees) {
						f.CreatedTime = nowTime; // from calc-r
						f.AssignedByUserID = 1; //  from calc-r
						f.LoanID = LoanID;
					}

					// setup as fee
					if (setupFee != null) {
						Log.Debug("setupFeeAmount: {0}", setupFeeAmount);
						nlFees.Add(
							new NL_LoanFees() {
								LoanID = LoanID,
								Amount = setupFeeAmount,
								AssignTime = history.EventTime,
								Notes = "setup fee one-part",
								LoanFeeTypeID = (int)NLFeeTypes.SetupFee,
								CreatedTime = nowTime,
								AssignedByUserID = 1
							});
					}

					nlFees.ForEach(f => Log.Debug("Adding fees: {0}", f));

					// insert fees
					DB.ExecuteNonQuery(pconn, "NL_LoanFeesSave", CommandSpecies.StoredProcedure, DB.CreateTableParameter<NL_LoanFees>("Tbl", nlFees));

					model.Loan.Fees.Clear();
					model.Loan.Fees.AddRange(nlFees);

					// 7. history
					history.LoanID = LoanID;
					history.Description = "adding loan. oldID: " + model.Loan.OldLoanID;

					//Log.Debug("Adding history: {0}", history);

					history.LoanHistoryID = DB.ExecuteScalar<long>(pconn, "NL_LoanHistorySave", CommandSpecies.StoredProcedure, DB.CreateTableParameter("Tbl", history));

					//Log.Debug("NL_LoanHistorySave: LoanID: {0}, LoanHistoryID: {1}", model.Loan.LoanID, history.LoanHistoryID);

					// 8. loan agreements
					history.Agreements.ForEach(a => nlAgreements.Add(a));
					nlAgreements.ForEach(a => a.LoanHistoryID = history.LoanHistoryID);

					//nlAgreements.ForEach(a => Log.Debug("Adding agreement: {0}", a));

					DB.ExecuteNonQuery(pconn, "NL_LoanAgreementsSave", CommandSpecies.StoredProcedure, DB.CreateTableParameter<NL_LoanAgreements>("Tbl", nlAgreements));

					// 9. schedules 
					nlSchedule.ForEach(s => s.LoanHistoryID = history.LoanHistoryID);

					//nlSchedule.ForEach(s => Log.Debug("Adding schedule: {0}", s));

					DB.ExecuteNonQuery(pconn, "NL_LoanSchedulesSave", CommandSpecies.StoredProcedure, DB.CreateTableParameter<NL_LoanSchedules>("Tbl", nlSchedule));

					// 10. Fund Transfer 
					if (model.FundTransfer != null) {
						model.FundTransfer.LoanID = LoanID;
						model.FundTransfer.FundTransferID = DB.ExecuteScalar<long>(pconn, "NL_FundTransfersSave", CommandSpecies.StoredProcedure, DB.CreateTableParameter("Tbl", model.FundTransfer));
						//Log.Debug("NL_FundTransfersSave: LoanID: {0}, fundTransferID: {1}", this.LoanID, model.FundTransfer.FundTransferID);
					}

					// 11. save default loan options record
					model.Loan.LoanOptions.LoanOptionsID = DB.ExecuteScalar<long>(pconn, "NL_LoanOptionsSave",
					   CommandSpecies.StoredProcedure, DB.CreateTableParameter("Tbl", new NL_LoanOptions {
						   LoanID = LoanID,
						   UserID = 1, // default system user?
						   InsertDate = nowTime,
						   IsActive = true,
						   Notes = "default options"
					   }), new QueryParameter("@LoanID", LoanID)
					 );

					pconn.Commit();

					// ReSharper disable once CatchAllClause
				} catch (Exception ex) {

					pconn.Rollback();

					LoanID = 0;
					Error = ex.Message;
					Log.Error("Failed to add new loan: {0}", Error);

					SendMail("NL: loan rolled back", history, nlFees, nlSchedule, nlAgreements);

					NL_AddLog(LogType.Error, "Strategy failed - Failed to add new loan", this.strategyArgs, Error, ex.ToString(), ex.StackTrace);
					return;
				}

				// 7. Pacnet transaction
				try {
					if (model.FundTransfer != null && (model.FundTransfer.PacnetTransactions.Count > 0 && model.FundTransfer.FundTransferID > 0)) {

						var pacnetTransaction = model.FundTransfer.LastPacnetTransactions();
						pacnetTransaction.FundTransferID = model.FundTransfer.FundTransferID;
						pacnetTransaction.PacnetTransactionID = DB.ExecuteScalar<long>("NL_PacnetTransactionsSave", CommandSpecies.StoredProcedure, DB.CreateTableParameter("Tbl", pacnetTransaction));

						//Log.Debug("NL_PacnetTransactionsSave: LoanID: {0}, pacnetTransactionID: {1}", this.LoanID, pacnetTransaction.PacnetTransactionID);
					}
					// ReSharper disable once CatchAllClause
				} catch (Exception e1) {

					Error = e1.Message;
					Log.Error("Failed to save PacnetTransaction: {0}", Error);

					// PacnetTransaction error
					SendMail("NL: Failed to save PacnetTransaction", history, nlFees, nlSchedule, nlAgreements);
				}

				// 11. if setup fee - add payment to offset it
				SetupOffsetPayment();
				
				// 6. broker commissions
				// done in controller. When old loan removed: check if this is the broker's customer, calc broker fees, insert into LoanBrokerCommission
				if (model.Offer.BrokerSetupFeePercent > 0) {
					DB.ExecuteNonQuery(string.Format("UPDATE dbo.LoanBrokerCommission SET NLLoanID = {0} WHERE LoanID = {1}", LoanID, model.Loan.OldLoanID));
				}

				// OK
				SendMail("NL: Saved successfully", history, nlFees, nlSchedule, nlAgreements);

				// copy LoanCharges Ids into OldFeeID, NL_LoanFees
				DB.ExecuteNonQuery("NL_LoanFeesOldIDUpdate", CommandSpecies.StoredProcedure);

				// temporary - should be removed/modified after "old" loan remove
				CopyRebateTransaction();

				//MigrateLoanTransaction sMigrateLoan = new MigrateLoanTransaction();
				//try {
				//	sMigrateLoan.Execute();
				//	// ReSharper disable once CatchAllClause
				//} catch (Exception mex) {
				//	Error = mex.Message;
				//	Log.Error("Failed sync migration: {0}", Error);
				//	NL_AddLog(LogType.Error, "Failed sync migration", this.strategyArgs, Error, mex.ToString(), mex.StackTrace);
				//}

				NL_AddLog(LogType.Info, "Strategy End", this.strategyArgs, LoanID, Error, null);

				// ReSharper disable once CatchAllClause
			} catch (Exception ex) {
				NL_AddLog(LogType.Error, "Strategy failed", this.strategyArgs, Error, ex.ToString(), ex.StackTrace);
			}
		}//Execute
Esempio n. 7
0
		} // CreateNewLoan

		public BuiltLoan BuildLoan(CashRequest cr, decimal amount, DateTime now, int term, int interestOnlyTerm = 0) {
			decimal setupFeePct = cr.ManualSetupFeePercent ?? 0;
			decimal brokerFeePct = cr.BrokerSetupFeePercent ?? 0;

			decimal approvedAmount = (decimal)(cr.ManagerApprovedSum ?? cr.SystemCalculatedSum ?? 0);

			if ((cr.Customer.Broker != null) && (approvedAmount != amount)) {
				log.Debug(
					"CreateNewLoan: broker customer '{0}', broker fee in cash request with approved amount {1} is {2}.",
					cr.Customer.Stringify(),
					approvedAmount.ToString("C2"),
					brokerFeePct.ToString("P2")
				);

				Loan firstLoan = cr.Customer.Loans.OrderBy(l => l.Date).FirstOrDefault();

				brokerFeePct = new CommissionCalculator(amount, firstLoan == null ? (DateTime?)null : firstLoan.Date)
					.Calculate()
					.BrokerCommission;

				log.Debug(
					"CreateNewLoan: broker customer '{0}', broker fee adjusted to loan amount {1} is {2}.",
					cr.Customer.Stringify(),
					amount.ToString("C2"),
					brokerFeePct.ToString("P2")
				);
			} // if broker customer

			var fees = new SetupFeeCalculator(setupFeePct, brokerFeePct).Calculate(amount);

			decimal setupFee = fees.Total;
			decimal brokerFee = fees.Broker;

			var calculator = new LoanScheduleCalculator { Interest = cr.InterestRate, Term = term };

			LoanLegal loanLegal = cr.LoanLegals.LastOrDefault();

			var loan = new Loan {
				LoanAmount = amount,
				Date = now,
				LoanType = cr.LoanType,
				CashRequest = cr,
				SetupFee = setupFee,
				LoanLegalId = loanLegal == null ? (int?)null : loanLegal.Id
			};

			calculator.Calculate(amount, loan, loan.Date, interestOnlyTerm, cr.SpreadSetupFee());

			loan.LoanSource = cr.LoanSource;

			if (brokerFee > 0 && cr.Customer.Broker != null) {
				loan.BrokerCommissions.Add(new LoanBrokerCommission {
					Broker = cr.Customer.Broker,
					CardInfo = cr.Customer.Broker.BankAccounts.FirstOrDefault(
						x => x.IsDefault.HasValue && x.IsDefault.Value
					),
					CommissionAmount = brokerFee,
					CreateDate = now,
					Loan = loan,
				});
			} // if broker fee & broker

			return new BuiltLoan {
				Loan = loan,
				BrokerFeePercent = brokerFeePct,
				ManualSetupFeePercent = setupFeePct,
			};
		} // BuildLoan
Esempio n. 8
0
        }         // constructor

        /// <exception cref="OverflowException">Condition. </exception>
        public virtual void Execute()
        {
            // not accepted rollover
            if (Calculator.acceptedRollover.Rollover == null)
            {
                Log.Alert("RolloverRescheduling: no accepted rollover");
                return;
            }

            // not accepted rollover
            if (!Calculator.acceptedRollover.Rollover.IsAccepted || !Calculator.acceptedRollover.Rollover.CustomerActionTime.HasValue)
            {
                Log.Alert("RolloverRescheduling: rollover not accepted (paid). {0}", Calculator.acceptedRollover.Rollover);
                return;
            }

            // rollover proceeseed
            if (Calculator.acceptedRollover.Rollover.CustomerActionTime.Value.Date == Calculator.currentHistory.EventTime.Date || Calculator.acceptedRolloverProcessed)
            {
                Log.Debug("RolloverRescheduling: History ({0}) for rollover {1:d}, rolloverID {2} already exists", Calculator.currentHistory.LoanHistoryID, Calculator.acceptedRollover.Rollover.CustomerActionTime.Value, Calculator.acceptedRollover.Rollover.LoanRolloverID);
                return;
            }

            var rolloverPayment = Calculator.events.FirstOrDefault(p => p.Payment != null && p.Payment.PaymentTime.Date.Equals(Calculator.acceptedRollover.Rollover.CustomerActionTime.Value.Date) && p.Payment.PaymentDestination.Equals(NLPaymentDestinations.Rollover.ToString()));

            // rollover not paid
            if (rolloverPayment == null)
            {
                Log.Alert("RolloverRescheduling: rollover payment not found. {0}", Calculator.acceptedRollover.Rollover);
                return;
            }

            DateTime acceptionTime = Calculator.acceptedRollover.Rollover.CustomerActionTime.Value.Date;

            NL_LoanHistory lastHistory = WorkingModel.Loan.LastHistory();

            // 1. create new history
            NL_LoanHistory newHistory = new NL_LoanHistory {
                LoanID                  = lastHistory.LoanID,
                LoanLegalID             = lastHistory.LoanLegalID,
                AgreementModel          = lastHistory.AgreementModel,
                Agreements              = lastHistory.Agreements,
                InterestRate            = lastHistory.InterestRate,
                RepaymentIntervalTypeID = lastHistory.RepaymentIntervalTypeID,
                UserID                  = WorkingModel.CustomerID,
                Description             = "accept rollover",
                Amount                  = Calculator.currentOpenPrincipal,
                EventTime               = acceptionTime
            };

            RepaymentIntervalTypes intervalType = (RepaymentIntervalTypes)lastHistory.RepaymentIntervalTypeID;

            int removedItems = 0;

            newHistory.RepaymentDate = DateTime.MinValue;

            // 2. mark removed schedules + add new schedules
            foreach (NL_LoanSchedules s in Calculator.schedule.Where(s => s.PlannedDate >= acceptionTime))
            {
                s.SetStatusOnRescheduling();
                s.ClosedTime = acceptionTime;

                removedItems++;

                DateTime plannedDate = Calculator.AddRepaymentIntervals(1, s.PlannedDate, intervalType);

                if (newHistory.RepaymentDate.Equals(DateTime.MinValue))
                {
                    newHistory.RepaymentDate = plannedDate;
                }

                // add new schedule instead of removed
                NL_LoanSchedules newSchedule = new NL_LoanSchedules()
                {
                    LoanScheduleID       = 0,
                    LoanScheduleStatusID = (int)NLScheduleStatuses.StillToPay,
                    Position             = s.Position,
                    PlannedDate          = plannedDate,
                    Principal            = s.Principal,          // (s.Principal - s.PrincipalPaid),
                    InterestRate         = s.InterestRate,
                    TwoDaysDueMailSent   = false,                //s.TwoDaysDueMailSent,
                    FiveDaysDueMailSent  = false,                //s.FiveDaysDueMailSent
                };

                Log.Debug("schedule {0} replaced by {1}", s, newSchedule);

                newHistory.Schedule.Add(newSchedule);
            }

            newHistory.RepaymentCount = removedItems;

            WorkingModel.Loan.Histories.Add(newHistory);

            //List<NL_LoanFees> replacedDistributedFees = new List<NL_LoanFees>();

            bool distributedFees = false;

            // 3. mark removed distributed fees + add new distributed fees
            foreach (NL_LoanFees fee in Calculator.distributedFeesList.Where(f => f.AssignTime.Date >= acceptionTime))
            {
                fee.DisabledTime    = acceptionTime;
                fee.Notes           = "disabled on rollover";
                fee.DeletedByUserID = WorkingModel.UserID ?? 1;

                distributedFees = true;

                //NL_LoanFees newFee = new NL_LoanFees() {
                //	LoanFeeID = 0,
                //	Amount = fee.Amount,
                //	AssignTime = Calculator.AddRepaymentIntervals(1, fee.AssignTime, intervalType),
                //	LoanID = WorkingModel.Loan.LoanID,
                //	LoanFeeTypeID = fee.LoanFeeTypeID,
                //	AssignedByUserID = fee.AssignedByUserID,
                //	CreatedTime = acceptionTime,
                //	Notes = fee.Notes
                //};

                //Log.Debug("fee {0} replaced by {1}", fee, newFee);

                //replacedDistributedFees.Add(newFee);
            }

            //Calculator.distributedFeesList.AddRange(replacedDistributedFees);
            //WorkingModel.Loan.Fees.AddRange(replacedDistributedFees);

            if (distributedFees)
            {
                // offer-fees
                NL_OfferFees offerFees = WorkingModel.Offer.OfferFees.FirstOrDefault();

                if (offerFees != null && offerFees.DistributedPartPercent != null && (decimal)offerFees.DistributedPartPercent == 1)
                {
                    var     feeCalculator          = new SetupFeeCalculator(offerFees.Percent, null);
                    decimal servicingFeeAmount     = feeCalculator.Calculate(newHistory.Amount).Total;
                    decimal servicingFeePaidAmount = WorkingModel.Loan.Fees.Where(f => f.LoanFeeTypeID == (int)NLFeeTypes.ServicingFee).Sum(f => f.PaidAmount);

                    Log.Debug("servicingFeeAmount: {0}, servicingFeePaidAmount: {1}", servicingFeeAmount, servicingFeePaidAmount);                     // new "spreaded" amount

                    Calculator.AttachDistributedFeesToLoanBySchedule(WorkingModel, (servicingFeeAmount - servicingFeePaidAmount), acceptionTime);
                }
            }

            // TODO could be reseted at all??????????????
            // reset paid amount for deleted/closed schedules and disabled distributed fees
            foreach (NL_Payments p in WorkingModel.Loan.Payments)
            {
                foreach (NL_LoanSchedulePayments sp in p.SchedulePayments)
                {
                    foreach (NL_LoanSchedules s in Calculator.schedule.Where(s => s.IsDeleted()))
                    {
                        if (s.LoanScheduleID == sp.LoanScheduleID)
                        {
                            sp.ResetInterestPaid  = sp.PrincipalPaid;
                            sp.ResetPrincipalPaid = sp.PrincipalPaid;

                            sp.PrincipalPaid = 0;
                            sp.InterestPaid  = 0;
                        }
                    }
                }

                foreach (NL_LoanFeePayments fp in p.FeePayments)
                {
                    foreach (NL_LoanFees f in Calculator.distributedFeesList.Where(f => f.DisabledTime.Equals(acceptionTime)))
                    {
                        if (f.LoanFeeID == fp.LoanFeeID)
                        {
                            fp.ResetAmount = fp.Amount;
                            fp.Amount      = 0;

                            f.PaidAmount -= (decimal)fp.ResetAmount;
                        }
                    }
                }
            }

            Calculator.acceptedRolloverProcessed = true;
        }