You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by na...@apache.org on 2016/08/01 13:39:25 UTC

[1/5] incubator-fineract git commit: FINERACT-202 : enabling the option for multi reshedule

Repository: incubator-fineract
Updated Branches:
  refs/heads/develop ada9ced9a -> c44cf55fc


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestReadPlatformServiceImpl.java
index c880364..19f6b79 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestReadPlatformServiceImpl.java
@@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.codes.data.CodeValueData;
 import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
 import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
 import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
@@ -36,6 +37,7 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanResched
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestEnumerations;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestStatusEnumData;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestTimelineData;
+import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
 import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.EmptyResultDataAccessException;
@@ -71,13 +73,8 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
             sqlBuilder.append("mc.id as clientId, ");
             sqlBuilder.append("ml.account_no as loanAccountNumber, ");
             sqlBuilder.append("lr.reschedule_from_installment as rescheduleFromInstallment, ");
-            sqlBuilder.append("lr.grace_on_principal as graceOnPrincipal, ");
-            sqlBuilder.append("lr.grace_on_interest as graceOnInterest, ");
             sqlBuilder.append("lr.reschedule_from_date as rescheduleFromDate, ");
-            sqlBuilder.append("lr.adjusted_due_date as adjustedDueDate, ");
-            sqlBuilder.append("lr.extra_terms as extraTerms, ");
             sqlBuilder.append("lr.recalculate_interest as recalculateInterest, ");
-            sqlBuilder.append("lr.interest_rate as interestRate, ");
             sqlBuilder.append("lr.reschedule_reason_cv_id as rescheduleReasonCvId, ");
             sqlBuilder.append("cv.code_value as rescheduleReasonCvValue, ");
             sqlBuilder.append("lr.reschedule_reason_comment as rescheduleReasonComment, ");
@@ -95,7 +92,14 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
             sqlBuilder.append("lr.rejected_on_date as rejectedOnDate, ");
             sqlBuilder.append("rbu.username as rejectedByUsername, ");
             sqlBuilder.append("rbu.firstname as rejectedByFirstname, ");
-            sqlBuilder.append("rbu.lastname as rejectedByLastname ");
+            sqlBuilder.append("rbu.lastname as rejectedByLastname, ");
+            
+            sqlBuilder.append("tv.id as termId,");
+            sqlBuilder.append("tv.term_type as termType,");
+            sqlBuilder.append("tv.applicable_date as variationApplicableFrom, ");
+            sqlBuilder.append("tv.decimal_value as decimalValue, ");
+            sqlBuilder.append("tv.date_value as dateValue, ");
+            sqlBuilder.append("tv.is_specific_to_installment as isSpecificToInstallment ");
 
             sqlBuilder.append("from " + loanRescheduleRequestTableName() + " lr ");
             sqlBuilder.append("left join m_code_value cv on cv.id = lr.reschedule_reason_cv_id ");
@@ -104,6 +108,8 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
             sqlBuilder.append("left join m_appuser rbu on rbu.id = lr.rejected_by_user_id ");
             sqlBuilder.append("left join m_loan ml on ml.id = lr.loan_id ");
             sqlBuilder.append("left join m_client mc on mc.id = ml.client_id ");
+            sqlBuilder.append("join m_loan_reschedule_request_term_variations_mapping rrtvm on lr.id = rrtvm.loan_reschedule_request_id ");
+            sqlBuilder.append("join m_loan_term_variations tv on tv.id = rrtvm.loan_term_variations_id and tv.parent_id is null") ;
 
             this.schema = sqlBuilder.toString();
         }
@@ -127,12 +133,7 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
             final String loanAccountNumber = rs.getString("loanAccountNumber");
             final Long clientId = rs.getLong("clientId");
             final Integer rescheduleFromInstallment = JdbcSupport.getInteger(rs, "rescheduleFromInstallment");
-            final Integer graceOnPrincipal = JdbcSupport.getInteger(rs, "graceOnPrincipal");
-            final Integer graceOnInterest = JdbcSupport.getInteger(rs, "graceOnInterest");
             final LocalDate rescheduleFromDate = JdbcSupport.getLocalDate(rs, "rescheduleFromDate");
-            final LocalDate adjustedDueDate = JdbcSupport.getLocalDate(rs, "adjustedDueDate");
-            final Integer extraTerms = JdbcSupport.getInteger(rs, "extraTerms");
-            final BigDecimal interestRate = rs.getBigDecimal("interestRate");
             final Long rescheduleReasonCvId = JdbcSupport.getLong(rs, "rescheduleReasonCvId");
             final String rescheduleReasonCvValue = rs.getString("rescheduleReasonCvValue");
             final CodeValueData rescheduleReasonCodeValue = CodeValueData.instance(rescheduleReasonCvId, rescheduleReasonCvValue);
@@ -157,14 +158,40 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
             final LoanRescheduleRequestTimelineData timeline = new LoanRescheduleRequestTimelineData(submittedOnDate, submittedByUsername,
                     submittedByFirstname, submittedByLastname, approvedOnDate, approvedByUsername, approvedByFirstname, approvedByLastname,
                     rejectedOnDate, rejectedByUsername, rejectedByFirstname, rejectedByLastname);
-
-            return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
-                    rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
-                    timeline, clientName, loanAccountNumber, clientId, recalculateInterest, rescheduleReasons);
+            
+            Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
+            
+            do {
+                Long tempId = rs.getLong("id");
+                if (id.equals(tempId)) {
+                    loanTermVariations.add(fetchLoanTermVariation(rs));
+                } else {
+                    rs.previous();
+                    break;
+                }
+            } while (rs.next());
+
+            return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, rescheduleFromDate,
+                    rescheduleReasonCodeValue, rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId,
+                    recalculateInterest, rescheduleReasons, loanTermVariations);
+        }
+        
+        private LoanTermVariationsData fetchLoanTermVariation(final ResultSet rs) throws SQLException {
+            final Long id = rs.getLong("termId");
+            final LocalDate variationApplicableFrom = JdbcSupport.getLocalDate(rs, "variationApplicableFrom");
+            final BigDecimal decimalValue = rs.getBigDecimal("decimalValue");
+            final LocalDate dateValue = JdbcSupport.getLocalDate(rs, "dateValue");
+            final boolean isSpecificToInstallment = rs.getBoolean("isSpecificToInstallment");
+            final int termType = rs.getInt("termType");
+
+            final LoanTermVariationsData loanTermVariationsData = new LoanTermVariationsData(id,
+                    LoanEnumerations.loanvariationType(termType), variationApplicableFrom, decimalValue, dateValue,
+                    isSpecificToInstallment);
+            return loanTermVariationsData;
         }
 
     }
-
+    
     @Override
     public List<LoanRescheduleRequestData> readLoanRescheduleRequests(Long loanId) {
         final Loan loan = this.loanRepository.findOne(loanId);
@@ -209,12 +236,7 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
         final Long loanId = null;
         final LoanRescheduleRequestStatusEnumData statusEnum = null;
         final Integer rescheduleFromInstallment = null;
-        final Integer graceOnPrincipal = null;
-        final Integer graceOnInterest = null;
         final LocalDate rescheduleFromDate = null;
-        final LocalDate adjustedDueDate = null;
-        final Integer extraTerms = null;
-        final BigDecimal interestRate = null;
         final CodeValueData rescheduleReasonCodeValue = null;
         final String rescheduleReasonComment = null;
         final LoanRescheduleRequestTimelineData timeline = null;
@@ -222,9 +244,10 @@ public class LoanRescheduleRequestReadPlatformServiceImpl implements LoanResched
         final String loanAccountNumber = null;
         final Long clientId = null;
         final Boolean recalculateInterest = null;
+        final Collection<LoanTermVariationsData> loanTermVariationsData = null;
 
-        return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
-                rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
-                timeline, clientName, loanAccountNumber, clientId, recalculateInterest, rescheduleReasons);
+        return LoanRescheduleRequestData.instance(id, loanId, statusEnum, rescheduleFromInstallment, rescheduleFromDate,
+                rescheduleReasonCodeValue, rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId, recalculateInterest,
+                rescheduleReasons, loanTermVariationsData);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
index 74ec624..6c70d7c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
@@ -22,9 +22,9 @@ import java.math.BigDecimal;
 import java.math.MathContext;
 import java.math.RoundingMode;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -33,64 +33,52 @@ import java.util.Set;
 import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
 import org.apache.fineract.infrastructure.codes.domain.CodeValue;
 import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
-import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
-import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
-import org.apache.fineract.organisation.holiday.domain.Holiday;
-import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper;
 import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
 import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
-import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
-import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
-import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
-import org.apache.fineract.portfolio.calendar.domain.Calendar;
-import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
-import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
-import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
-import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
-import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
+import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
-import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
+import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
-import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
-import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
-import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.LocalDate;
 import org.joda.time.format.DateTimeFormat;
@@ -107,25 +95,25 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
 
     private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
 
-    private final LoanRepositoryWrapper loanRepositoryWrapper;
     private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
     private final PlatformSecurityContext platformSecurityContext;
     private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
     private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
     private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
-    private final ConfigurationDomainService configurationDomainService;
-    private final HolidayRepositoryWrapper holidayRepository;
-    private final WorkingDaysRepositoryWrapper workingDaysRepository;
     private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
     private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
-    private final CalendarInstanceRepository calendarInstanceRepository;
-    private final LoanChargeReadPlatformService loanChargeReadPlatformService;
     private final LoanTransactionRepository loanTransactionRepository;
     private final JournalEntryWritePlatformService journalEntryWritePlatformService;
     private final LoanRepository loanRepository;
     private final LoanAssembler loanAssembler;
-    private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
     private final LoanUtilService loanUtilService;
+    private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
+    private final LoanScheduleGeneratorFactory loanScheduleFactory;
+    private final LoanSummaryWrapper loanSummaryWrapper;
+    private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
+    private final DefaultScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
+    private final LoanAccountDomainService loanAccountDomainService;
+    private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
 
     /**
      * LoanRescheduleRequestWritePlatformServiceImpl constructor
@@ -133,38 +121,39 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
      * @return void
      **/
     @Autowired
-    public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper,
-            CodeValueRepositoryWrapper codeValueRepositoryWrapper, PlatformSecurityContext platformSecurityContext,
-            LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
-            LoanRescheduleRequestRepository loanRescheduleRequestRepository,
-            ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, ConfigurationDomainService configurationDomainService,
-            HolidayRepositoryWrapper holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
-            LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
+    public LoanRescheduleRequestWritePlatformServiceImpl(final CodeValueRepositoryWrapper codeValueRepositoryWrapper,
+            final PlatformSecurityContext platformSecurityContext,
+            final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
+            final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
+            final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
+            final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
             final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
-            final CalendarInstanceRepository calendarInstanceRepository, final LoanChargeReadPlatformService loanChargeReadPlatformService,
             final LoanTransactionRepository loanTransactionRepository,
             final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanRepository loanRepository,
-            final LoanAssembler loanAssembler, final FloatingRatesReadPlatformService floatingRatesReadPlatformService,
-            final LoanUtilService loanUtilService) {
-        this.loanRepositoryWrapper = loanRepositoryWrapper;
+            final LoanAssembler loanAssembler, final LoanUtilService loanUtilService,
+            final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
+            final LoanScheduleGeneratorFactory loanScheduleFactory, final LoanSummaryWrapper loanSummaryWrapper,
+            final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
+            final LoanAccountDomainService loanAccountDomainService,
+            final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) {
         this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
         this.platformSecurityContext = platformSecurityContext;
         this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
         this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
         this.applicationCurrencyRepository = applicationCurrencyRepository;
-        this.configurationDomainService = configurationDomainService;
-        this.holidayRepository = holidayRepository;
-        this.workingDaysRepository = workingDaysRepository;
         this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
         this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
-        this.calendarInstanceRepository = calendarInstanceRepository;
-        this.loanChargeReadPlatformService = loanChargeReadPlatformService;
         this.loanTransactionRepository = loanTransactionRepository;
         this.journalEntryWritePlatformService = journalEntryWritePlatformService;
         this.loanRepository = loanRepository;
         this.loanAssembler = loanAssembler;
-        this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
         this.loanUtilService = loanUtilService;
+        this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
+        this.loanScheduleFactory = loanScheduleFactory;
+        this.loanSummaryWrapper = loanSummaryWrapper;
+        this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
+        this.loanAccountDomainService = loanAccountDomainService;
+        this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
     }
 
     /**
@@ -182,7 +171,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
             final Long loanId = jsonCommand.longValueOfParameterNamed(RescheduleLoansApiConstants.loanIdParamName);
 
             // use the loan id to get a Loan entity object
-            final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
+            final Loan loan = this.loanAssembler.assembleFrom(loanId);
 
             // validate the request in the JsonCommand object passed as
             // parameter
@@ -275,12 +264,24 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
             }
 
             final LoanRescheduleRequest loanRescheduleRequest = LoanRescheduleRequest.instance(loan,
-                    LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
-                    rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue,
-                    rescheduleReasonComment, submittedOnDate, this.platformSecurityContext.authenticatedUser(), null, null, null, null);
+                    LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, rescheduleFromDate,
+                    recalculateInterest, rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate,
+                    this.platformSecurityContext.authenticatedUser(), null, null, null, null);
+
+            // update reschedule request to term variations mapping
+            List<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = new ArrayList<>();
+            final Boolean isActive = false;
+            final boolean isSpecificToInstallment = false;
+            BigDecimal decimalValue = null;
+            Date dueDate = null;
+            // create term variations for flat and declining balance loans
+            createLoanTermVariationsForRegularLoans(loan, graceOnPrincipal, graceOnInterest, extraTerms, interestRate, rescheduleFromDate,
+                    adjustedDueDate, loanRescheduleRequest, loanRescheduleRequestToTermVariationMappings, isActive,
+                    isSpecificToInstallment, decimalValue, dueDate);
 
             // create a new entry in the m_loan_reschedule_request table
             this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
+            this.loanRepository.save(loan);
 
             return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequest.getId())
                     .withLoanId(loan.getId()).build();
@@ -295,6 +296,63 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
         }
     }
 
+    private void createLoanTermVariationsForRegularLoans(final Loan loan, final Integer graceOnPrincipal, final Integer graceOnInterest,
+            final Integer extraTerms, final BigDecimal interestRate, Date rescheduleFromDate, Date adjustedDueDate,
+            final LoanRescheduleRequest loanRescheduleRequest,
+            List<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings, final Boolean isActive,
+            final boolean isSpecificToInstallment, BigDecimal decimalValue, Date dueDate) {
+
+        if (rescheduleFromDate != null && adjustedDueDate != null) {
+            LoanTermVariations parent = null;
+            final Integer termType = LoanTermVariationType.DUE_DATE.getValue();
+            createLoanTermVariations(termType, loan, rescheduleFromDate, adjustedDueDate, loanRescheduleRequestToTermVariationMappings,
+                    isActive, isSpecificToInstallment, decimalValue, parent);
+        }
+
+        if (rescheduleFromDate != null && interestRate != null) {
+            LoanTermVariations parent = null;
+            final Integer termType = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue();
+            createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings, isActive,
+                    isSpecificToInstallment, interestRate, parent);
+        }
+
+        if (rescheduleFromDate != null && graceOnPrincipal != null) {
+            final Integer termType = LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue();
+            LoanTermVariations parent = null;
+            parent = createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings,
+                    isActive, isSpecificToInstallment, BigDecimal.valueOf(graceOnPrincipal), parent);
+            
+            BigDecimal extraTermsBasedOnGracePeriods = BigDecimal.valueOf(graceOnPrincipal);
+            createLoanTermVariations(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue(), loan, rescheduleFromDate, dueDate,
+                    loanRescheduleRequestToTermVariationMappings, isActive, isSpecificToInstallment, extraTermsBasedOnGracePeriods, parent);
+
+        }
+
+        if (rescheduleFromDate != null && graceOnInterest != null) {
+            LoanTermVariations parent = null;
+            final Integer termType = LoanTermVariationType.GRACE_ON_INTEREST.getValue();
+            createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings, isActive,
+                    isSpecificToInstallment, BigDecimal.valueOf(graceOnInterest), parent);
+        }
+
+        if (rescheduleFromDate != null && extraTerms != null) {
+            LoanTermVariations parent = null;
+            final Integer termType = LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue();
+            createLoanTermVariations(termType, loan, rescheduleFromDate, dueDate, loanRescheduleRequestToTermVariationMappings, isActive,
+                    isSpecificToInstallment, BigDecimal.valueOf(extraTerms), parent);
+        }
+        loanRescheduleRequest.updateLoanRescheduleRequestToTermVariationMappings(loanRescheduleRequestToTermVariationMappings);
+    }
+
+    private LoanTermVariations createLoanTermVariations(final Integer termType, final Loan loan, Date rescheduleFromDate,
+            Date adjustedDueDate, List<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings,
+            final Boolean isActive, final boolean isSpecificToInstallment, final BigDecimal decimalValue, LoanTermVariations parent) {
+        LoanTermVariations loanTermVariation = new LoanTermVariations(termType, rescheduleFromDate, decimalValue, adjustedDueDate,
+                isSpecificToInstallment, loan, loan.status().getValue(), isActive, parent);
+        loanRescheduleRequestToTermVariationMappings.add(LoanRescheduleRequestToTermVariationMapping.createNew(loanTermVariation));
+        return loanTermVariation;
+    }
+
     @Override
     @Transactional
     public CommandProcessingResult approve(JsonCommand jsonCommand) {
@@ -322,132 +380,112 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
             changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter));
             changes.put("approvedByUserId", appUser.getId());
 
-            if (!changes.isEmpty()) {
-                Loan loan = loanRescheduleRequest.getLoan();
-                final LoanSummary loanSummary = loan.getSummary();
-
-                final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
-                final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
-                        .getDisbursementDate().toDate());
-                final WorkingDays workingDays = this.workingDaysRepository.findOne();
-                final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
-                final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
-                final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
-
-                final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
-                final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
-                final MathContext mathContext = new MathContext(8, roundingMode);
-
-                Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
-                        .createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
-
-                HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
-                CalendarInstance restCalendarInstance = null;
-                CalendarInstance compoundingCalendarInstance = null;
-                if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
-                    restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
-                            loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
-                    compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
-                            loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
-                }
-                final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
-                        CalendarEntityType.LOANS.getValue());
-                Calendar loanCalendar = null;
-                if (loanCalendarInstance != null) {
-                    loanCalendar = loanCalendarInstance.getCalendar();
-                }
-                FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
-                Boolean isSkipRepaymentOnFirstMonth = false;
-                Integer numberOfDays = 0;
-                boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
-                if(isSkipRepaymentOnFirstMonthEnabled){
-                    isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar);
-                    if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); }
-                    
-                }
-                LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
-                        loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
-                        loanCalendar, floatingRateDTO, isSkipRepaymentOnFirstMonth, numberOfDays);
-
-                final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
-                List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
-                Collection<LoanCharge> waiveLoanCharges = new ArrayList<>();
-                final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
-                for (LoanRescheduleModelRepaymentPeriod period : periods) {
-
-                    if (period.isNew()) {
-                        LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment(loan,
-                                period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(),
-                                period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false, compoundingDetails);
-
-                        loan.addLoanRepaymentScheduleInstallment(repaymentScheduleInstallment);
-                        repaymentScheduleInstallments.add(repaymentScheduleInstallment) ;
-                    }
-
-                    else {
-                        for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
-
-                            if (repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
-
-                                LocalDate periodDueDate = repaymentScheduleInstallment.getDueDate();
-                                Money zeroAmount = Money.of(currency, new BigDecimal(0));
-
-                                repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
-                                repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
-                                repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
-                                repaymentScheduleInstallment.updatePrincipal(period.principalDue());
-                                repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
-
-                                if (Money.of(currency, period.principalDue()).isZero() && Money.of(currency, period.interestDue()).isZero()
-                                        && repaymentScheduleInstallment.isNotFullyPaidOff()) {
-
-                                    if (repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isGreaterThan(zeroAmount)
-                                            || repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isGreaterThan(zeroAmount)) {
-
-                                        waiveLoanCharges.addAll(loan.getLoanCharges(periodDueDate));
-                                    }
-                                }
-
-                                break;
-                            }
+            Loan loan = loanRescheduleRequest.getLoan();
+            final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
+            final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
+
+            ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan,
+                    loanRescheduleRequest.getRescheduleFromDate());
+
+            Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
+                    .createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
+
+            final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
+
+            LocalDate rescheduleFromDate = null;
+            Set<LoanTermVariations> activeLoanTermVariations = loan.getActiveLoanTermVariations();
+            LoanTermVariations dueDateVariationInCurrentRequest = loanRescheduleRequest.getDueDateTermVariationIfExists();
+            if (dueDateVariationInCurrentRequest != null && activeLoanTermVariations != null) {
+                LocalDate fromScheduleDate = dueDateVariationInCurrentRequest.fetchTermApplicaDate();
+                LocalDate currentScheduleDate = fromScheduleDate;
+                LocalDate modifiedScheduleDate = dueDateVariationInCurrentRequest.fetchDateValue();
+                Map<LocalDate, LocalDate> changeMap = new HashMap<>();
+                changeMap.put(currentScheduleDate, modifiedScheduleDate);
+                for (LoanTermVariations activeLoanTermVariation : activeLoanTermVariations) {
+                    if (activeLoanTermVariation.getTermType().isDueDateVariation()
+                            && activeLoanTermVariation.fetchDateValue().equals(dueDateVariationInCurrentRequest.fetchTermApplicaDate())) {
+                        activeLoanTermVariation.markAsInactive();
+                        rescheduleFromDate = activeLoanTermVariation.fetchTermApplicaDate();
+                        dueDateVariationInCurrentRequest.setTermApplicableFrom(rescheduleFromDate.toDate());
+                    } else if (!activeLoanTermVariation.fetchTermApplicaDate().isBefore(fromScheduleDate)) {
+                        while (currentScheduleDate.isBefore(activeLoanTermVariation.fetchTermApplicaDate())) {
+                            currentScheduleDate = this.scheduledDateGenerator.generateNextRepaymentDate(currentScheduleDate,
+                                    loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
+                            modifiedScheduleDate = this.scheduledDateGenerator.generateNextRepaymentDate(modifiedScheduleDate,
+                                    loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
+                            changeMap.put(currentScheduleDate, modifiedScheduleDate);
+                        }
+                        if (changeMap.containsKey(activeLoanTermVariation.fetchTermApplicaDate())) {
+                            activeLoanTermVariation.setTermApplicableFrom(changeMap.get(activeLoanTermVariation.fetchTermApplicaDate())
+                                    .toDate());
                         }
                     }
                 }
-
-                for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
-                    this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
+            }
+            if (rescheduleFromDate == null) {
+                rescheduleFromDate = loanRescheduleRequest.getRescheduleFromDate();
+            }
+            for (LoanRescheduleRequestToTermVariationMapping mapping : loanRescheduleRequest
+                    .getLoanRescheduleRequestToTermVariationMappings()) {
+                mapping.getLoanTermVariations().updateIsActive(true);
+            }
+            BigDecimal annualNominalInterestRate = null;
+            List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
+            loan.constructLoanTermVariations(scheduleGeneratorDTO.getFloatingRateDTO(), annualNominalInterestRate, loanTermVariations);
+            loanApplicationTerms.getLoanTermVariations().setExceptionData(loanTermVariations);
+
+            /*for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
+                if (rescheduleFromDate.isBefore(loanTermVariation.getTermApplicableFrom())) {
+                    LocalDate applicableDate = this.scheduledDateGenerator.generateNextRepaymentDate(rescheduleFromDate,
+                            loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
+                    if (loanTermVariation.getTermApplicableFrom().equals(applicableDate)) {
+                        LocalDate adjustedDate = this.scheduledDateGenerator.generateNextRepaymentDate(adjustedApplicableDate,
+                                loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
+                        loanTermVariation.setApplicableFromDate(adjustedDate);
+                    }
                 }
+            }*/
+
+            final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
+            final MathContext mathContext = new MathContext(8, roundingMode);
+            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
+                    .determineProcessor(loan.transactionProcessingStrategy());
+            final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
+            final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
+            loan.setHelpers(loanLifecycleStateMachine, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
+            final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
+                    loan, loanApplicationTerms.getHolidayDetailDTO(),
+                    loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
+
+            loan.updateLoanSchedule(loanSchedule.getInstallments(), appUser);
+            loan.recalculateAllCharges();
+            ChangedTransactionDetail changedTransactionDetail =  loan.processTransactions();
+
+            for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
+                this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
+            }
 
-                loan.updateRescheduledByUser(appUser);
-                loan.updateRescheduledOnDate(new LocalDate());
-
-                // waive all loan charges of zero instalments
-                waiveLoanCharges(loan, waiveLoanCharges);
-
-                // update the Loan summary
-                loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true, null);
-
-                // update the total number of schedule repayments
-                loan.updateNumberOfRepayments(periods.size());
-
-                // update the loan term frequency (loan term frequency = number
-                // of repayments)
-                loan.updateTermFrequency(periods.size());
-
-                // update the status of the request
-                loanRescheduleRequest.approve(appUser, approvedOnDate);
-
-                // update the derived fields of each loan repayments schedule
-                // instalments
-                for (final LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
-                    repaymentScheduleInstallment.updateDerivedFields(currency, new LocalDate());
+            loan.updateRescheduledByUser(appUser);
+            loan.updateRescheduledOnDate(new LocalDate());
+
+            // update the status of the request
+            loanRescheduleRequest.approve(appUser, approvedOnDate);
+
+            // update the loan object
+            saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
+            
+            if (changedTransactionDetail != null) {
+                for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+                    this.loanTransactionRepository.save(mapEntry.getValue());
+                    // update loan with references to the newly created
+                    // transactions
+                    loan.addLoanTransaction(mapEntry.getValue());
+                    this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
                 }
-
-                // updates maturity date
-                loan.updateLoanScheduleDependentDerivedFields();
-                // update the loan object
-                this.loanRepository.save(loan);
             }
+            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+            
+            this.loanAccountDomainService.recalculateAccruals(loan, true);
 
             return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
                     .withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).build();
@@ -462,60 +500,24 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
         }
     }
 
-    /**
-     * waive all charges in the collection
-     * 
-     * @param loan
-     *            Loan object
-     * @param loanCharges
-     *            collection of LoanCharge objects
-     * @return void
-     **/
-    private void waiveLoanCharges(Loan loan, Collection<LoanCharge> loanCharges) {
-        AppUser currentUser = this.platformSecurityContext.authenticatedUser();
-        this.loanAssembler.setHelpers(loan);
-
-        for (LoanCharge loanCharge : loanCharges) {
-
-            if (loanCharge.isChargePending()) {
-                Integer loanInstallmentNumber = null;
-
-                if (loanCharge.isInstalmentFee()) {
-                    LoanInstallmentCharge chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
-
-                    if (chargePerInstallment != null) {
-                        loanInstallmentNumber = chargePerInstallment.getRepaymentInstallment().getInstallmentNumber();
-                    }
-                }
-
-                final Map<String, Object> changes = new LinkedHashMap<>(3);
-
-                final List<Long> existingTransactionIds = new ArrayList<>();
-                final List<Long> existingReversedTransactionIds = new ArrayList<>();
-                LocalDate recalculateFrom = null;
-                if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
-                    recalculateFrom = DateUtils.getLocalDateOfTenant();
-                }
-
-                ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
-
-                Money accruedCharge = Money.zero(loan.getCurrency());
-                if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
-                    Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
-                            loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
-                    for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) {
-                        accruedCharge = accruedCharge.plus(chargePaidByData.getAmount());
-                    }
+    private void saveAndFlushLoanWithDataIntegrityViolationChecks(final Loan loan) {
+        try {
+            List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
+            for (LoanRepaymentScheduleInstallment installment : installments) {
+                if (installment.getId() == null) {
+                    this.repaymentScheduleInstallmentRepository.save(installment);
                 }
-
-                final LoanTransaction loanTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
-                        existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge,
-                        currentUser);
-
-                this.loanTransactionRepository.save(loanTransaction);
-
-                postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
             }
+            this.loanRepository.saveAndFlush(loan);
+        } catch (final DataIntegrityViolationException e) {
+            final Throwable realCause = e.getCause();
+            final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+            final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
+            if (realCause.getMessage().toLowerCase().contains("external_id_unique")) {
+                baseDataValidator.reset().parameter("externalId").failWithCode("value.must.be.unique");
+            }
+            if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
+                    "Validation errors exist.", dataValidationErrors); }
         }
     }
 
@@ -528,11 +530,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
         this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
     }
 
-    private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
-        final List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
-        return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
-    }
-
     @Override
     @Transactional
     public CommandProcessingResult reject(JsonCommand jsonCommand) {
@@ -562,6 +559,11 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
 
             if (!changes.isEmpty()) {
                 loanRescheduleRequest.reject(appUser, rejectedOnDate);
+                Set<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = loanRescheduleRequest
+                        .getLoanRescheduleRequestToTermVariationMappings();
+                for (LoanRescheduleRequestToTermVariationMapping loanRescheduleRequestToTermVariationMapping : loanRescheduleRequestToTermVariationMappings) {
+                    loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().markAsInactive();
+                }
             }
 
             return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
@@ -593,20 +595,4 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
                 "Unknown data integrity issue with resource.");
     }
 
-    private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
-        FloatingRateDTO floatingRateDTO = null;
-        if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
-            boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
-            BigDecimal interestRateDiff = loan.getInterestRateDifferential();
-            List<FloatingRatePeriodData> baseLendingRatePeriods = null;
-            try {
-                baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate().getRatePeriods();
-            } catch (final FloatingRateNotFoundException ex) {
-                // Do not do anything
-            }
-            floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
-                    baseLendingRatePeriods);
-        }
-        return floatingRateDTO;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
index dffd39c..18c9058 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
@@ -551,6 +551,22 @@ public class LoanEnumerations {
                 optionData = new EnumOptionData(LoanTermVariationType.PRINCIPAL_AMOUNT.getValue().longValue(),
                         LoanTermVariationType.PRINCIPAL_AMOUNT.getCode(), "principalAmount");
             break;
+            case GRACE_ON_INTEREST:
+                optionData = new EnumOptionData(LoanTermVariationType.GRACE_ON_INTEREST.getValue().longValue(),
+                        LoanTermVariationType.GRACE_ON_INTEREST.getCode(), "graceOnInterest");
+            break;
+            case GRACE_ON_PRINCIPAL:
+                optionData = new EnumOptionData(LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue().longValue(),
+                        LoanTermVariationType.GRACE_ON_PRINCIPAL.getCode(), "graceOnPrincipal");
+            break;
+            case EXTEND_REPAYMENT_PERIOD:
+                optionData = new EnumOptionData(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue().longValue(),
+                        LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getCode(), "extendRepaymentPeriod");
+            break;
+            case INTEREST_RATE_FROM_INSTALLMENT:
+                optionData = new EnumOptionData(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue().longValue(),
+                        LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getCode(), "interestRateForInstallment");
+            break;
             default:
                 optionData = new EnumOptionData(LoanTermVariationType.INVALID.getValue().longValue(),
                         LoanTermVariationType.INVALID.getCode(), "Invalid");

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
new file mode 100644
index 0000000..1fca1d1
--- /dev/null
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
@@ -0,0 +1,141 @@
+ALTER TABLE `m_loan_term_variations`
+	ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT '1' AFTER `applied_on_loan_status`;
+	
+ALTER TABLE `m_loan_term_variations`
+	ADD COLUMN `parent_id` BIGINT(20) NULL DEFAULT NULL AFTER `is_active`;
+
+ALTER TABLE `m_loan_term_variations`
+	ADD COLUMN `reshedule_request_id` BIGINT(20) NULL DEFAULT NULL AFTER `parent_id`;
+
+insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
+(	
+select
+mlrr.loan_id,
+ 7 term_type,
+ ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
+ mlrr.grace_on_interest decimal_value,
+ null date_value,
+ 1 is_specific_to_installment,
+ 300 applied_on_loan_status_id,
+ mlrr.id reshedule_request_id,
+ if(mlrr.status_enum = 200 ,1,0) is_active,
+ null parent_id
+from m_loan_reschedule_request mlrr 
+where mlrr.grace_on_interest is not null);
+
+insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
+(	
+select
+mlrr.loan_id,
+ 8 term_type,
+ ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
+ mlrr.grace_on_principal decimal_value,
+ null date_value,
+ 1 is_specific_to_installment,
+ 300 applied_on_loan_status_id,
+ mlrr.id reshedule_request_id,
+ if(mlrr.status_enum = 200 ,1,0) is_active,
+ null parent_id
+from m_loan_reschedule_request mlrr 
+where mlrr.grace_on_principal is not null);
+
+insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
+(	
+select
+mlrr.loan_id,
+ 4 term_type,
+ mlrr.reschedule_from_date applicable_date,
+ null decimal_value,
+ mlrr.adjusted_due_date date_value,
+ 1 is_specific_to_installment,
+ 300 applied_on_loan_status_id,
+ mlrr.id reshedule_request_id,
+ if(mlrr.status_enum = 200 ,1,0) is_active,
+ null parent_id
+from m_loan_reschedule_request mlrr 
+where mlrr.adjusted_due_date is not null);
+
+insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
+(	
+select
+mlrr.loan_id,
+ 9 term_type,
+ ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
+ mlrr.extra_terms decimal_value,
+ null date_value,
+ 1 is_specific_to_installment,
+ 300 applied_on_loan_status_id,
+ mlrr.id reshedule_request_id,
+ if(mlrr.status_enum = 200 ,1,0) is_active,
+ null parent_id
+from m_loan_reschedule_request mlrr 
+where mlrr.extra_terms is not null);
+
+insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
+(	
+select
+mlrr.loan_id,
+ 2 term_type,
+ ifnull(mlrr.adjusted_due_date,mlrr.reschedule_from_date) applicable_date,
+ mlrr.interest_rate decimal_value,
+ null date_value,
+ 1 is_specific_to_installment,
+ 300 applied_on_loan_status_id,
+ mlrr.id reshedule_request_id,
+ if(mlrr.status_enum = 200 ,1,0) is_active,
+ null parent_id
+from m_loan_reschedule_request mlrr 
+where mlrr.interest_rate is not null);
+
+insert into m_loan_term_variations(`loan_id`, `term_type`, `applicable_date`, `decimal_value`, `date_value` , `is_specific_to_installment`, `applied_on_loan_status`, `reshedule_request_id` , `is_active` , `parent_id`)
+(	
+select
+mlrr.loan_id,
+ 9 term_type,
+ mlrr.reschedule_from_date applicable_date,
+ if(ifnull(mlrr.grace_on_principal,0) > ifnull(mlrr.grace_on_interest,0),mlrr.grace_on_principal,mlrr.grace_on_interest) decimal_value,
+ null date_value,
+ 1 is_specific_to_installment,
+ 300 applied_on_loan_status_id,
+ mlrr.id reshedule_request_id,
+ if(mlrr.status_enum = 200 ,1,0) is_active,
+ (select distinct mlt.id
+ from m_loan_term_variations mlt 
+ where mlt.reshedule_request_id is not null
+ and (mlt.term_type =8 or mlt.term_type = 7)
+ and mlt.reshedule_request_id = mlrr.id
+ order by mlt.term_type desc limit 1) parent_id
+from m_loan_reschedule_request mlrr 
+where (mlrr.grace_on_interest is not null or mlrr.grace_on_principal is not null));
+	
+ALTER TABLE `m_loan_reschedule_request`
+	DROP COLUMN `grace_on_principal`,
+	DROP COLUMN `grace_on_interest`,
+	DROP COLUMN `extra_terms`,
+	DROP COLUMN `interest_rate`,
+	DROP COLUMN `adjusted_due_date`;
+
+CREATE TABLE `m_loan_reschedule_request_term_variations_mapping` (
+	`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
+	`loan_reschedule_request_id` BIGINT(20) NOT NULL,
+	`loan_term_variations_id` BIGINT(20) NOT NULL,
+	PRIMARY KEY (`id`),
+	INDEX `FK__m_loan_reschedule_request` (`loan_reschedule_request_id`),
+	INDEX `FK__m_loan_term_variations` (`loan_term_variations_id`),
+	CONSTRAINT `FK__m_loan_reschedule_request` FOREIGN KEY (`loan_reschedule_request_id`) REFERENCES `m_loan_reschedule_request` (`id`),
+	CONSTRAINT `FK__m_loan_term_variations` FOREIGN KEY (`loan_term_variations_id`) REFERENCES `m_loan_term_variations` (`id`)
+)
+COLLATE='utf8_general_ci'
+ENGINE=InnoDB
+AUTO_INCREMENT=1
+;
+
+insert ignore into m_loan_reschedule_request_term_variations_mapping (`loan_reschedule_request_id`, `loan_term_variations_id`)
+(
+select distinct mltv.reshedule_request_id,mltv.id
+from m_loan_term_variations mltv
+where reshedule_request_id is not null
+);
+
+ALTER TABLE `m_loan_term_variations`
+	DROP COLUMN `reshedule_request_id`;
\ No newline at end of file


[2/5] incubator-fineract git commit: FINERACT-202 : enabling the option for multi reshedule

Posted by na...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index c1f91ea..0746522 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -21,7 +21,9 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
 import java.math.BigDecimal;
 import java.math.MathContext;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -192,6 +194,25 @@ public final class LoanApplicationTerms {
     private final boolean isSkipRepaymentOnFirstDayOfMonth;
 
     private final HolidayDetailDTO holidayDetailDTO;
+    
+    private final Set<Integer> periodNumbersApplicableForPrincipalGrace = new HashSet<>();
+    
+    private final Set<Integer> periodNumbersApplicableForInterestGrace = new HashSet<>();
+    
+    
+    // used for FLAT loans when interest rate changed 
+    private Integer excludePeriodsForCalculation = 0;
+    private Money totalPrincipalAccountedForInterestCalcualtion;
+    
+    
+    //used for FLAT loans generation on modifying terms 
+    private Money totalPrincipalAccounted;
+    private Money totalInterestAccounted;
+    private int periodsCompleted = 0;
+    private int extraPeriods = 0;
+    
+    
+    
 
     public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
             final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
@@ -482,6 +503,12 @@ public final class LoanApplicationTerms {
         this.isInterestChargedFromDateSameAsDisbursalDateEnabled = isInterestChargedFromDateSameAsDisbursalDateEnabled;
         this.holidayDetailDTO = holidayDetailDTO;
         this.allowCompoundingOnEod = allowCompoundingOnEod;
+        Integer periodNumber = 1;
+        updatePeriodNumberApplicableForPrincipalOrInterestGrace(periodNumber);
+        updateRecurringMoratoriumOnPrincipalPeriods(periodNumber);
+        this.totalPrincipalAccountedForInterestCalcualtion = principal.zero();
+        this.totalInterestAccounted = principal.zero();
+        this.totalPrincipalAccounted = principal.zero();
     }
 
     public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
@@ -706,7 +733,8 @@ public final class LoanApplicationTerms {
         switch (this.interestMethod) {
             case FLAT:
                 final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
-                totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
+                totalInterestDue = this.principal.minus(totalPrincipalAccountedForInterestCalcualtion).multiplyRetainScale(interestRateForLoanTerm,
+                        mc.getRoundingMode());
 
             break;
             case DECLINING_BALANCE:
@@ -715,10 +743,6 @@ public final class LoanApplicationTerms {
             break;
         }
 
-        if (this.totalInterestDue != null) {
-            totalInterestDue = this.totalInterestDue;
-        }
-
         return totalInterestDue;
     }
 
@@ -844,13 +868,19 @@ public final class LoanApplicationTerms {
     private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) {
 
         final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc);
-
-        return totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments), mc.getRoundingMode());
+        Money interestPerInstallment = totalInterestForLoanTerm.dividedBy(Long.valueOf(this.actualNumberOfRepayments) - defaultToZeroIfNull(this.excludePeriodsForCalculation), mc.getRoundingMode());
+        if (this.excludePeriodsForCalculation < this.periodsCompleted) {
+            Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
+            interestPerInstallment = interestLeft.dividedBy(Long.valueOf(this.actualNumberOfRepayments)
+                    - defaultToZeroIfNull(this.periodsCompleted), mc.getRoundingMode());
+        }
+        
+        return interestPerInstallment;
     }
 
     private Money calculateTotalPrincipalPerPeriodWithoutGrace(final MathContext mc, final int periodNumber) {
         final int totalRepaymentsWithCapitalPayment = calculateNumberOfRepaymentsWithPrincipalPayment();
-        Money principalPerPeriod = this.principal.dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(
+        Money principalPerPeriod = this.principal.minus(totalPrincipalAccounted).dividedBy(totalRepaymentsWithCapitalPayment, mc.getRoundingMode()).plus(
                 this.adjustPrincipalForFlatLoans);
         if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) {
             principalPerPeriod = principalPerPeriod.zero();
@@ -906,11 +936,30 @@ public final class LoanApplicationTerms {
 
             final Money totalInterestFree = interestPerGracePeriod.multipliedBy(getInterestChargingGrace());
             final Money realTotalInterestForLoan = totalInterestForLoanTerm.minus(totalInterestFree);
-
-            final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.actualNumberOfRepayments);
-
+            
+            
+            
+            Integer interestPaymentDuePeriods = calculateNumberOfRemainingInterestPaymentPeriods(this.actualNumberOfRepayments,
+                    this.excludePeriodsForCalculation);
             interestForInstallment = realTotalInterestForLoan
                     .dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode());
+            if (this.excludePeriodsForCalculation < this.periodsCompleted) {
+                Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
+                Integer interestDuePeriods = calculateNumberOfRemainingInterestPaymentPeriods(this.actualNumberOfRepayments,
+                        this.periodsCompleted);
+                interestForInstallment = interestLeft.dividedBy(Long.valueOf(interestDuePeriods), mc.getRoundingMode());
+            }
+            if(!this.periodNumbersApplicableForInterestGrace.isEmpty()){
+                int periodsElapsed = calculateLastInterestGracePeriod(periodNumber);
+                if (periodsElapsed > this.excludePeriodsForCalculation && periodsElapsed > this.periodsCompleted) {
+                    Money interestLeft = this.totalInterestDue.minus(this.totalInterestAccounted);
+                    Integer interestDuePeriods = calculateNumberOfRemainingInterestPaymentPeriods(this.actualNumberOfRepayments,
+                            periodsElapsed);
+                    interestForInstallment = interestLeft.dividedBy(Long.valueOf(interestDuePeriods), mc.getRoundingMode());
+                }
+            }
+            
+
         }
 
         return interestForInstallment;
@@ -1032,24 +1081,46 @@ public final class LoanApplicationTerms {
 
     private int calculateNumberOfRepaymentsWithPrincipalPayment() {
         int numPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(this.actualNumberOfRepayments,
-                this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), 0);
+                this.periodsCompleted);
+//        numPeriods = numPeriods - this.periodsCompleted;
         return numPeriods;
     }
-
-    private Integer calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(final Integer totalNumberOfRepaymentPeriods) {
-        return totalNumberOfRepaymentPeriods - Math.max(getInterestChargingGrace(), getInterestPaymentGrace());
+    
+    private Integer calculateNumberOfRemainingInterestPaymentPeriods(final Integer totalNumberOfRepaymentPeriods, int periodsElapsed) {
+        int principalFeePeriods = 0;
+        for (Integer intNumber : this.periodNumbersApplicableForInterestGrace) {
+            if (intNumber > periodsElapsed) {
+                principalFeePeriods++;
+            }
+        }
+        Integer periodsRemaining = totalNumberOfRepaymentPeriods - periodsElapsed - principalFeePeriods;
+        return periodsRemaining;
+    }
+    
+    private Integer calculateLastInterestGracePeriod(int periodNumber){
+        int lastGracePeriod = 0;
+        for (Integer grace : this.periodNumbersApplicableForInterestGrace) {
+            if(grace < periodNumber && lastGracePeriod < grace){
+                lastGracePeriod = grace;
+            }
+        }
+        return lastGracePeriod;
     }
 
     public boolean isPrincipalGraceApplicableForThisPeriod(final int periodNumber) {
-        if (this.getRecurringMoratoriumOnPrincipalPeriods() > 0) {
-            return ((periodNumber > 0 && periodNumber <= getPrincipalGrace()) || (periodNumber > 0 && (((periodNumber - getPrincipalGrace()) % (this
-                    .getRecurringMoratoriumOnPrincipalPeriods() + 1)) != 1)));
+        boolean isPrincipalGraceApplicableForThisPeriod = false;
+        if (this.periodNumbersApplicableForPrincipalGrace.contains(periodNumber)) {
+            isPrincipalGraceApplicableForThisPeriod = true;
         }
-        return periodNumber > 0 && periodNumber <= getPrincipalGrace();
+        return isPrincipalGraceApplicableForThisPeriod;
     }
 
-    private boolean isInterestPaymentGraceApplicableForThisPeriod(final int periodNumber) {
-        return periodNumber > 0 && periodNumber <= getInterestPaymentGrace();
+    public boolean isInterestPaymentGraceApplicableForThisPeriod(final int periodNumber) {
+        boolean isInterestPaymentGraceApplicableForThisPeriod = false;
+        if (this.periodNumbersApplicableForInterestGrace.contains(periodNumber)) {
+            isInterestPaymentGraceApplicableForThisPeriod = true;
+        }
+        return isInterestPaymentGraceApplicableForThisPeriod;
     }
 
     private boolean isFirstPeriodAfterInterestPaymentGracePeriod(final int periodNumber) {
@@ -1099,7 +1170,7 @@ public final class LoanApplicationTerms {
             final double principalDouble = balance.getAmount().multiply(BigDecimal.valueOf(-1)).doubleValue();
 
             final Integer periodsRemaining = calculateNumberOfRemainingPrincipalPaymentPeriods(this.actualNumberOfRepayments,
-                    this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), periodsElapsed);
+                    periodsElapsed);
 
             double installmentAmount = FinanicalFunctions.pmt(periodicInterestRate.doubleValue(), periodsRemaining.doubleValue(),
                     principalDouble, futureValue, false);
@@ -1165,7 +1236,7 @@ public final class LoanApplicationTerms {
         Money principal = this.principal;
         if (this.fixedPrincipalAmount == null) {
             final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(
-                    this.actualNumberOfRepayments, this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), periodNumber);
+                    this.actualNumberOfRepayments, periodNumber);
             principal = this.principal.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
             this.fixedPrincipalAmount = principal.getAmount();
         }
@@ -1179,21 +1250,19 @@ public final class LoanApplicationTerms {
 
     public void updateFixedPrincipalAmount(final MathContext mc, final int periodNumber, final Money outstandingAmount) {
         final Integer numberOfPrincipalPaymentPeriods = calculateNumberOfRemainingPrincipalPaymentPeriods(this.actualNumberOfRepayments,
-                this.getRecurringMoratoriumOnPrincipalPeriods(), this.getPrincipalGrace(), periodNumber - 1);
+                periodNumber - 1);
         Money principal = outstandingAmount.dividedBy(numberOfPrincipalPaymentPeriods, mc.getRoundingMode());
         this.fixedPrincipalAmount = principal.getAmount();
     }
 
-    private static Integer calculateNumberOfRemainingPrincipalPaymentPeriods(final Integer totalNumberOfRepaymentPeriods,
-            final int recurringMoratoriumOnPrincipalPeriods, final int PrincipalGrace, int periodsElapsed) {
-        if (PrincipalGrace > periodsElapsed) {
-            periodsElapsed = PrincipalGrace;
-        }
-        Integer periodsRemaining = totalNumberOfRepaymentPeriods - periodsElapsed;
-        if (recurringMoratoriumOnPrincipalPeriods > 0) {
-            double periodsRemainingDouble = ((double) periodsRemaining / ((double) (recurringMoratoriumOnPrincipalPeriods + 1)));
-            periodsRemaining = (int) Math.ceil(periodsRemainingDouble);
+    private Integer calculateNumberOfRemainingPrincipalPaymentPeriods(final Integer totalNumberOfRepaymentPeriods, int periodsElapsed) {
+        int principalFeePeriods = 0;
+        for (Integer intNumber : this.periodNumbersApplicableForPrincipalGrace) {
+            if (intNumber > periodsElapsed) {
+                principalFeePeriods++;
+            }
         }
+        Integer periodsRemaining = totalNumberOfRepaymentPeriods - periodsElapsed - principalFeePeriods;
         return periodsRemaining;
     }
 
@@ -1469,10 +1538,7 @@ public final class LoanApplicationTerms {
     }
 
     public void updateTotalInterestDue(Money totalInterestDue) {
-
-        if (totalInterestDue != null) {
-            this.totalInterestDue = totalInterestDue;
-        }
+        this.totalInterestDue = totalInterestDue;
     }
 
     public ApplicationCurrency getApplicationCurrency() {
@@ -1576,4 +1642,81 @@ public final class LoanApplicationTerms {
         }
         return disbursedAmount;
     }
+    
+    public void updatePeriodNumberApplicableForPrincipalOrInterestGrace(final Integer periodsApplicationForGrace) {
+        int applicablePeriodNumber = periodsApplicationForGrace;
+        int graceOnPrincipal = defaultToZeroIfNull(this.principalGrace);
+        int graceOnInterest = defaultToZeroIfNull(this.interestPaymentGrace);
+
+        while (graceOnPrincipal > 0 || graceOnInterest > 0) {
+            if (graceOnPrincipal > 0) {
+                this.periodNumbersApplicableForPrincipalGrace.add(applicablePeriodNumber);
+            }
+            if (graceOnInterest > 0) {
+                this.periodNumbersApplicableForInterestGrace.add(applicablePeriodNumber);
+            }
+            applicablePeriodNumber++;
+            graceOnPrincipal--;
+            graceOnInterest--;
+        }
+    }
+    
+    /**
+     * set the value to zero if the provided value is null
+     * 
+     * @return integer value equal/greater than 0
+     **/
+    private Integer defaultToZeroIfNull(Integer value) {
+
+        return (value != null) ? value : 0;
+    }
+    
+    public void updateExcludePeriodsForCalculation(Integer excludePeriodsForCalculation){
+        this.excludePeriodsForCalculation = excludePeriodsForCalculation;
+        this.extraPeriods = 0;
+    }
+    
+    public Integer getActualNoOfRepaymnets() {
+        return this.actualNumberOfRepayments;
+    }
+    
+    public Money getTotalInterestDue() {
+        return this.totalInterestDue;
+    }
+    
+    private void updateRecurringMoratoriumOnPrincipalPeriods(Integer periodNumber) {
+        Boolean isPrincipalGraceApplicableForThisPeriod = false;
+        Integer numberOfRepayments = this.actualNumberOfRepayments;
+        if (this.getRecurringMoratoriumOnPrincipalPeriods() > 0) {
+            while (numberOfRepayments > 0) {
+                isPrincipalGraceApplicableForThisPeriod = ((periodNumber > 0 && periodNumber <= getPrincipalGrace()) || (periodNumber > 0 && (((periodNumber - getPrincipalGrace()) % (this
+                        .getRecurringMoratoriumOnPrincipalPeriods() + 1)) != 1)));
+                if (isPrincipalGraceApplicableForThisPeriod) {
+                    this.periodNumbersApplicableForPrincipalGrace.add(periodNumber);
+                }
+                numberOfRepayments--;
+                periodNumber++;
+            }
+        }
+
+    }
+
+    
+    public void setTotalPrincipalAccounted(Money totalPrincipalAccounted) {
+        this.totalPrincipalAccountedForInterestCalcualtion = totalPrincipalAccounted;
+    }
+    
+    
+    //Used for FLAT loans to calculate principal and interest per installment  
+    public void updateAccountedTillPeriod(int periodNumber,  Money totalPrincipalAccounted,Money totalInterestAccounted, int extendPeriods){
+        this.periodsCompleted = periodNumber;
+        this.totalPrincipalAccounted = totalPrincipalAccounted;
+        this.totalInterestAccounted = totalInterestAccounted;
+        this.extraPeriods = this.extraPeriods + extendPeriods;
+    }
+    
+    public void updateTotalInterestAccounted(Money totalInterestAccounted){
+        this.totalInterestAccounted = totalInterestAccounted;
+    }
+    
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
index ae67451..9583ac4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java
@@ -19,22 +19,15 @@
 package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
 
 import java.math.MathContext;
-import java.util.List;
 import java.util.Set;
 
-import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
-import org.apache.fineract.portfolio.calendar.domain.Calendar;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
 import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
 import org.joda.time.LocalDate;
 
 public interface LoanScheduleGenerator {
@@ -42,18 +35,12 @@ public interface LoanScheduleGenerator {
     LoanScheduleModel generate(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
             final HolidayDetailDTO holidayDetailDTO);
 
-    LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
-            final HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> transactions,
-            LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, LocalDate rescheduleFrom);
+    LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Loan loan,
+            final HolidayDetailDTO holidayDetailDTO, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
+            LocalDate rescheduleFrom);
 
     LoanRepaymentScheduleInstallment calculatePrepaymentAmount(MonetaryCurrency currency, LocalDate onDate,
-            LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges, HolidayDetailDTO holidayDetailDTO,
-            List<LoanTransaction> loanTransactions, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
+            LoanApplicationTerms loanApplicationTerms, MathContext mc, Loan loan, HolidayDetailDTO holidayDetailDTO,
+            LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor);
 
-    LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
-            final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance,
-            CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, FloatingRateDTO floatingRateDTO,
-            final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays);
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index ac4a368..9565ff3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -86,7 +86,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
 import org.apache.fineract.portfolio.loanaccount.exception.MinDaysBetweenDisbursalAndFirstRepaymentViolationException;
@@ -621,9 +620,8 @@ public class LoanScheduleAssembler {
     }
 
     public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
-            List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
-            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom) {
+            Loan loan, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
+            final LocalDate rescheduleFrom) {
         final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
         final MathContext mc = new MathContext(8, roundingMode);
         final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
@@ -634,15 +632,13 @@ public class LoanScheduleAssembler {
 
         final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
         HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
-        return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loanCharges, detailDTO, transactions,
-                loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments, rescheduleFrom).getLoanScheduleModel();
+        return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loan, detailDTO,
+                loanRepaymentScheduleTransactionProcessor, rescheduleFrom).getLoanScheduleModel();
     }
 
     public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(MonetaryCurrency currency, LocalDate onDate,
-            LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final Long officeId,
-            List<LoanTransaction> loanTransactions,
-            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments) {
+            LoanApplicationTerms loanApplicationTerms, Loan loan, final Long officeId,
+            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
         final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
         final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
         final MathContext mc = new MathContext(8, roundingMode);
@@ -653,8 +649,8 @@ public class LoanScheduleAssembler {
         final WorkingDays workingDays = this.workingDaysRepository.findOne();
         HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
 
-        return loanScheduleGenerator.calculatePrepaymentAmount(currency, onDate, loanApplicationTerms, mc, loanCharges, holidayDetailDTO,
-                loanTransactions, loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments);
+        return loanScheduleGenerator.calculatePrepaymentAmount(currency, onDate, loanApplicationTerms, mc, loan, holidayDetailDTO,
+                loanRepaymentScheduleTransactionProcessor);
 
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
index 41e8313..a8ea977 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java
@@ -171,9 +171,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
         }
         LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(loan);
         LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(currency,
-                today, loanApplicationTerms, loan.charges(), loan.getOfficeId(),
-                loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
-                loan.getRepaymentScheduleInstallments());
+                today, loanApplicationTerms, loan, loan.getOfficeId(),
+                loanRepaymentScheduleTransactionProcessor);
         Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
                 loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
         Money interestDue = Money.zero(currency);
@@ -194,8 +193,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
         }
 
         LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
-                modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, loan.getRepaymentScheduleInstallments(),
-                loan.fetchInterestRecalculateFromDate());
+                loan, loanRepaymentScheduleTransactionProcessor, loan.fetchInterestRecalculateFromDate());
         LoanScheduleData scheduleDate = model.toData();
         Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
         for (LoanSchedulePeriodData periodData : periodDatas) {

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java
index 2094a05..591e21b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/api/RescheduleLoansApiResource.java
@@ -42,9 +42,9 @@ import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSeria
 import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestData;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanReschedulePreviewPlatformService;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -108,14 +108,14 @@ public class RescheduleLoansApiResource {
         final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
 
         if (compareIgnoreCase(command, "previewLoanReschedule")) {
-            final LoanRescheduleModel loanRescheduleModel = this.loanReschedulePreviewPlatformService.previewLoanReschedule(scheduleId);
+            final LoanScheduleModel loanRescheduleModel = this.loanReschedulePreviewPlatformService.previewLoanReschedule(scheduleId);
 
             return this.loanRescheduleToApiJsonSerializer.serialize(settings, loanRescheduleModel.toData(), new HashSet<String>());
         }
 
         final LoanRescheduleRequestData loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
                 .readLoanRescheduleRequest(scheduleId);
-
+        
         return this.loanRescheduleRequestToApiJsonSerializer.serialize(settings, loanRescheduleRequestData);
     }
 
@@ -170,4 +170,20 @@ public class RescheduleLoansApiResource {
     private boolean compareIgnoreCase(String firstString, String secondString) {
         return StringUtils.isNotBlank(firstString) && firstString.trim().equalsIgnoreCase(secondString);
     }
+    
+    /*@GET
+    @Path("{scheduleId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveTemplate(@Context final UriInfo uriInfo) {
+
+        this.platformSecurityContext.authenticatedUser().validateHasReadPermission(RescheduleLoansApiConstants.ENTITY_NAME);
+        final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+
+        LoanRescheduleRequestData loanRescheduleReasons = null;
+        loanRescheduleReasons = this.loanRescheduleRequestReadPlatformService
+                .retrieveAllRescheduleReasons(RescheduleLoansApiConstants.LOAN_RESCHEDULE_REASON);
+
+        return this.loanRescheduleRequestToApiJsonSerializer.serialize(settings, loanRescheduleReasons);
+    }*/
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java
index b74388c..2a6dfbd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestData.java
@@ -18,10 +18,10 @@
  */
 package org.apache.fineract.portfolio.loanaccount.rescheduleloan.data;
 
-import java.math.BigDecimal;
 import java.util.Collection;
 
 import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.joda.time.LocalDate;
 
 /**
@@ -36,59 +36,55 @@ public class LoanRescheduleRequestData {
     private final String loanAccountNumber;
     private final LoanRescheduleRequestStatusEnumData statusEnum;
     private final Integer rescheduleFromInstallment;
-    private final Integer graceOnPrincipal;
-    private final Integer graceOnInterest;
     private final LocalDate rescheduleFromDate;
-    private final Integer extraTerms;
-    private final BigDecimal interestRate;
     private final Boolean recalculateInterest;
     private final CodeValueData rescheduleReasonCodeValue;
     private final LoanRescheduleRequestTimelineData timeline;
     private final String rescheduleReasonComment;
-    private final LocalDate adjustedDueDate;
+    @SuppressWarnings("unused")
     private final Collection<CodeValueData> rescheduleReasons;
+    @SuppressWarnings("unused")
+    private final Collection<LoanTermVariationsData> loanTermVariationsData;
     
     /**
      * LoanRescheduleRequestData constructor
+     * @param loanTermVariationsData TODO
      **/
     private LoanRescheduleRequestData(Long id, Long loanId, LoanRescheduleRequestStatusEnumData statusEnum,
-            Integer rescheduleFromInstallment, Integer graceOnPrincipal, Integer graceOnInterest, LocalDate rescheduleFromDate,
-            LocalDate adjustedDueDate, Integer extraTerms, BigDecimal interestRate, CodeValueData rescheduleReasonCodeValue,
+            Integer rescheduleFromInstallment, LocalDate rescheduleFromDate, CodeValueData rescheduleReasonCodeValue,
             String rescheduleReasonComment, LoanRescheduleRequestTimelineData timeline, final String clientName,
-            final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest, Collection<CodeValueData> rescheduleReasons) {
+            final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest,
+            Collection<CodeValueData> rescheduleReasons, final Collection<LoanTermVariationsData> loanTermVariationsData) {
 
         this.id = id;
         this.loanId = loanId;
         this.statusEnum = statusEnum;
         this.rescheduleFromInstallment = rescheduleFromInstallment;
-        this.graceOnPrincipal = graceOnPrincipal;
-        this.graceOnInterest = graceOnInterest;
         this.rescheduleFromDate = rescheduleFromDate;
-        this.extraTerms = extraTerms;
-        this.interestRate = interestRate;
         this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
         this.rescheduleReasonComment = rescheduleReasonComment;
-        this.adjustedDueDate = adjustedDueDate;
         this.timeline = timeline;
         this.clientName = clientName;
         this.loanAccountNumber = loanAccountNumber;
         this.clientId = clientId;
         this.recalculateInterest = recalculateInterest;
         this.rescheduleReasons = rescheduleReasons ;
+        this.loanTermVariationsData = loanTermVariationsData;
     }
 
     /**
+     * @param loanTermVariationsData TODO
      * @return an instance of the LoanRescheduleRequestData class
      **/
     public static LoanRescheduleRequestData instance(Long id, Long loanId, LoanRescheduleRequestStatusEnumData statusEnum,
-            Integer rescheduleFromInstallment, Integer graceOnPrincipal, Integer graceOnInterest, LocalDate rescheduleFromDate,
-            LocalDate adjustedDueDate, Integer extraTerms, BigDecimal interestRate, CodeValueData rescheduleReasonCodeValue,
+            Integer rescheduleFromInstallment, LocalDate rescheduleFromDate, CodeValueData rescheduleReasonCodeValue,
             String rescheduleReasonComment, LoanRescheduleRequestTimelineData timeline, final String clientName,
-            final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest, Collection<CodeValueData> rescheduleReasons) {
+            final String loanAccountNumber, final Long clientId, final Boolean recalculateInterest,
+            Collection<CodeValueData> rescheduleReasons, final Collection<LoanTermVariationsData> loanTermVariationsData) {
 
-        return new LoanRescheduleRequestData(id, loanId, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
-                rescheduleFromDate, adjustedDueDate, extraTerms, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment,
-                timeline, clientName, loanAccountNumber, clientId, recalculateInterest, rescheduleReasons);
+        return new LoanRescheduleRequestData(id, loanId, statusEnum, rescheduleFromInstallment, rescheduleFromDate,
+                rescheduleReasonCodeValue, rescheduleReasonComment, timeline, clientName, loanAccountNumber, clientId, recalculateInterest,
+                rescheduleReasons, loanTermVariationsData);
     }
 
     /**
@@ -120,20 +116,6 @@ public class LoanRescheduleRequestData {
     }
 
     /**
-     * @return the graceOnPrincipal
-     */
-    public Integer getGraceOnPrincipal() {
-        return graceOnPrincipal;
-    }
-
-    /**
-     * @return the graceOnInterest
-     */
-    public Integer getGraceOnInterest() {
-        return graceOnInterest;
-    }
-
-    /**
      * @return the reschedule from date
      */
     public LocalDate getRescheduleFromDate() {
@@ -141,20 +123,6 @@ public class LoanRescheduleRequestData {
     }
 
     /**
-     * @return the extraTerms
-     */
-    public Integer getExtraTerms() {
-        return extraTerms;
-    }
-
-    /**
-     * @return the interestRate
-     */
-    public BigDecimal getInterestRate() {
-        return interestRate;
-    }
-
-    /**
      * @return the rescheduleReasonCodeValueId
      */
     public CodeValueData getRescheduleReasonCodeValueId() {
@@ -176,13 +144,6 @@ public class LoanRescheduleRequestData {
     }
 
     /**
-     * @return the adjustedDueDate
-     */
-    public LocalDate getAdjustedDueDate() {
-        return adjustedDueDate;
-    }
-
-    /**
      * @return the clientName
      */
     public String getClientName() {

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java
index 5254187..67e0f80 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/data/LoanRescheduleRequestDataValidator.java
@@ -32,10 +32,8 @@ import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidati
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.service.LoanRescheduleRequestReadPlatformService;
 import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -47,13 +45,10 @@ import com.google.gson.reflect.TypeToken;
 public class LoanRescheduleRequestDataValidator {
 
     private final FromJsonHelper fromJsonHelper;
-    private final LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService;
 
     @Autowired
-    public LoanRescheduleRequestDataValidator(FromJsonHelper fromJsonHelper,
-            LoanRescheduleRequestReadPlatformService loanRescheduleRequestReadPlatformService) {
+    public LoanRescheduleRequestDataValidator(FromJsonHelper fromJsonHelper) {
         this.fromJsonHelper = fromJsonHelper;
-        this.loanRescheduleRequestReadPlatformService = loanRescheduleRequestReadPlatformService;
     }
 
     /**
@@ -159,21 +154,8 @@ public class LoanRescheduleRequestDataValidator {
                         .failWithCode("repayment.schedule.installment.obligation.met", "Repayment schedule installment obligation met");
             }
 
-            if (installment != null && installment.isPartlyPaid()) {
-                dataValidatorBuilder.reset().parameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)
-                        .failWithCode("repayment.schedule.installment.partly.paid", "Repayment schedule installment is partly paid");
-            }
         }
 
-        if (loanId != null) {
-            List<LoanRescheduleRequestData> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
-                    .readLoanRescheduleRequests(loanId, LoanStatus.APPROVED.getValue());
-
-            if (loanRescheduleRequestData.size() > 0) {
-                dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled",
-                        "The loan can only be rescheduled once.");
-            }
-        }
         if(loan.isMultiDisburmentLoan()) {
             dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(RescheduleLoansApiConstants.resheduleForMultiDisbursementNotSupportedErrorCode,
                     "Loan rescheduling is not supported for multidisbursement loans");
@@ -232,7 +214,6 @@ public class LoanRescheduleRequestDataValidator {
         final Loan loan = loanRescheduleRequest.getLoan();
 
         if (loan != null) {
-            Long loanId = loan.getId();
 
             if (!loan.status().isActive()) {
                 dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.is.not.active", "Loan is not active");
@@ -251,16 +232,6 @@ public class LoanRescheduleRequestDataValidator {
                             "loan.repayment.schedule.installment." + "obligation.met", "Repayment schedule installment obligation met");
                 }
             }
-
-            if (loanId != null) {
-                List<LoanRescheduleRequestData> loanRescheduleRequestData = this.loanRescheduleRequestReadPlatformService
-                        .readLoanRescheduleRequests(loanId, LoanStatus.APPROVED.getValue());
-
-                if (loanRescheduleRequestData.size() > 0) {
-                    dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode("loan.already.rescheduled",
-                            "The loan can only be rescheduled once.");
-                }
-            }
         }
 
         if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java
deleted file mode 100644
index bcb7bc4..0000000
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
-
-import java.math.MathContext;
-
-import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
-import org.apache.fineract.portfolio.calendar.domain.Calendar;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
-import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
-import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator;
-import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator;
-import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
-
-public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
-
-    @Override
-    public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
-            final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
-            final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance,
-            final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, final FloatingRateDTO floatingRateDTO,
-            final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) {
-
-        LoanRescheduleModel loanRescheduleModel = null;
-
-        switch (interestMethod) {
-            case DECLINING_BALANCE:
-                loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
-                        applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
-                        floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays);
-            break;
-
-            case FLAT:
-                loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
-                        applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
-                        floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays);
-            break;
-
-            case INVALID:
-            break;
-        }
-
-        return loanRescheduleModel;
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequest.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequest.java
index 8893054..8a0a844 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequest.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequest.java
@@ -18,20 +18,27 @@
  */
 package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
 
-import java.math.BigDecimal;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
+import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 
 import org.apache.fineract.infrastructure.codes.domain.CodeValue;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.joda.time.LocalDate;
 import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -40,116 +47,97 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
 @Table(name = "m_loan_reschedule_request")
 public class LoanRescheduleRequest extends AbstractPersistable<Long> {
 	
-	@ManyToOne
+    @ManyToOne
     @JoinColumn(name = "loan_id", nullable = false)
     private Loan loan;
 	
-	@Column(name = "status_enum", nullable = false)
-	private Integer statusEnum;
-	
-	@Column(name = "reschedule_from_installment")
-	private Integer rescheduleFromInstallment;
-	
-	@Column(name = "grace_on_principal")
-	private Integer graceOnPrincipal;
-	
-	@Column(name = "grace_on_interest")
-	private Integer graceOnInterest;
-	
-	@Temporal(TemporalType.DATE)
+    @Column(name = "status_enum", nullable = false)
+    private Integer statusEnum;
+
+    @Column(name = "reschedule_from_installment")
+    private Integer rescheduleFromInstallment;
+
+    @Temporal(TemporalType.DATE)
     @Column(name = "reschedule_from_date")
-	private Date rescheduleFromDate;
-	
-	@Temporal(TemporalType.DATE)
-    @Column(name = "adjusted_due_date")
-	private Date adjustedDueDate;
-	
-	@Column(name = "extra_terms")
-	private Integer extraTerms;
-	
-	@Column(name = "recalculate_interest")
-	private Boolean recalculateInterest;
-	
-	@Column(name = "interest_rate", scale = 6, precision = 19)
-	private BigDecimal interestRate;
-	
-	@ManyToOne
+    private Date rescheduleFromDate;
+
+    @Column(name = "recalculate_interest")
+    private Boolean recalculateInterest;
+
+    @ManyToOne
     @JoinColumn(name = "reschedule_reason_cv_id")
-	private CodeValue rescheduleReasonCodeValue;
-	
-	@Column(name = "reschedule_reason_comment")
-	private String rescheduleReasonComment;
-	
-	@Temporal(TemporalType.DATE)
+    private CodeValue rescheduleReasonCodeValue;
+
+    @Column(name = "reschedule_reason_comment")
+    private String rescheduleReasonComment;
+
+    @Temporal(TemporalType.DATE)
     @Column(name = "submitted_on_date")
-	private Date submittedOnDate;
-	
-	@ManyToOne
+    private Date submittedOnDate;
+
+    @ManyToOne
     @JoinColumn(name = "submitted_by_user_id")
-	private AppUser submittedByUser;
-	
-	@Temporal(TemporalType.DATE)
+    private AppUser submittedByUser;
+
+    @Temporal(TemporalType.DATE)
     @Column(name = "approved_on_date")
-	private Date approvedOnDate;
+    private Date approvedOnDate;
 	
-	@ManyToOne
+    @ManyToOne
     @JoinColumn(name = "approved_by_user_id")
-	private AppUser approvedByUser;
+    private AppUser approvedByUser;
 	
-	@Temporal(TemporalType.DATE)
+    @Temporal(TemporalType.DATE)
     @Column(name = "rejected_on_date")
-	private Date rejectedOnDate;
+    private Date rejectedOnDate;
 	
-	@ManyToOne
+    @ManyToOne
     @JoinColumn(name = "rejected_by_user_id")
-	private AppUser rejectedByUser;
-	
-	/** 
-	 * LoanRescheduleRequest constructor
-	 **/
-	protected LoanRescheduleRequest() {}
-	
-	/** 
-	 * LoanRescheduleRequest constructor
-	 **/
-	private LoanRescheduleRequest(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment, 
-			final Integer graceOnPrincipal, final Integer graceOnInterest, final Date rescheduleFromDate, final Date adjustedDueDate,
-			final Integer extraTerms, final Boolean recalculateInterest, final BigDecimal interestRate, final CodeValue rescheduleReasonCodeValue, 
-			final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser, 
-			final Date approvedOnDate, final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
-		this.loan = loan;
-		this.statusEnum = statusEnum;
-		this.rescheduleFromInstallment = rescheduleFromInstallment;
-		this.graceOnPrincipal = graceOnPrincipal;
-		this.graceOnInterest = graceOnInterest;
-		this.rescheduleFromDate = rescheduleFromDate;
-		this.extraTerms = extraTerms;
-		this.interestRate = interestRate;
-		this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
-		this.rescheduleReasonComment = rescheduleReasonComment;
-		this.submittedOnDate = submittedOnDate;
-		this.submittedByUser = submittedByUser;
-		this.approvedOnDate = approvedOnDate;
-		this.approvedByUser = approvedByUser;
-		this.rejectedOnDate = rejectedOnDate;
-		this.rejectedByUser = rejectedByUser; 
-		this.adjustedDueDate = adjustedDueDate;
-		this.recalculateInterest = recalculateInterest;
-	}
-	
-	/** 
-	 * @return a new instance of the LoanRescheduleRequest class
-	 **/
-	public static LoanRescheduleRequest instance(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment, 
-			final Integer graceOnPrincipal, final Integer graceOnInterest, final Date rescheduleFromDate, final Date adjustedDueDate,
-			final Integer extraTerms, final Boolean recalculateInterest, final BigDecimal interestRate, final CodeValue rescheduleReasonCodeValue, 
-			final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser, 
-			final Date approvedOnDate, final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
-		
-		return new LoanRescheduleRequest(loan, statusEnum, rescheduleFromInstallment, graceOnPrincipal, graceOnInterest, 
-				rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate,
-				submittedByUser, approvedOnDate, approvedByUser, rejectedOnDate, rejectedByUser);
-	}
+    private AppUser rejectedByUser;
+
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER)
+    @JoinColumn(name = "loan_reschedule_request_id", nullable = false)
+    private Set<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = new HashSet<>();
+	
+    /**
+     * LoanRescheduleRequest constructor
+     **/
+    protected LoanRescheduleRequest() {}
+
+    /**
+     * LoanRescheduleRequest constructor
+     **/
+    private LoanRescheduleRequest(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
+            final Date rescheduleFromDate, final Boolean recalculateInterest, final CodeValue rescheduleReasonCodeValue,
+            final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser, final Date approvedOnDate,
+            final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
+        this.loan = loan;
+        this.statusEnum = statusEnum;
+        this.rescheduleFromInstallment = rescheduleFromInstallment;
+        this.rescheduleFromDate = rescheduleFromDate;
+        this.rescheduleReasonCodeValue = rescheduleReasonCodeValue;
+        this.rescheduleReasonComment = rescheduleReasonComment;
+        this.submittedOnDate = submittedOnDate;
+        this.submittedByUser = submittedByUser;
+        this.approvedOnDate = approvedOnDate;
+        this.approvedByUser = approvedByUser;
+        this.rejectedOnDate = rejectedOnDate;
+        this.rejectedByUser = rejectedByUser;
+        this.recalculateInterest = recalculateInterest;
+    }
+	
+    /**
+     * @return a new instance of the LoanRescheduleRequest class
+     **/
+    public static LoanRescheduleRequest instance(final Loan loan, final Integer statusEnum, final Integer rescheduleFromInstallment,
+            final Date rescheduleFromDate, final Boolean recalculateInterest, final CodeValue rescheduleReasonCodeValue,
+            final String rescheduleReasonComment, final Date submittedOnDate, final AppUser submittedByUser, final Date approvedOnDate,
+            final AppUser approvedByUser, final Date rejectedOnDate, AppUser rejectedByUser) {
+
+        return new LoanRescheduleRequest(loan, statusEnum, rescheduleFromInstallment, rescheduleFromDate, recalculateInterest,
+                rescheduleReasonCodeValue, rescheduleReasonComment, submittedOnDate, submittedByUser, approvedOnDate, approvedByUser,
+                rejectedOnDate, rejectedByUser);
+    }
 	
 	/** 
 	 * @return the reschedule request loan object 
@@ -173,20 +161,6 @@ public class LoanRescheduleRequest extends AbstractPersistable<Long> {
 	}
 	
 	/** 
-	 * @return the grace on principal 
-	 **/
-	public Integer getGraceOnPrincipal() {
-		return this.graceOnPrincipal;
-	}
-	
-	/** 
-	 * @return the grace on interest 
-	 **/
-	public Integer getGraceOnInterest() {
-		return this.graceOnInterest;
-	}
-	
-	/** 
 	 * @return due date of the rescheduling start point 
 	 **/
 	public LocalDate getRescheduleFromDate() {
@@ -201,34 +175,6 @@ public class LoanRescheduleRequest extends AbstractPersistable<Long> {
 	}
 	
 	/** 
-	 * @return due date of the first rescheduled installment
-	 **/
-	public LocalDate getAdjustedDueDate() {
-		
-		LocalDate localDate = null;
-		
-		if(this.adjustedDueDate != null) {
-			localDate = new LocalDate(this.adjustedDueDate);
-		}
-		
-		return localDate;
-	}
-	
-	/** 
-	 * @return extra terms to be added after the last loan installment 
-	 **/
-	public Integer getExtraTerms() {
-		return this.extraTerms;
-	}
-	
-	/** 
-	 * @return the new interest rate to be applied to unpaid installments 
-	 **/
-	public BigDecimal getInterestRate() {
-		return this.interestRate;
-	}
-	
-	/** 
 	 * @return the reschedule reason code value object 
 	 **/
 	public CodeValue getRescheduleReasonCodeValue() {
@@ -348,4 +294,24 @@ public class LoanRescheduleRequest extends AbstractPersistable<Long> {
 			this.statusEnum = LoanStatus.REJECTED.getValue();
 		}
 	}
+	
+    public void updateLoanRescheduleRequestToTermVariationMappings(final List<LoanRescheduleRequestToTermVariationMapping> mapping) {
+        this.loanRescheduleRequestToTermVariationMappings.addAll(mapping);
+    }
+    
+    public Set<LoanRescheduleRequestToTermVariationMapping> getLoanRescheduleRequestToTermVariationMappings() {
+        return this.loanRescheduleRequestToTermVariationMappings;
+    }
+
+	public LoanTermVariations getDueDateTermVariationIfExists() {
+		if(this.loanRescheduleRequestToTermVariationMappings != null
+				&& this.loanRescheduleRequestToTermVariationMappings.size() > 0){
+			for (LoanRescheduleRequestToTermVariationMapping mapping : this.loanRescheduleRequestToTermVariationMappings) {
+				if(mapping.getLoanTermVariations().getTermType().isDueDateVariation()){
+					return mapping.getLoanTermVariations();
+				}
+			}
+		}
+		return null;
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java
deleted file mode 100644
index f2e8bc7..0000000
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;
-
-import java.math.MathContext;
-
-import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
-import org.apache.fineract.portfolio.calendar.domain.Calendar;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
-import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
-import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
-
-public interface LoanReschedulerFactory {
-
-    public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
-            final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
-            final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance,
-            final Calendar loanCalendar, FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst,
-            final Integer numberofdays);
-}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformService.java
index b300b4d..f91649e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformService.java
@@ -18,9 +18,9 @@
  */
 package org.apache.fineract.portfolio.loanaccount.rescheduleloan.service;
 
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
 
 public interface LoanReschedulePreviewPlatformService {
 	
-	public LoanRescheduleModel previewLoanReschedule(Long requestId);
+	public LoanScheduleModel previewLoanReschedule(Long requestId);
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java
index 64040af..f6a918d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java
@@ -18,41 +18,33 @@
  */
 package org.apache.fineract.portfolio.loanaccount.rescheduleloan.service;
 
-import java.math.BigDecimal;
 import java.math.MathContext;
 import java.math.RoundingMode;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
-import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
-import org.apache.fineract.organisation.holiday.domain.Holiday;
-import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
-import org.apache.fineract.organisation.holiday.domain.HolidayStatusType;
-import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
-import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
-import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
-import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
-import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
-import org.apache.fineract.portfolio.calendar.domain.Calendar;
-import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
-import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
-import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException;
-import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
-import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
+import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
-import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRescheduleRequestToTermVariationMapping;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
+import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
 import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
-import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
-import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
+import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -60,103 +52,94 @@ import org.springframework.stereotype.Service;
 public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
 
     private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
-    private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
-    private final ConfigurationDomainService configurationDomainService;
-    private final HolidayRepository holidayRepository;
-    private final WorkingDaysRepositoryWrapper workingDaysRepository;
-    private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
-    private final CalendarInstanceRepository calendarInstanceRepository;
-    private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
     private final LoanUtilService loanUtilService;
+    private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
+    private final LoanScheduleGeneratorFactory loanScheduleFactory;
+    private final LoanSummaryWrapper loanSummaryWrapper;
+    private final DefaultScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
 
     @Autowired
     public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
-            final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
-            final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
-            final WorkingDaysRepositoryWrapper workingDaysRepository,
-            final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
-            final CalendarInstanceRepository calendarInstanceRepository,
-            final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService) {
+            final LoanUtilService loanUtilService,
+            final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
+            final LoanScheduleGeneratorFactory loanScheduleFactory, final LoanSummaryWrapper loanSummaryWrapper) {
         this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
-        this.applicationCurrencyRepository = applicationCurrencyRepository;
-        this.configurationDomainService = configurationDomainService;
-        this.holidayRepository = holidayRepository;
-        this.workingDaysRepository = workingDaysRepository;
-        this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
-        this.calendarInstanceRepository = calendarInstanceRepository;
-        this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
         this.loanUtilService = loanUtilService;
+        this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
+        this.loanScheduleFactory = loanScheduleFactory;
+        this.loanSummaryWrapper = loanSummaryWrapper;
     }
 
     @Override
-    public LoanRescheduleModel previewLoanReschedule(Long requestId) {
+    public LoanScheduleModel previewLoanReschedule(Long requestId) {
         final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId);
 
         if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(requestId); }
 
         Loan loan = loanRescheduleRequest.getLoan();
 
-        final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
-        final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
-                .getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
-        final WorkingDays workingDays = this.workingDaysRepository.findOne();
-        final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
-        final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
-        final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
-
-        final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
-        final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
-        final MathContext mathContext = new MathContext(8, roundingMode);
-        List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
-                loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
-        HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
-        CalendarInstance restCalendarInstance = null;
-        CalendarInstance compoundingCalendarInstance = null;
-        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
-            restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
-                    CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
-            compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
-                    loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
+        ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan,
+                loanRescheduleRequest.getRescheduleFromDate());
+        LocalDate rescheduleFromDate = null;
+        List<LoanTermVariationsData> removeLoanTermVariationsData = new ArrayList<>();
+        final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
+        LoanTermVariations dueDateVariationInCurrentRequest = loanRescheduleRequest.getDueDateTermVariationIfExists();
+        if(dueDateVariationInCurrentRequest != null){
+            for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
+                if (loanTermVariation.getDateValue().equals(dueDateVariationInCurrentRequest.fetchTermApplicaDate())) {
+                    rescheduleFromDate = loanTermVariation.getTermApplicableFrom();
+                    removeLoanTermVariationsData.add(loanTermVariation);
+                }
+            }
         }
-        final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
-                CalendarEntityType.LOANS.getValue());
-        Calendar loanCalendar = null;
-        if (loanCalendarInstance != null) {
-            loanCalendar = loanCalendarInstance.getCalendar();
+        loanApplicationTerms.getLoanTermVariations().getDueDateVariation().removeAll(removeLoanTermVariationsData);
+        if (rescheduleFromDate == null) {
+            rescheduleFromDate = loanRescheduleRequest.getRescheduleFromDate();
         }
-        final FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
-        Boolean isSkipRepaymentOnFirstMonth = false;
-        Integer numberOfDays = 0;
-        boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled();
-        if(isSkipRepaymentOnFirstMonthEnabled){
-            isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar);
-            if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); }
-            
+        List<LoanTermVariationsData> loanTermVariationsData = new ArrayList<>();
+        LocalDate adjustedApplicableDate = null;
+        Set<LoanRescheduleRequestToTermVariationMapping> loanRescheduleRequestToTermVariationMappings = loanRescheduleRequest.getLoanRescheduleRequestToTermVariationMappings();
+        if (!loanRescheduleRequestToTermVariationMappings.isEmpty()) {
+            for (LoanRescheduleRequestToTermVariationMapping loanRescheduleRequestToTermVariationMapping : loanRescheduleRequestToTermVariationMappings) {
+                if (loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().getTermType().isDueDateVariation()
+                        && rescheduleFromDate != null) {
+                    adjustedApplicableDate = loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().fetchDateValue();
+                    loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().setTermApplicableFrom(
+                            rescheduleFromDate.toDate());
+                }
+                loanTermVariationsData.add(loanRescheduleRequestToTermVariationMapping.getLoanTermVariations().toData());
+            }
         }
-        LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
-                loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
-                loanCalendar, floatingRateDTO, isSkipRepaymentOnFirstMonth, numberOfDays);
-        LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
-                oldPeriods);
-        return loanRescheduleModelWithOldPeriods;
-    }
-
-    private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
-        FloatingRateDTO floatingRateDTO = null;
-        if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
-            boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
-            BigDecimal interestRateDiff = loan.getInterestRateDifferential();
-            List<FloatingRatePeriodData> baseLendingRatePeriods = null;
-            try{
-            	baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
-            								.getRatePeriods();
-            }catch(final FloatingRateNotFoundException ex){
-            	// Do not do anything
+        
+        for (LoanTermVariationsData loanTermVariation : loanApplicationTerms.getLoanTermVariations().getDueDateVariation()) {
+            if (rescheduleFromDate.isBefore(loanTermVariation.getTermApplicableFrom())) {
+                LocalDate applicableDate = this.scheduledDateGenerator.generateNextRepaymentDate(rescheduleFromDate, loanApplicationTerms,
+                        false, loanApplicationTerms.getHolidayDetailDTO());
+                if (loanTermVariation.getTermApplicableFrom().equals(applicableDate)) {
+                    LocalDate adjustedDate = this.scheduledDateGenerator.generateNextRepaymentDate(adjustedApplicableDate,
+                            loanApplicationTerms, false, loanApplicationTerms.getHolidayDetailDTO());
+                    loanTermVariation.setApplicableFromDate(adjustedDate);
+                    loanTermVariationsData.add(loanTermVariation);
+                }
             }
-            floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
-                    baseLendingRatePeriods);
         }
-        return floatingRateDTO;
+        
+        loanApplicationTerms.getLoanTermVariations().updateLoanTermVariationsData(loanTermVariationsData);
+        final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
+        final MathContext mathContext = new MathContext(8, roundingMode);
+        final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
+                .determineProcessor(loan.transactionProcessingStrategy());
+        final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
+        final LoanLifecycleStateMachine loanLifecycleStateMachine = null;
+        loan.setHelpers(loanLifecycleStateMachine, this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
+        final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms,
+                loan, loanApplicationTerms.getHolidayDetailDTO(),
+                loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
+        final LoanScheduleModel loanScheduleModel = loanSchedule.getLoanScheduleModel();
+        LoanScheduleModel loanScheduleModels = LoanScheduleModel.withLoanScheduleModelPeriods(loanScheduleModel.getPeriods(),
+                loanScheduleModel);
+        
+        return loanScheduleModels;
     }
 
 }


[4/5] incubator-fineract git commit: FINERACT-202 : enabling the option for multi reshedule

Posted by na...@apache.org.
FINERACT-202 : enabling the option for multi reshedule


Project: http://git-wip-us.apache.org/repos/asf/incubator-fineract/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-fineract/commit/fe617123
Tree: http://git-wip-us.apache.org/repos/asf/incubator-fineract/tree/fe617123
Diff: http://git-wip-us.apache.org/repos/asf/incubator-fineract/diff/fe617123

Branch: refs/heads/develop
Commit: fe6171237e09cb1f347dc6577800ca4c665e361e
Parents: ada9ced
Author: venkatconflux <ve...@confluxtechnologies.com>
Authored: Fri Jul 29 18:22:46 2016 +0530
Committer: venkatconflux <ve...@confluxtechnologies.com>
Committed: Mon Aug 1 17:07:34 2016 +0530

----------------------------------------------------------------------
 api-docs/apiLive.htm                            |  90 ++-
 .../LoanRescheduleRequestTest.java              |   2 +-
 .../data/LoanTermVariationsData.java            |   6 +-
 .../data/LoanTermVariationsDataWrapper.java     |  78 +-
 .../portfolio/loanaccount/domain/Loan.java      |  55 +-
 .../domain/LoanAccountDomainService.java        |   2 +
 .../domain/LoanAccountDomainServiceJpa.java     |  23 +-
 .../LoanRepaymentScheduleInstallment.java       |   6 +
 ...RescheduleRequestToTermVariationMapping.java |  53 ++
 .../domain/LoanTermVariationType.java           |  34 +-
 .../loanaccount/domain/LoanTermVariations.java  |  43 +-
 .../loanaccount/domain/LoanTransaction.java     |   6 +
 .../domain/AbstractLoanScheduleGenerator.java   | 750 ++++++++-----------
 ...ingBalanceInterestLoanScheduleGenerator.java |   6 +-
 .../FlatInterestLoanScheduleGenerator.java      |   8 +-
 .../domain/LoanApplicationTerms.java            | 217 +++++-
 .../domain/LoanScheduleGenerator.java           |  25 +-
 .../service/LoanScheduleAssembler.java          |  20 +-
 ...nScheduleCalculationPlatformServiceImpl.java |   8 +-
 .../api/RescheduleLoansApiResource.java         |  22 +-
 .../data/LoanRescheduleRequestData.java         |  71 +-
 .../LoanRescheduleRequestDataValidator.java     |  31 +-
 .../domain/DefaultLoanReschedulerFactory.java   |  62 --
 .../domain/LoanRescheduleRequest.java           | 244 +++---
 .../domain/LoanReschedulerFactory.java          |  37 -
 .../LoanReschedulePreviewPlatformService.java   |   4 +-
 ...oanReschedulePreviewPlatformServiceImpl.java | 187 +++--
 ...escheduleRequestReadPlatformServiceImpl.java |  71 +-
 ...scheduleRequestWritePlatformServiceImpl.java | 488 ++++++------
 .../loanproduct/service/LoanEnumerations.java   |  16 +
 .../core_db/V313__multi_rescheduling_script.sql | 141 ++++
 31 files changed, 1496 insertions(+), 1310 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/api-docs/apiLive.htm
----------------------------------------------------------------------
diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index 696e066..7fe3fc9 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -11877,11 +11877,11 @@ Content-Type: application/json Request Body:
 {
 	"loanId": 1,
 	"graceOnPrincipal": 2,
-	"rescheduleFromDate": "04 December 2014",
-	"adjustedDueDate": "20 December 2014",
+	"rescheduleFromDate": "25 December 2013",
+	"adjustedDueDate": "31 December 2013",
 	"dateFormat": "dd MMMM yyyy",
 	"locale": "en",
-	"submittedOnDate": "04 September 2014",
+	"submittedOnDate": "04 September 2013",
 	"rescheduleReasonComment" : "Client has gone AWOL",
 	"rescheduleReasonId": 1
 }
@@ -11907,52 +11907,80 @@ GET https://DomainName/api/v1/rescheduleloans/{requestId}
 					</code>
 					<code class="method-response">
 {
-  "id": 2,
-  "loanId": 17,
-  "clientId": 4,
-  "clientName": "Fernando Michael Torres",
-  "loanAccountNumber": "000000017",
+  "id": 1,
+  "loanId": 1,
+  "clientId": 1,
+  "clientName": "test test",
+  "loanAccountNumber": "000000001",
   "statusEnum": {
-    "id": 500,
-    "code": "loanStatusType.rejected",
-    "value": "Rejected",
-    "pendingApproval": false,
+    "id": 100,
+    "code": "loanStatusType.submitted.and.pending.approval",
+    "value": "Submitted and pending approval",
+    "pendingApproval": true,
     "approved": false,
-    "rejected": true
+    "rejected": false
   },
   "rescheduleFromInstallment": 5,
-  "graceOnPrincipal": 3,
   "rescheduleFromDate": [
     2013,
     12,
-    6
+    25
   ],
   "recalculateInterest": false,
   "rescheduleReasonCodeValue": {
-    "id": 239,
-    "name": "client cannot pay on time"
+    "id": 1,
+    "name": "Passport",
+    "isActive": false
   },
   "timeline": {
     "submittedOnDate": [
-      2014,
-      8,
+      2013,
+      9,
       4
     ],
     "submittedByUsername": "mifos",
     "submittedByFirstname": "App",
-    "submittedByLastname": "Administrator",
-    "rejectedOnDate": [
-      2014,
-      8,
-      5
-    ],
-    "rejectedByUsername": "mifos",
-    "rejectedByFirstname": "App",
-    "rejectedByLastname": "Administrator"
+    "submittedByLastname": "Administrator"
   },
-  "rescheduleReasonComment": ""
-}
-					</code>
+  "rescheduleReasonComment": "Client has gone AWOL",
+  "loanTermVariationsData": [
+    {
+      "id": 13,
+      "termType": {
+        "id": 4,
+        "code": "loanTermType.dueDate",
+        "value": "dueDate"
+      },
+      "termVariationApplicableFrom": [
+        2013,
+        12,
+        25
+      ],
+      "dateValue": [
+        2013,
+        12,
+        31
+      ],
+      "isSpecificToInstallment": false
+    },
+    {
+      "id": 14,
+      "termType": {
+        "id": 8,
+        "code": "loanTermType.graceOnPrincipal",
+        "value": "graceOnPrincipal"
+      },
+      "termVariationApplicableFrom": [
+        2013,
+        12,
+        25
+      ],
+      "decimalValue": 2,
+      "isSpecificToInstallment": false
+    }
+  ]
+}			
+</code>
 				</div>
 			</div>
 			<a id="loan_reschedule_preview_retrieve" name="loan_reschedule_preview_retrieve" class="old-syle-anchor">&nbsp;</a>

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
index 2b00a98..ae39433 100644
--- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/LoanRescheduleRequestTest.java
@@ -211,7 +211,7 @@ public class LoanRescheduleRequestTest {
     	final HashMap loanSummary = this.loanTransactionHelper.getLoanSummary(requestSpec, generalResponseSpec, loanId);
     	final Float totalExpectedRepayment = (Float) loanSummary.get("totalExpectedRepayment");
     	
-    	assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "16", numberOfRepayments.toString());
+    	assertEquals("NUMBER OF REPAYMENTS SHOULD BE 16, NOT 12", "12", numberOfRepayments.toString());
     	assertEquals("TOTAL EXPECTED REPAYMENT MUST BE EQUAL TO 118000.0", "118000.0", totalExpectedRepayment.toString());
     	
     	System.out.println("Successfully approved loan reschedule request (ID: " + this.loanRescheduleRequestId + ")");

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java
index f5d2cc0..a19d3b1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsData.java
@@ -29,7 +29,7 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
     @SuppressWarnings("unused")
     private final Long id;
     private final EnumOptionData termType;
-    private final LocalDate termVariationApplicableFrom;
+    private LocalDate termVariationApplicableFrom;
     private final BigDecimal decimalValue;
     private final LocalDate dateValue;
     private final boolean isSpecificToInstallment;
@@ -113,5 +113,9 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
         }
         return comparsion;
     }
+    
+    public void setApplicableFromDate(final LocalDate applicableFromDate) {
+        this.termVariationApplicableFrom = applicableFromDate;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
index 65730e7..ea784e3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTermVariationsDataWrapper.java
@@ -22,37 +22,27 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
-
 import org.joda.time.LocalDate;
 
 public class LoanTermVariationsDataWrapper {
 
     private final List<LoanTermVariationsData> exceptionData;
-    private final ListIterator<LoanTermVariationsData> iterator;
+    private ListIterator<LoanTermVariationsData> iterator;
     private final List<LoanTermVariationsData> interestRateChanges;
+    private final List<LoanTermVariationsData> interestRateFromInstallment;
     private final List<LoanTermVariationsData> dueDateVariation;
-    private final ListIterator<LoanTermVariationsData> dueDateIterator;
+    private ListIterator<LoanTermVariationsData> dueDateIterator;
 
     public LoanTermVariationsDataWrapper(final List<LoanTermVariationsData> exceptionData) {
         if (exceptionData == null) {
             this.exceptionData = new ArrayList<>(1);
         } else {
             this.exceptionData = exceptionData;
-            Collections.sort(this.exceptionData);
         }
         this.interestRateChanges = new ArrayList<>();
         this.dueDateVariation = new ArrayList<>();
-        for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
-            if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) {
-                this.interestRateChanges.add(loanTermVariationsData);
-            } else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) {
-                this.dueDateVariation.add(loanTermVariationsData);
-            }
-        }
-        this.exceptionData.removeAll(this.interestRateChanges);
-        this.exceptionData.removeAll(this.dueDateVariation);
-        iterator = this.exceptionData.listIterator();
-        dueDateIterator = this.dueDateVariation.listIterator();
+        this.interestRateFromInstallment = new ArrayList<>();
+        deriveLoanTermVariations();
     }
 
     public boolean hasVariation(final LocalDate date) {
@@ -84,6 +74,10 @@ public class LoanTermVariationsDataWrapper {
     public LoanTermVariationsData nextDueDateVariation() {
         return this.dueDateIterator.next();
     }
+    
+    public LoanTermVariationsData previousDueDateVariation() {
+        return this.dueDateIterator.previous();
+    }
 
     public List<LoanTermVariationsData> getInterestRateChanges() {
         return this.interestRateChanges;
@@ -96,6 +90,23 @@ public class LoanTermVariationsDataWrapper {
     public List<LoanTermVariationsData> getExceptionData() {
         return this.exceptionData;
     }
+    
+    public void setExceptionData(final List<LoanTermVariationsData> exceptionData) {
+        clearTerms();
+        this.exceptionData.addAll(exceptionData);
+        deriveLoanTermVariations();
+    }
+
+    public void clearTerms() {
+        this.exceptionData.clear();
+        this.interestRateChanges.clear();
+        this.dueDateVariation.clear();
+        this.interestRateFromInstallment.clear();
+    }
+    
+    public List<LoanTermVariationsData> getInterestRateFromInstallment() {
+        return this.interestRateFromInstallment;
+    }
 
     public int adjustNumberOfRepayments() {
         int repaymetsForAdjust = 0;
@@ -108,7 +119,7 @@ public class LoanTermVariationsDataWrapper {
         }
         return repaymetsForAdjust;
     }
-
+    
     public LoanTermVariationsData fetchLoanTermDueDateVariationsData(final LocalDate onDate) {
         LoanTermVariationsData data = null;
         for (LoanTermVariationsData termVariationsData : this.dueDateVariation) {
@@ -125,4 +136,39 @@ public class LoanTermVariationsDataWrapper {
         return hasNext(date, iterator);
     }
 
+    public void updateLoanTermVariationsData(final List<LoanTermVariationsData> exceptionData){
+        if(this.exceptionData != null && exceptionData != null && exceptionData.size() > 0){
+            this.exceptionData.addAll(exceptionData);
+            deriveLoanTermVariations();
+        }
+    }
+    
+    private void deriveLoanTermVariations() {
+        Collections.sort(this.exceptionData);
+        for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
+            if (loanTermVariationsData.getTermVariationType().isInterestRateVariation()) {
+                this.interestRateChanges.add(loanTermVariationsData);
+            } else if (loanTermVariationsData.getTermVariationType().isDueDateVariation()) {
+                this.dueDateVariation.add(loanTermVariationsData);
+            } else if (loanTermVariationsData.getTermVariationType().isInterestRateFromInstallment()) {
+                this.interestRateFromInstallment.add(loanTermVariationsData);
+            }
+        }
+        Collections.sort(this.dueDateVariation);
+        this.exceptionData.removeAll(this.interestRateChanges);
+        this.exceptionData.removeAll(this.dueDateVariation);
+        this.exceptionData.removeAll(this.interestRateFromInstallment);
+        this.iterator = this.exceptionData.listIterator();
+        this.dueDateIterator = this.dueDateVariation.listIterator();
+    }
+    
+    public void resetVariations(){
+        
+        for (LoanTermVariationsData loanTermVariationsData : this.exceptionData) {
+            loanTermVariationsData.setProcessed(false);
+        }
+        this.iterator = this.exceptionData.listIterator();
+        this.dueDateIterator = this.dueDateVariation.listIterator();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 5bd7a0b..adb516e 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -2734,6 +2734,8 @@ public class Loan extends AbstractPersistable<Long> {
         final LoanStatus currentStatus = LoanStatus.fromInt(this.loanStatus);
         final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, currentStatus);
         validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_DISBURSAL_UNDO, getDisbursementDate());
+        existingTransactionIds.addAll(findExistingTransactionIds());
+        existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
         if (!statusEnum.hasStateOf(currentStatus)) {
             this.loanStatus = statusEnum.getValue();
             actualChanges.put("status", LoanEnumerations.status(this.loanStatus));
@@ -2750,6 +2752,7 @@ public class Loan extends AbstractPersistable<Long> {
                 }
             }
             boolean isEmiAmountChanged = this.loanTermVariations.size() > 0;
+            
             updateLoanToPreDisbursalState();
             if (isScheduleRegenerateRequired || isDisbursedAmountChanged || isEmiAmountChanged
                     || this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
@@ -2760,14 +2763,14 @@ public class Loan extends AbstractPersistable<Long> {
                 if (isDisbursedAmountChanged) {
                     updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
                 }
+            }else if(isPeriodicAccrualAccountingEnabledOnLoanProduct()){
+                for (final LoanRepaymentScheduleInstallment period : getRepaymentScheduleInstallments()) {
+                    period.resetAccrualComponents();
+                }
             }
 
             actualChanges.put("actualDisbursementDate", "");
 
-            existingTransactionIds.addAll(findExistingTransactionIds());
-            existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
-            this.accruedTill = null;
-            reverseExistingTransactions();
             updateLoanSummaryDerivedFields();
 
         }
@@ -2776,14 +2779,22 @@ public class Loan extends AbstractPersistable<Long> {
     }
 
     private final void reverseExistingTransactions() {
-
+        Collection<LoanTransaction> retainTransactions = new ArrayList<>();
         for (final LoanTransaction transaction : this.loanTransactions) {
             transaction.reverse();
+            if(transaction.getId() != null){
+                retainTransactions.add(transaction);
+            }
         }
+        this.loanTransactions.retainAll(retainTransactions);
+        isTransactionsListDirty = true ;
     }
 
     private void updateLoanToPreDisbursalState() {
         this.actualDisbursementDate = null;
+        
+        this.accruedTill = null;
+        reverseExistingTransactions();
 
         for (final LoanCharge charge : charges()) {
             if (charge.isOverdueInstallmentCharge()) {
@@ -2796,13 +2807,11 @@ public class Loan extends AbstractPersistable<Long> {
         for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
             currentInstallment.resetDerivedComponents();
         }
-        final List<LoanTermVariations> removeTerms = new ArrayList<>(this.loanTermVariations.size());
         for (LoanTermVariations variations : this.loanTermVariations) {
             if (variations.getOnLoanStatus().equals(LoanStatus.ACTIVE.getValue())) {
-                removeTerms.add(variations);
+                variations.markAsInactive();
             }
         }
-        this.loanTermVariations.removeAll(removeTerms);
         final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
         wrapper.reprocess(getCurrency(), getDisbursementDate(), getRepaymentScheduleInstallments(), charges());
 
@@ -5024,7 +5033,7 @@ public class Loan extends AbstractPersistable<Long> {
 
     }
 
-    private ChangedTransactionDetail processTransactions() {
+    public ChangedTransactionDetail processTransactions() {
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategy);
         final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
@@ -5292,9 +5301,8 @@ public class Loan extends AbstractPersistable<Long> {
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                 .determineProcessor(this.transactionProcessingStrategy);
 
-        return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(), generatorDTO.getHolidayDetailDTO(),
-                retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
-                getRepaymentScheduleInstallments(), generatorDTO.getRecalculateFrom());
+        return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, this, generatorDTO.getHolidayDetailDTO(),
+                loanRepaymentScheduleTransactionProcessor, generatorDTO.getRecalculateFrom());
     }
 
     public LoanRepaymentScheduleInstallment fetchPrepaymentDetail(final ScheduleGeneratorDTO scheduleGeneratorDTO, final LocalDate onDate) {
@@ -5310,9 +5318,8 @@ public class Loan extends AbstractPersistable<Long> {
             final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod);
             final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
                     .determineProcessor(this.transactionProcessingStrategy);
-            installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, charges(),
-                    scheduleGeneratorDTO.getHolidayDetailDTO(), retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
-                    loanRepaymentScheduleTransactionProcessor, getRepaymentScheduleInstallments());
+            installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this,
+                    scheduleGeneratorDTO.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor);
         } else {
             installment = this.getTotalOutstandingOnLoan();
         }
@@ -5377,10 +5384,12 @@ public class Loan extends AbstractPersistable<Long> {
         return loanApplicationTerms;
     }
 
-    private BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
+    public BigDecimal constructLoanTermVariations(FloatingRateDTO floatingRateDTO, BigDecimal annualNominalInterestRate,
             List<LoanTermVariationsData> loanTermVariations) {
         for (LoanTermVariations variationTerms : this.loanTermVariations) {
-            loanTermVariations.add(variationTerms.toData());
+            if(variationTerms.isActive()) {
+                loanTermVariations.add(variationTerms.toData());
+            }
         }
         annualNominalInterestRate = constructFloatingInterestRates(annualNominalInterestRate, floatingRateDTO, loanTermVariations);
         return annualNominalInterestRate;
@@ -6332,4 +6341,16 @@ public class Loan extends AbstractPersistable<Long> {
         return isForeClosure;
     }
 
+	public Set<LoanTermVariations> getActiveLoanTermVariations() {
+		Set<LoanTermVariations> retData = new HashSet<>();
+		if(this.loanTermVariations != null && this.loanTermVariations.size() > 0){
+			for (LoanTermVariations loanTermVariations : this.loanTermVariations) {
+				if(loanTermVariations.isActive()){
+					retData.add(loanTermVariations);
+				}
+			}
+		}
+        return retData.size()>0?retData:null;
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
index b1052c3..407a706 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
@@ -65,4 +65,6 @@ public interface LoanAccountDomainService {
      * @param loan {@link Loan} object
      */
     void disableStandingInstructionsLinkedToClosedLoan(Loan loan);
+
+    void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened);
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index a46b43f..f7b1a6b 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -447,15 +447,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
      */
     @Override
     public void recalculateAccruals(Loan loan) {
+        boolean isInterestCalcualtionHappened = loan.repaymentScheduleDetail().isInterestRecalculationEnabled();
+        recalculateAccruals(loan, isInterestCalcualtionHappened);
+    }
+
+    @Override
+    public void recalculateAccruals(Loan loan, boolean isInterestCalcualtionHappened) {
         LocalDate accruedTill = loan.getAccruedTill();
+        if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !isInterestCalcualtionHappened
+                || accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; }
+        
         boolean isOrganisationDateEnabled = this.configurationDomainService.isOrganisationstartDateEnabled();
         Date organisationStartDate = new Date();
         if(isOrganisationDateEnabled){
             organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate(); 
         }
-        
-        if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
-                || accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; }
         Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
         List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
         Long loanId = loan.getId();
@@ -697,17 +703,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
 
     }
 
-    private LoanRepaymentScheduleInstallment fetchLoanRepaymentScheduleInstallment(LocalDate fromDate, final Loan loan) {
-        LoanRepaymentScheduleInstallment installment = null;
-        for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : loan.getRepaymentScheduleInstallments()) {
-            if (fromDate.equals(loanRepaymentScheduleInstallment.getFromDate())) {
-                installment = loanRepaymentScheduleInstallment;
-                break;
-            }
-        }
-        return installment;
-    }
-
     private Map<BUSINESS_ENTITY, Object> constructEntityMap(final BUSINESS_ENTITY entityEvent, Object entity) {
         Map<BUSINESS_ENTITY, Object> map = new HashMap<>(1);
         map.put(entityEvent, entity);

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index d95d741..c31d1ec 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -344,6 +344,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
         this.obligationsMet = false;
         this.obligationsMetOnDate = null;
     }
+    
+    public void resetAccrualComponents() {
+        this.interestAccrued = null;
+        this.feeAccrued = null;
+        this.penaltyAccrued = null;
+    }
 
     public Money payPenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java
new file mode 100644
index 0000000..af9a8e2
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRescheduleRequestToTermVariationMapping.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.domain;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+@Entity
+@Table(name="m_loan_reschedule_request_term_variations_mapping")
+public class LoanRescheduleRequestToTermVariationMapping extends AbstractPersistable<Long> {
+    
+    
+    @ManyToOne(optional = false, cascade = CascadeType.PERSIST)
+    @JoinColumn(name = "loan_term_variations_id", nullable = false)
+    private LoanTermVariations loanTermVariations;
+    
+    protected LoanRescheduleRequestToTermVariationMapping(){
+        
+    }
+
+    private LoanRescheduleRequestToTermVariationMapping(final LoanTermVariations loanTermVariations) {
+        this.loanTermVariations = loanTermVariations;
+    }
+
+    public static LoanRescheduleRequestToTermVariationMapping createNew(final LoanTermVariations loanTermVariation) {
+        return new LoanRescheduleRequestToTermVariationMapping(loanTermVariation);
+    }
+    
+    public LoanTermVariations getLoanTermVariations() {
+        return this.loanTermVariations;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
index c0df3bb..0b12182 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariationType.java
@@ -26,7 +26,11 @@ public enum LoanTermVariationType {
     PRINCIPAL_AMOUNT(3, "loanTermType.principalAmount"), //
     DUE_DATE(4, "loanTermType.dueDate"), //
     INSERT_INSTALLMENT(5, "loanTermType.insertInstallment"), //
-    DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment");
+    DELETE_INSTALLMENT(6, "loanTermType.deleteInstallment"),
+    GRACE_ON_INTEREST(7, "loanTermType.graceOnInterest"),
+    GRACE_ON_PRINCIPAL(8, "loanTermType.graceOnPrincipal"),
+    EXTEND_REPAYMENT_PERIOD(9, "loanTermType.extendRepaymentPeriod"),
+    INTEREST_RATE_FROM_INSTALLMENT(10, "loanTermType.interestRateFromInstallment");
 
     private final Integer value;
     private final String code;
@@ -66,6 +70,18 @@ public enum LoanTermVariationType {
             case 6:
                 enumeration = LoanTermVariationType.DELETE_INSTALLMENT;
             break;
+            case 7:
+                enumeration = LoanTermVariationType.GRACE_ON_INTEREST;
+            break;
+            case 8:
+                enumeration = LoanTermVariationType.GRACE_ON_PRINCIPAL;
+            break;
+            case 9:
+                enumeration = LoanTermVariationType.EXTEND_REPAYMENT_PERIOD;
+            break;
+            case 10:
+                enumeration = LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT;
+            break;
         }
         return enumeration;
     }
@@ -93,4 +109,20 @@ public enum LoanTermVariationType {
     public boolean isDeleteInstallment() {
         return this.value.equals(LoanTermVariationType.DELETE_INSTALLMENT.getValue());
     }
+    
+    public boolean isGraceOnInterest() {
+        return this.value.equals(LoanTermVariationType.GRACE_ON_INTEREST.getValue());
+    }
+    
+    public boolean isGraceOnPrincipal() {
+        return this.value.equals(LoanTermVariationType.GRACE_ON_PRINCIPAL.getValue());
+    }
+    
+    public boolean isExtendRepaymentPeriod() {
+        return this.value.equals(LoanTermVariationType.EXTEND_REPAYMENT_PERIOD.getValue());
+    }
+    
+    public boolean isInterestRateFromInstallment() {
+        return this.value.equals(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT.getValue());
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
index 01245b9..6dbadad 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTermVariations.java
@@ -23,12 +23,13 @@ import java.util.Date;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
 import javax.persistence.Table;
 import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
-
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
@@ -62,6 +63,13 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
 
     @Column(name = "applied_on_loan_status", nullable = false)
     private Integer onLoanStatus;
+    
+    @Column(name = "is_active", nullable = false)
+    private Boolean isActive;
+    
+    @OneToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "parent_id")
+    private LoanTermVariations parent;
 
     public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
             final boolean isSpecificToInstallment, final Loan loan) {
@@ -72,6 +80,8 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
         this.dateValue = dateValue;
         this.isSpecificToInstallment = isSpecificToInstallment;
         this.onLoanStatus = loan.status().getValue();
+        this.isActive = true;
+        this.parent = null;
     }
     
     public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
@@ -83,6 +93,21 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
         this.dateValue = dateValue;
         this.isSpecificToInstallment = isSpecificToInstallment;
         this.onLoanStatus = loanStatus;
+        this.isActive = true;
+        this.parent = null;
+    }
+    
+    public LoanTermVariations(final Integer termType, final Date termApplicableFrom, final BigDecimal decimalValue, final Date dateValue,
+            final boolean isSpecificToInstallment, final Loan loan, final Integer loanStatus, final Boolean isActive, final LoanTermVariations parent) {
+        this.loan = loan;
+        this.termApplicableFrom = termApplicableFrom;
+        this.termType = termType;
+        this.decimalValue = decimalValue;
+        this.dateValue = dateValue;
+        this.isSpecificToInstallment = isSpecificToInstallment;
+        this.onLoanStatus = loanStatus;
+        this.isActive = isActive;
+        this.parent = parent;
     }
 
     protected LoanTermVariations() {
@@ -135,5 +160,21 @@ public class LoanTermVariations extends AbstractPersistable<Long> {
     public Integer getOnLoanStatus() {
         return this.onLoanStatus;
     }
+    
+    public Boolean isActive() {
+        return this.isActive;
+    }
+
+    public LoanTermVariations parent() {
+        return this.parent;
+    }
+    
+    public void updateIsActive(final Boolean isActive){
+        this.isActive = isActive;
+    }
+    
+    public void markAsInactive() {
+        this.isActive = false;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index 414ca14..7b16a5c 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -777,4 +777,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
     public boolean isAccrualTransaction() {
         return isAccrual();
     }
+    
+    public boolean isPaymentTransaction() {
+        return this.isNotReversed()
+                && !(this.isDisbursement() || this.isAccrual() || this.isRepaymentAtDisbursement() || this.isNonMonetaryTransaction() || this
+                        .isIncomePosting());
+    }
 }
\ No newline at end of file



[3/5] incubator-fineract git commit: FINERACT-202 : enabling the option for multi reshedule

Posted by na...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index d9e57a0..b380b34 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -22,7 +22,6 @@ import java.math.BigDecimal;
 import java.math.MathContext;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -36,11 +35,9 @@ import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
-import org.apache.fineract.portfolio.calendar.domain.Calendar;
 import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
 import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
 import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
-import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO;
 import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
 import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
@@ -48,7 +45,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
@@ -56,10 +52,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.ScheduleDateException;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
-import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
-import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
 import org.joda.time.Days;
 import org.joda.time.LocalDate;
 
@@ -96,7 +88,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
 
         final MonetaryCurrency currency = loanApplicationTerms.getCurrency();
         final int numberOfRepayments = loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions();
-
         LoanScheduleParams scheduleParams = null;
         if (loanScheduleParams == null) {
             scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency, Money.of(currency, chargesDueAtTimeOfDisbursement),
@@ -114,14 +105,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = scheduleParams
                 .getLoanRepaymentScheduleTransactionProcessor();
 
-        final Collection<LoanScheduleModelPeriod> periods = createNewLoanScheduleListWithDisbursementDetails(numberOfRepayments,
-                loanApplicationTerms, chargesDueAtTimeOfDisbursement);
+        Collection<LoanScheduleModelPeriod> periods = new ArrayList<>();
+        if(!scheduleParams.isPartialUpdate()) {
+            periods = createNewLoanScheduleListWithDisbursementDetails(numberOfRepayments,
+                    loanApplicationTerms, chargesDueAtTimeOfDisbursement);
+        }
 
         // Determine the total interest owed over the full loan for FLAT
         // interest method .
-        final Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
-                this.paymentPeriodsInOneYearCalculator, mc);
-
+        if (!scheduleParams.isPartialUpdate()) {
+            Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
+                    this.paymentPeriodsInOneYearCalculator, mc);
+            loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
+        }
+        
         boolean isFirstRepayment = true;
         LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
                 loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
@@ -167,6 +164,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
         }
 
         final Collection<LoanTermVariationsData> interestRates = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges();
+        final Collection<LoanTermVariationsData> interestRatesForInstallments = loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment();
 
         // this block is to start the schedule generation from specified date
         if (scheduleParams.isPartialUpdate()) {
@@ -174,7 +172,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                 loanApplicationTerms.setPrincipal(scheduleParams.getPrincipalToBeScheduled());
             }
 
-            applyLoanVariationsForPartialScheduleGenerate(loanApplicationTerms, scheduleParams, interestRates);
+            applyLoanVariationsForPartialScheduleGenerate(loanApplicationTerms, scheduleParams, interestRates, interestRatesForInstallments);
 
             isFirstRepayment = false;
         }
@@ -185,7 +183,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             isFirstRepayment = false;
             LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(scheduleParams.getActualRepaymentDate(),
                     loanApplicationTerms, holidayDetailDTO);
-
+            
             // calculated interest start date for the period
             LocalDate periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms,
                     scheduleParams.getPeriodStartDate(), idealDisbursementDate, firstRepaymentdate,
@@ -194,7 +192,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
 
             // Loan Schedule Exceptions that need to be applied for Loan Account
             LoanTermVariationParams termVariationParams = applyLoanTermVariations(loanApplicationTerms, scheduleParams,
-                    previousRepaymentDate, scheduledDueDate);
+                    previousRepaymentDate, scheduledDueDate, interestRatesForInstallments, this.paymentPeriodsInOneYearCalculator, mc);
 
             scheduledDueDate = termVariationParams.getScheduledDueDate();
             // Updates total days in term
@@ -244,7 +242,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             // for a loan repayment which falls between the
             // two periods for interest first repayment strategies
             handleRecalculationForNonDueDateTransactions(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, scheduleParams, periods,
-                    totalInterestChargedForFullLoanTerm, idealDisbursementDate, firstRepaymentdate, lastRestDate, scheduledDueDate,
+                    loanApplicationTerms.getTotalInterestDue(), idealDisbursementDate, firstRepaymentdate, lastRestDate, scheduledDueDate,
                     periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams);
 
             if (currentPeriodParams.isSkipCurrentLoop()) {
@@ -262,10 +260,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                     this.paymentPeriodsInOneYearCalculator, currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
                     scheduleParams.getTotalCumulativePrincipal().minus(scheduleParams.getReducePrincipal()),
-                    scheduleParams.getTotalCumulativeInterest(), totalInterestChargedForFullLoanTerm,
+                    scheduleParams.getTotalCumulativeInterest(), loanApplicationTerms.getTotalInterestDue(),
                     scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(), scheduleParams.getOutstandingBalanceAsPerRest(),
                     loanApplicationTerms, scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
                     scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, scheduledDueDate, interestRates);
+            
 
             // will check for EMI amount greater than interest calculated
             if (loanApplicationTerms.getFixedEmiAmount() != null
@@ -320,7 +319,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             // apply loan transactions on installments to identify early/late
             // payments for interest recalculation
             installment = handleRecalculationForTransactions(mc, loanApplicationTerms, holidayDetailDTO, currency, scheduleParams,
-                    loanRepaymentScheduleTransactionProcessor, totalInterestChargedForFullLoanTerm, lastRestDate, scheduledDueDate,
+                    loanRepaymentScheduleTransactionProcessor, loanApplicationTerms.getTotalInterestDue(), lastRestDate, scheduledDueDate,
                     periodStartDateApplicableForInterest, applicableTransactions, currentPeriodParams,
                     lastTotalOutstandingInterestPaymentDueToGrace, installment, loanCharges);
             periods.add(installment);
@@ -960,11 +959,18 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
      * @param priviousScheduledDueDate
      * @param previousRepaymentDate
      * @param scheduledDueDate
+     * @param interestRatesForInstallments 
+     * @param mc 
+     * @param paymentPeriodsInOneYearCalculator 
+     * @param interestRates 
+     * @param periodsApplicableForGrace 
      * @param scheduleDateForReversal
      * @return
      */
     private LoanTermVariationParams applyLoanTermVariations(final LoanApplicationTerms loanApplicationTerms,
-            final LoanScheduleParams scheduleParams, final LocalDate previousRepaymentDate, final LocalDate scheduledDueDate) {
+            final LoanScheduleParams scheduleParams, final LocalDate previousRepaymentDate, final LocalDate scheduledDueDate, 
+            Collection<LoanTermVariationsData> interestRatesForInstallments, PaymentPeriodsInOneYearCalculator calculator, 
+            MathContext mc) {
         boolean skipPeriod = false;
         boolean recalculateAmounts = false;
         LocalDate modifiedScheduledDueDate = scheduledDueDate;
@@ -981,7 +987,28 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                 loanTermVariationsData.setProcessed(true);
             }
         }
+        
+        for (LoanTermVariationsData variation : interestRatesForInstallments) {
+            if (variation.isApplicable(modifiedScheduledDueDate) && variation.getDecimalValue() != null && !variation.isProcessed()) {
+                loanApplicationTerms.updateAnnualNominalInterestRate(variation.getDecimalValue());
+                if (loanApplicationTerms.getInterestMethod().isDecliningBalnce()) {
+                    if (loanApplicationTerms.getActualFixedEmiAmount() == null) {
+                        loanApplicationTerms.setFixedEmiAmount(null);
+                    }
+                } else {
+                    Money totalInterestDueForLoan = Money.zero(loanApplicationTerms.getCurrency());
+                    loanApplicationTerms.setTotalPrincipalAccounted(scheduleParams.getTotalCumulativePrincipal());
+                    totalInterestDueForLoan = loanApplicationTerms.calculateTotalInterestCharged(calculator, mc);
+                    totalInterestDueForLoan = totalInterestDueForLoan.plus(scheduleParams.getTotalCumulativeInterest());
+                    loanApplicationTerms.updateTotalInterestDue(totalInterestDueForLoan);
+                    // exclude till last period in calculations
+                    loanApplicationTerms.updateExcludePeriodsForCalculation(scheduleParams.getPeriodNumber() - 1);
 
+                }
+                variation.setProcessed(true);
+            }
+        }
+        
         while (loanApplicationTerms.getLoanTermVariations().hasVariation(modifiedScheduledDueDate)) {
             LoanTermVariationsData loanTermVariationsData = loanApplicationTerms.getLoanTermVariations().nextVariation();
             if (loanTermVariationsData.isProcessed()) {
@@ -1026,6 +1053,33 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                     }
                     loanTermVariationsData.setProcessed(true);
                 break;
+                case EXTEND_REPAYMENT_PERIOD:
+                    Integer rescheduleNumberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
+                    rescheduleNumberOfRepayments += loanTermVariationsData.getDecimalValue().intValue();
+                    loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments);
+                    LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, loanApplicationTerms.getHolidayDetailDTO());
+                    loanApplicationTerms.updateLoanEndDate(loanEndDate);
+                    loanApplicationTerms.updateAccountedTillPeriod(scheduleParams.getPeriodNumber() - 1,
+                            scheduleParams.getTotalCumulativePrincipal(), scheduleParams.getTotalCumulativeInterest(),
+                            loanTermVariationsData.getDecimalValue().intValue());
+                    loanApplicationTerms.setFixedEmiAmount(null);
+                    loanTermVariationsData.setProcessed(true);
+                break;
+                case GRACE_ON_PRINCIPAL:
+                    loanApplicationTerms.updatePrincipalGrace(loanTermVariationsData.getDecimalValue().intValue());
+                    Integer interestPaymentGrace = 0;
+                    loanApplicationTerms.updateInterestPaymentGrace(interestPaymentGrace);
+                    loanApplicationTerms.updatePeriodNumberApplicableForPrincipalOrInterestGrace(scheduleParams.getPeriodNumber());
+                    loanTermVariationsData.setProcessed(true);
+                break;
+                case GRACE_ON_INTEREST:
+                    loanApplicationTerms.updateInterestPaymentGrace(loanTermVariationsData.getDecimalValue().intValue());
+                    Integer principalGrace = 0;
+                    loanApplicationTerms.updatePrincipalGrace(principalGrace);
+                    loanApplicationTerms.updatePeriodNumberApplicableForPrincipalOrInterestGrace(scheduleParams.getPeriodNumber());
+                    loanApplicationTerms.updateTotalInterestAccounted(scheduleParams.getTotalCumulativeInterest());
+                    loanTermVariationsData.setProcessed(true);
+                break;
                 default:
                 break;
 
@@ -1040,14 +1094,33 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
      * @param loanApplicationTerms
      * @param scheduledDueDate
      * @param exceptionDataListIterator
+     * @param instalmentNumber 
+     * @param totalCumulativePrincipal TODO
+     * @param totalCumulativeInterest TODO
+     * @param mc TODO
+     * @param periodNumber 
      * @return
      */
     private LoanTermVariationParams applyExceptionLoanTermVariations(final LoanApplicationTerms loanApplicationTerms,
-            final LocalDate scheduledDueDate, final ListIterator<LoanTermVariationsData> exceptionDataListIterator) {
+            final LocalDate scheduledDueDate, final ListIterator<LoanTermVariationsData> exceptionDataListIterator, int instalmentNumber,
+            Money totalCumulativePrincipal, Money totalCumulativeInterest, MathContext mc) {
         boolean skipPeriod = false;
         boolean recalculateAmounts = false;
         LocalDate modifiedScheduledDueDate = scheduledDueDate;
         ArrayList<LoanTermVariationsData> variationsData = new ArrayList<>();
+        
+        for (LoanTermVariationsData variation : loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment()) {
+            if (variation.isApplicable(modifiedScheduledDueDate) && variation.getDecimalValue() != null && !variation.isProcessed()) {
+                loanApplicationTerms.updateAnnualNominalInterestRate(variation.getDecimalValue());
+                if (loanApplicationTerms.getInterestMethod().isDecliningBalnce()) {
+                    adjustInstallmentOrPrincipalAmount(loanApplicationTerms, totalCumulativePrincipal, instalmentNumber, mc);
+                } else {
+                    loanApplicationTerms.setTotalPrincipalAccounted(totalCumulativePrincipal);
+                    loanApplicationTerms.updateExcludePeriodsForCalculation(instalmentNumber - 1);
+                }
+                variation.setProcessed(true);
+            }
+        }
 
         while (loanApplicationTerms.getLoanTermVariations().hasExceptionVariation(modifiedScheduledDueDate, exceptionDataListIterator)) {
             LoanTermVariationsData loanTermVariationsData = exceptionDataListIterator.next();
@@ -1057,14 +1130,40 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             switch (loanTermVariationsData.getTermVariationType()) {
                 case INSERT_INSTALLMENT:
                     modifiedScheduledDueDate = loanTermVariationsData.getTermApplicableFrom();
-                    variationsData.add(loanTermVariationsData) ;
+                    variationsData.add(loanTermVariationsData);
                 break;
                 case DELETE_INSTALLMENT:
                     if (loanTermVariationsData.getTermApplicableFrom().isEqual(modifiedScheduledDueDate)) {
                         skipPeriod = true;
-                        variationsData.add(loanTermVariationsData) ;
+                        variationsData.add(loanTermVariationsData);
                     }
-                    
+                break;
+                case GRACE_ON_PRINCIPAL:
+                    loanApplicationTerms.updatePrincipalGrace(loanTermVariationsData.getDecimalValue().intValue());
+                    Integer interestPaymentGrace = 0;
+                    loanApplicationTerms.updateInterestPaymentGrace(interestPaymentGrace);
+                    loanApplicationTerms.updatePeriodNumberApplicableForPrincipalOrInterestGrace(instalmentNumber);
+                    variationsData.add(loanTermVariationsData);
+                break;
+                case GRACE_ON_INTEREST:
+                    loanApplicationTerms.updateInterestPaymentGrace(loanTermVariationsData.getDecimalValue().intValue());
+                    Integer principalGrace = 0;
+                    loanApplicationTerms.updatePrincipalGrace(principalGrace);
+                    loanApplicationTerms.updatePeriodNumberApplicableForPrincipalOrInterestGrace(instalmentNumber);
+                    loanApplicationTerms.updateTotalInterestAccounted(totalCumulativeInterest);
+                    variationsData.add(loanTermVariationsData);
+                break;
+                case EXTEND_REPAYMENT_PERIOD:
+                    Integer rescheduleNumberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
+                    rescheduleNumberOfRepayments += loanTermVariationsData.getDecimalValue().intValue();
+                    loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments);
+                    // generate list of proposed schedule due dates
+                    LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms,
+                            loanApplicationTerms.getHolidayDetailDTO());
+                    loanApplicationTerms.updateLoanEndDate(loanEndDate);
+                    adjustInstallmentOrPrincipalAmount(loanApplicationTerms, totalCumulativePrincipal, instalmentNumber, mc);
+                    loanTermVariationsData.setProcessed(true);
+                    loanApplicationTerms.updateAccountedTillPeriod(instalmentNumber-1, totalCumulativePrincipal, totalCumulativeInterest, loanTermVariationsData.getDecimalValue().intValue());
                 break;
                 default:
                 break;
@@ -1080,9 +1179,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
      * @param loanApplicationTerms
      * @param scheduleParams
      * @param interestRates
+     * @param interestRatesForInstallments 
+     * @param periodsApplicableForGrace 
      */
     private void applyLoanVariationsForPartialScheduleGenerate(final LoanApplicationTerms loanApplicationTerms,
-            LoanScheduleParams scheduleParams, final Collection<LoanTermVariationsData> interestRates) {
+            LoanScheduleParams scheduleParams, final Collection<LoanTermVariationsData> interestRates, 
+            final Collection<LoanTermVariationsData> interestRatesForInstallments) {
         // Applies loan variations
         while (loanApplicationTerms.getLoanTermVariations().hasVariation(scheduleParams.getPeriodStartDate())) {
             LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextVariation();
@@ -1109,6 +1211,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                 loanApplicationTerms.updateAnnualNominalInterestRate(variation.getDecimalValue());
             }
         }
+        
+        // Applies interest rate changes for installments
+        for (LoanTermVariationsData variation : interestRatesForInstallments) {
+            if (variation.getTermVariationType().isInterestRateFromInstallment() && variation.isApplicable(scheduleParams.getPeriodStartDate())
+                    && variation.getDecimalValue() != null) {
+                loanApplicationTerms.updateAnnualNominalInterestRate(variation.getDecimalValue());
+                variation.setProcessed(true);
+            }
+        }
     }
 
     /**
@@ -1189,6 +1300,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
         Collection<LoanTermVariationsData> applicableVariations = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges();
         Money uncompoundedFromLastInstallment = params.getUnCompoundedAmount();
         LocalDate additionalPeriodsStartDate = params.getPeriodStartDate();
+
         do {
 
             params.setActualRepaymentDate(this.scheduledDateGenerator.generateNextRepaymentDate(params.getActualRepaymentDate(),
@@ -1327,18 +1439,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
         return applicableTransactions;
     }
 
-    private Collection<LoanTermVariationsData> getApplicableTermVariationsForPeriod(final LocalDate fromDate, final LocalDate dueDate,
-            final Collection<LoanTermVariationsData> variations) {
-        Collection<LoanTermVariationsData> applicableVariations = new ArrayList<>();
-        for (LoanTermVariationsData detail : variations) {
-            if (detail.isApplicable(fromDate, dueDate)) {
-                applicableVariations.add(detail);
-            }
-        }
-        variations.removeAll(applicableVariations);
-        return applicableVariations;
-    }
-
     private List<LoanTransaction> createCurrentTransactionList(RecalculationDetail detail) {
         List<LoanTransaction> currentTransactions = new ArrayList<>(2);
         currentTransactions.add(detail.getTransaction());
@@ -1511,7 +1611,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                     final boolean isPastDate = params.applyInterestRecalculation()
                             && loanRepaymentScheduleInstallment.getDueDate().isBefore(DateUtils.getLocalDateOfTenant());
                     boolean periodHasCompoundingDate = false;
-                    Money amountCharged = getIncomeForCompounding(loanApplicationTerms, currency, loanRepaymentScheduleInstallment);
+                    Money amountCharged = Money.zero(currency);
+                    if (loanApplicationTerms.getInterestRecalculationCompoundingMethod() != null) {
+                        amountCharged = getIncomeForCompounding(loanApplicationTerms, currency, loanRepaymentScheduleInstallment);
+                    }
                     final Map<LocalDate, Money> compoundingMap = params.getCompoundingMap();
                     LocalDate effectiveStartDate = loanRepaymentScheduleInstallment.getFromDate();
                     if (loanApplicationTerms.allowCompoundingOnEod()) {
@@ -1756,316 +1859,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
 
     }
 
-    @Override
-    public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
-            final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO,
-            final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar,
-            final FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) {
-
-        final Loan loan = loanRescheduleRequest.getLoan();
-        final LoanSummary loanSummary = loan.getSummary();
-        final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
-        final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
-
-        // create an archive of the current loan schedule installments
-        Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = null;
-
-        // get the initial list of repayment installments
-        List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
-
-        // sort list by installment number in ASC order
-        Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator);
-
-        final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<>();
-
-        Money outstandingLoanBalance = loan.getPrincpal();
-
-        for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
-
-            Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber();
-            LocalDate fromDate = repaymentScheduleInstallment.getFromDate();
-            LocalDate dueDate = repaymentScheduleInstallment.getDueDate();
-            Money principalDue = repaymentScheduleInstallment.getPrincipal(currency);
-            Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency);
-            Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency);
-            Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency);
-            Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
-
-            outstandingLoanBalance = outstandingLoanBalance.minus(principalDue);
-
-            LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod
-                    .instance(oldPeriodNumber, oldPeriodNumber, fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue,
-                            feeChargesDue, penaltyChargesDue, totalDue, false);
-
-            periods.add(period);
-        }
-
-        Money outstandingBalance = loan.getPrincpal();
-        Money totalCumulativePrincipal = Money.zero(currency);
-        Money totalCumulativeInterest = Money.zero(currency);
-        Money actualTotalCumulativeInterest = Money.zero(currency);
-        Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
-        Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency);
-
-        LocalDate installmentDueDate = null;
-        LocalDate adjustedInstallmentDueDate = null;
-        LocalDate installmentFromDate = null;
-        Integer rescheduleFromInstallmentNo = defaultToZeroIfNull(loanRescheduleRequest.getRescheduleFromInstallment());
-        Integer installmentNumber = rescheduleFromInstallmentNo;
-        Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
-        Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
-        Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms());
-        final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest();
-        Integer numberOfRepayments = repaymentScheduleInstallments.size();
-        Integer rescheduleNumberOfRepayments = numberOfRepayments;
-        final Money principal = loan.getPrincpal();
-        final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding());
-        LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate();
-        BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate();
-        int loanTermInDays = Integer.valueOf(0);
-
-        if (rescheduleFromInstallmentNo > 0) {
-            // this will hold the loan repayment installment that is before the
-            // reschedule start installment
-            // (rescheduleFrominstallment)
-            LoanRepaymentScheduleInstallment previousInstallment = null;
-
-            // get the install number of the previous installment
-            int previousInstallmentNo = rescheduleFromInstallmentNo - 1;
-
-            // only fetch the installment if the number is greater than 0
-            if (previousInstallmentNo > 0) {
-                previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo);
-            }
-
-            LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1);
-
-            // the "installment from date" is equal to the due date of the
-            // previous installment, if it exists
-            if (previousInstallment != null) {
-                installmentFromDate = previousInstallment.getDueDate();
-            }
-
-            else {
-                installmentFromDate = firstInstallment.getFromDate();
-            }
-
-            installmentDueDate = installmentFromDate;
-            LocalDate periodStartDateApplicableForInterest = installmentFromDate;
-            Integer periodNumber = 1;
-            outstandingLoanBalance = loan.getPrincpal();
-
-            for (LoanRescheduleModelRepaymentPeriod period : periods) {
-
-                if (period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) {
-
-                    totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod.plus(period.principalDue());
-                    actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
-                    rescheduleNumberOfRepayments--;
-                    outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
-                    outstandingBalance = outstandingBalance.minus(period.principalDue());
-                }
-            }
-
-            while (graceOnPrincipal > 0 || graceOnInterest > 0) {
-
-                LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
-                        new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
-                        Money.zero(currency), Money.zero(currency), true);
-
-                periods.add(period);
-
-                if (graceOnPrincipal > 0) {
-                    graceOnPrincipal--;
-                }
-
-                if (graceOnInterest > 0) {
-                    graceOnInterest--;
-                }
-
-                rescheduleNumberOfRepayments++;
-                numberOfRepayments++;
-            }
-
-            while (extraTerms > 0) {
-
-                LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
-                        new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
-                        Money.zero(currency), Money.zero(currency), true);
-
-                periods.add(period);
-
-                extraTerms--;
-                rescheduleNumberOfRepayments++;
-                numberOfRepayments++;
-            }
-
-            // get the loan application terms from the Loan object
-            final LoanApplicationTerms loanApplicationTerms = loan
-                    .getLoanApplicationTerms(applicationCurrency, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
-                            floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays, holidayDetailDTO);
-
-            // for applying variations
-            Collection<LoanTermVariationsData> loanTermVariations = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges();
-
-            // update the number of repayments
-            loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
-
-            LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);
-            LoanTermVariationsData lastDueDateVariation = loanApplicationTerms.getLoanTermVariations().fetchLoanTermDueDateVariationsData(
-                    loanEndDate);
-            if (lastDueDateVariation != null) {
-                loanEndDate = lastDueDateVariation.getDateValue();
-            }
-            loanApplicationTerms.updateLoanEndDate(loanEndDate);
-
-            if (newInterestRate != null) {
-                loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate);
-                loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate);
-            }
-
-            graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
-            graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
-
-            loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest);
-            loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal);
-
-            loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
-            loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments);
-            loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments);
-            loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest);
-
-            Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
-                    this.paymentPeriodsInOneYearCalculator, mathContext);
-
-            if (!recalculateInterest && newInterestRate == null) {
-                totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged());
-                totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm.minus(actualTotalCumulativeInterest);
-
-                loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
-            }
-
-            for (LoanRescheduleModelRepaymentPeriod period : periods) {
-
-                if (period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate())
-                        || period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) || period.isNew()) {
-
-                    installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms,
-                            false, holidayDetailDTO);
-
-                    if (adjustedDueDate != null && periodNumber == 1) {
-                        installmentDueDate = adjustedDueDate;
-                    }
-
-                    adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
-                            holidayDetailDTO);
-
-                    final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
-
-                    period.updatePeriodNumber(installmentNumber);
-                    period.updatePeriodFromDate(installmentFromDate);
-                    period.updatePeriodDueDate(adjustedInstallmentDueDate);
-
-                    double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
-                            .calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
-                                    adjustedInstallmentDueDate, periodStartDateApplicableForInterest,
-                                    loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
-
-                    // ========================= Calculate the interest due
-                    // ========================================
-
-                    // change the principal to => Principal Disbursed - Total
-                    // Principal Paid
-                    // interest calculation is always based on the total
-                    // principal outstanding
-                    loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
-
-                    // for applying variations
-                    Collection<LoanTermVariationsData> applicableVariations = getApplicableTermVariationsForPeriod(installmentFromDate,
-                            adjustedInstallmentDueDate, loanTermVariations);
-
-                    // determine the interest & principal for the period
-                    PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
-                            this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
-                            totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
-                            totalOutstandingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms, periodNumber, mathContext,
-                            null, null, installmentFromDate, adjustedInstallmentDueDate, applicableVariations);
-
-                    // update the interest due for the period
-                    period.updateInterestDue(principalInterestForThisPeriod.interest());
-
-                    // =============================================================================================
-
-                    // ========================== Calculate the principal due
-                    // ======================================
-
-                    // change the principal to => Principal Disbursed - Total
-                    // cumulative Principal Amount before the reschedule
-                    // installment
-                    loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod));
-
-                    principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(this.paymentPeriodsInOneYearCalculator,
-                            interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest,
-                            totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalance,
-                            loanApplicationTerms, periodNumber, mathContext, null, null, installmentFromDate, adjustedInstallmentDueDate,
-                            applicableVariations);
-
-                    period.updatePrincipalDue(principalInterestForThisPeriod.principal());
-
-                    // ==============================================================================================
-
-                    outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
-                    period.updateOutstandingLoanBalance(outstandingLoanBalance);
-
-                    Money principalDue = Money.of(currency, period.principalDue());
-                    Money interestDue = Money.of(currency, period.interestDue());
-
-                    if (principalDue.isZero() && interestDue.isZero()) {
-                        period.updateFeeChargesDue(Money.zero(currency));
-                        period.updatePenaltyChargesDue(Money.zero(currency));
-                    }
-
-                    Money feeChargesDue = Money.of(currency, period.feeChargesDue());
-                    Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
-
-                    Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
-
-                    period.updateTotalDue(totalDue);
-
-                    // update cumulative fields for principal & interest
-                    totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue());
-                    totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue());
-                    actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
-                    totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
-
-                    installmentFromDate = adjustedInstallmentDueDate;
-                    installmentNumber++;
-                    periodNumber++;
-                    loanTermInDays += daysInInstallment;
-
-                    outstandingBalance = outstandingBalance.minus(period.principalDue());
-                }
-            }
-        }
-
-        final Money totalRepaymentExpected = principal // get the loan Principal
-                                                       // amount
-                .plus(actualTotalCumulativeInterest) // add the actual total
-                                                     // cumulative interest
-                .plus(loanSummary.getTotalFeeChargesCharged()) // add the total
-                                                               // fees charged
-                .plus(loanSummary.getTotalPenaltyChargesCharged()); // finally
-                                                                    // add the
-                                                                    // total
-                                                                    // penalty
-                                                                    // charged
-
-        return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays,
-                loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(),
-                actualTotalCumulativeInterest.getAmount(), loanSummary.getTotalFeeChargesCharged(),
-                loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(), loanSummary.getTotalOutstanding());
-    }
-
     public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
             double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
             Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, Money outstandingBalance,
@@ -2230,28 +2023,28 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
      * fields to generate the schedule
      */
     @Override
-    public LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
-            final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
-            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom) {
+    public LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, Loan loan,
+            final HolidayDetailDTO holidayDetailDTO,
+            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate rescheduleFrom) {
 
         // Fixed schedule End Date for generating schedule
         final LocalDate scheduleTillDate = null;
-        return rescheduleNextInstallments(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, transactions,
-                loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments, rescheduleFrom, scheduleTillDate);
+        return rescheduleNextInstallments(mc, loanApplicationTerms, loan, holidayDetailDTO, loanRepaymentScheduleTransactionProcessor,
+                rescheduleFrom, scheduleTillDate);
 
     }
 
     private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
-            final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
-            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom,
+            Loan loan, final HolidayDetailDTO holidayDetailDTO, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
+            final LocalDate rescheduleFrom,
             final LocalDate scheduleTillDate) {
         // Loan transactions to process and find the variation on payments
         Collection<RecalculationDetail> recalculationDetails = new ArrayList<>();
-        for (LoanTransaction loanTransaction : transactions) {
-            recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction
-                    .copyTransactionProperties(loanTransaction)));
+        for (LoanTransaction loanTransaction : loan.getLoanTransactions()) {
+            if (loanTransaction.isPaymentTransaction()) {
+                recalculationDetails.add(new RecalculationDetail(loanTransaction.getTransactionDate(), LoanTransaction
+                        .copyTransactionProperties(loanTransaction)));
+            }
         }
         final boolean applyInterestRecalculation = loanApplicationTerms.isInterestRecalculationEnabled();
 
@@ -2271,10 +2064,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
 
             // this is required to update total fee amounts in the
             // LoanScheduleModel
-            final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
+            final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loan.charges());
             periods = createNewLoanScheduleListWithDisbursementDetails(loanApplicationTerms.fetchNumberOfRepaymentsAfterExceptions(),
                     loanApplicationTerms, chargesDueAtTimeOfDisbursement);
-            final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
             MonetaryCurrency currency = outstandingBalance.getCurrency();
 
             // early payments will be added here and as per the selected
@@ -2302,7 +2094,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             final Map<LocalDate, Map<LocalDate, Money>> compoundingDateVariations = new HashMap<>();
             LocalDate currentDate = DateUtils.getLocalDateOfTenant();
             LocalDate lastRestDate = currentDate;
-            if (loanApplicationTerms.getRestCalendarInstance() != null) {
+            if (loanApplicationTerms.isInterestRecalculationEnabled()) {
                 lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
             }
             LocalDate actualRepaymentDate = loanApplicationTerms.getExpectedDisbursementDate();
@@ -2323,6 +2115,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             LocalDate periodStartDate = loanApplicationTerms.getExpectedDisbursementDate();
             // Set fixed Amortization Amounts(either EMI or Principal )
             updateAmortization(mc, loanApplicationTerms, periodNumber, outstandingBalance);
+            
+            // count periods without interest grace to exclude for flat loan calculations
 
             final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
             if (loanApplicationTerms.isMultiDisburseLoan()) {
@@ -2340,12 +2134,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             List<LoanTermVariationsData> exceptionDataList = loanApplicationTerms.getLoanTermVariations().getExceptionData();
             final ListIterator<LoanTermVariationsData> exceptionDataListIterator = exceptionDataList.listIterator();
             LoanTermVariationParams loanTermVariationParams = null;
+            
+            // identify retain installments
+            final List<LoanRepaymentScheduleInstallment> processInstallmentsInstallments = fetchRetainedInstallments(
+                    loan.getRepaymentScheduleInstallments(), rescheduleFrom, currency);
+            final List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
 
             // Block process the installment and creates the period if it falls
             // before reschedule from date
             // This will create the recalculation details by applying the
             // transactions
-            for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
+            for (LoanRepaymentScheduleInstallment installment : processInstallmentsInstallments) {
                 // this will generate the next schedule due date and allows to
                 // process the installment only if recalculate from date is
                 // greater than due date
@@ -2353,32 +2152,44 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                     if (totalCumulativePrincipal.isGreaterThanOrEqualTo(loanApplicationTerms.getTotalDisbursedAmount())) {
                         break;
                     }
-                    LocalDate previousRepaymentDate = actualRepaymentDate;
                     ArrayList<LoanTermVariationsData> dueDateVariationsDataList = new ArrayList<>();
+                    
 
                  // check for date changes
-                    while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
-                        LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
-                        if (!variation.isSpecificToInstallment()) {
-                            actualRepaymentDate = variation.getDateValue();
-                        }
-                        dueDateVariationsDataList.add(variation);
-                    }
-
+                   
                     do {
                         actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
                                 loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
                         isFirstRepayment = false;
                         lastInstallmentDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
                                 holidayDetailDTO);
+                        while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
+                            LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
+                            if (!variation.isSpecificToInstallment()) {
+                                actualRepaymentDate = variation.getDateValue();
+                                /*if (!isDueDateChangeApplied) {
+                                    previousRepaymentDate = actualRepaymentDate;
+                                    isDueDateChangeApplied = true;
+                                }*/
+                                lastInstallmentDate = actualRepaymentDate;
+                            }
+                            dueDateVariationsDataList.add(variation);
+                        }
                         loanTermVariationParams = applyExceptionLoanTermVariations(loanApplicationTerms, lastInstallmentDate,
-                                exceptionDataListIterator);
+                                exceptionDataListIterator, instalmentNumber, totalCumulativePrincipal, totalCumulativeInterest, mc);
                     } while (loanTermVariationParams != null && loanTermVariationParams.isSkipPeriod());
 
-                    if (!lastInstallmentDate.isBefore(rescheduleFrom)) {
+                    /*if (!lastInstallmentDate.isBefore(rescheduleFrom)) {
                         actualRepaymentDate = previousRepaymentDate;
+                        if(isDueDateChangeApplied){
+                            int numberOfDateChangesApplied = dueDateVariationsDataList.size();
+                            while(numberOfDateChangesApplied >0 ){
+                                loanApplicationTerms.getLoanTermVariations().previousDueDateVariation();
+                                numberOfDateChangesApplied--;
+                            }
+                        }
                         break;
-                    }
+                    }*/
                     periodNumber++;
 
                     for (LoanTermVariationsData dueDateVariation : dueDateVariationsDataList) {
@@ -2422,66 +2233,81 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                 totalPenaltyChargesCharged = totalPenaltyChargesCharged.plus(installment.getPenaltyChargesCharged(currency));
                 instalmentNumber++;
                 loanTermInDays = Days.daysBetween(installment.getFromDate(), installment.getDueDate()).getDays();
+                
+                
+                if (loanApplicationTerms.isInterestRecalculationEnabled()) {
+
+                    // populates the collection with transactions till the due
+                    // date
+                    // of
+                    // the period for interest recalculation enabled loans
+                    Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod(applyInterestRecalculation,
+                            installment.getDueDate(), recalculationDetails);
+
+                    // calculates the expected principal value for this
+                    // repayment
+                    // schedule
+                    Money principalPortionCalculated = principalToBeScheduled.zero();
+                    if (!installment.isRecalculatedInterestComponent()) {
+                        principalPortionCalculated = calculateExpectedPrincipalPortion(installment.getInterestCharged(currency),
+                                loanApplicationTerms);
+                    }
 
-                // populates the collection with transactions till the due date
-                // of
-                // the period for interest recalculation enabled loans
-                Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod(applyInterestRecalculation,
-                        installment.getDueDate(), recalculationDetails);
-
-                // calculates the expected principal value for this repayment
-                // schedule
-                Money principalPortionCalculated = principalToBeScheduled.zero();
-                if (!installment.isRecalculatedInterestComponent()) {
-                    principalPortionCalculated = calculateExpectedPrincipalPortion(installment.getInterestCharged(currency),
-                            loanApplicationTerms);
-                }
+                    // expected principal considering the previously paid excess
+                    // amount
+                    Money actualPrincipalPortion = principalPortionCalculated.minus(reducePrincipal);
+                    if (actualPrincipalPortion.isLessThanZero()) {
+                        actualPrincipalPortion = principalPortionCalculated.zero();
+                    }
 
-                // expected principal considering the previously paid excess
-                // amount
-                Money actualPrincipalPortion = principalPortionCalculated.minus(reducePrincipal);
-                if (actualPrincipalPortion.isLessThanZero()) {
-                    actualPrincipalPortion = principalPortionCalculated.zero();
-                }
+                    Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
+                            loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap,
+                            installment, applicableTransactions, actualPrincipalPortion);
+
+                    // this block is to adjust the period number based on the
+                    // actual
+                    // schedule due date and installment due date
+                    // recalculatedInterestComponent installment shouldn't be
+                    // considered while calculating fixed EMI amounts
+                    int period = periodNumber;
+                    if (!lastInstallmentDate.isEqual(installment.getDueDate())) {
+                        period--;
+                    }
+                    reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency), principalPortionCalculated, reducePrincipal,
+                            loanApplicationTerms, totalCumulativePrincipal, period, mc);
+                    // Updates principal paid map with efective date for
+                    // reducing
+                    // the amount from outstanding balance(interest calculation)
+                    LocalDate amountApplicableDate = null;
+                    if (loanApplicationTerms.getRestCalendarInstance() != null) {
+                        amountApplicableDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
+                                holidayDetailDTO);
+                    }
 
-                Money unprocessed = updateEarlyPaidAmountsToMap(loanApplicationTerms, holidayDetailDTO,
-                        loanRepaymentScheduleTransactionProcessor, newRepaymentScheduleInstallments, currency, principalPortionMap,
-                        installment, applicableTransactions, actualPrincipalPortion);
-
-                // this block is to adjust the period number based on the actual
-                // schedule due date and installment due date
-                // recalculatedInterestComponent installment shouldn't be
-                // considered while calculating fixed EMI amounts
-                int period = periodNumber;
-                if (!lastInstallmentDate.isEqual(installment.getDueDate())) {
-                    period--;
-                }
-                reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency), principalPortionCalculated, reducePrincipal,
-                        loanApplicationTerms, totalCumulativePrincipal, period, mc);
-                // Updates principal paid map with efective date for reducing
-                // the amount from outstanding balance(interest calculation)
-                LocalDate amountApplicableDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
-                        holidayDetailDTO);
-                // updates map with the installment principal amount excluding
-                // unprocessed amount since this amount is already accounted.
-                updateMapWithAmount(principalPortionMap, installment.getPrincipal(currency).minus(unprocessed), amountApplicableDate);
-                uncompoundedAmount = updateCompoundingDetailsForPartialScheduleGeneration(installment, loanApplicationTerms,
-                        principalPortionMap, compoundingDateVariations, uncompoundedAmount, applicableTransactions, lastRestDate,
-                        holidayDetailDTO);
-                // update outstanding balance for interest calculation
-                outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, installment.getDueDate(),
-                        outstandingBalanceAsPerRest, false);
-                outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, installment.getDueDate(),
-                        outstandingBalanceAsPerRest, true);
+                    // updates map with the installment principal amount
+                    // excluding
+                    // unprocessed amount since this amount is already
+                    // accounted.
+                    updateMapWithAmount(principalPortionMap, installment.getPrincipal(currency).minus(unprocessed), amountApplicableDate);
+                    uncompoundedAmount = updateCompoundingDetailsForPartialScheduleGeneration(installment, loanApplicationTerms,
+                            principalPortionMap, compoundingDateVariations, uncompoundedAmount, applicableTransactions, lastRestDate,
+                            holidayDetailDTO);
 
+                    // update outstanding balance for interest calculation
+                    outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, installment.getDueDate(),
+                            outstandingBalanceAsPerRest, false);
+                    outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, installment.getDueDate(),
+                            outstandingBalanceAsPerRest, true);
+                    // updates the map with over due amounts
+                    updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, lastInstallmentDate,
+                            newRepaymentScheduleInstallments, true, lastRestDate);
+                } else {
+                    outstandingBalanceAsPerRest = outstandingBalance;
+                }
             }
             totalRepaymentExpected = totalCumulativePrincipal.plus(totalCumulativeInterest).plus(totalFeeChargesCharged)
                     .plus(totalPenaltyChargesCharged);
 
-            // updates the map with over due amounts
-            updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, lastInstallmentDate,
-                    newRepaymentScheduleInstallments, true, lastRestDate);
-
             // for partial schedule generation
             if (!newRepaymentScheduleInstallments.isEmpty() && totalCumulativeInterest.isGreaterThanZero()) {
                 Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
@@ -2494,6 +2320,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                         scheduleTillDate, currency, applyInterestRecalculation);
                 retainedInstallments.addAll(newRepaymentScheduleInstallments);
                 loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
+                loanApplicationTerms.updateTotalInterestDue(Money.of(currency, loan.getLoanSummary().getTotalInterestCharged()));
+            }else{
+                loanApplicationTerms.getLoanTermVariations().resetVariations();
             }
 
         }
@@ -2501,9 +2330,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
         if (loanScheduleParams == null) {
             loanScheduleParams = LoanScheduleParams.createLoanScheduleParamsForCompleteUpdate(recalculationDetails,
                     loanRepaymentScheduleTransactionProcessor, scheduleTillDate, applyInterestRecalculation);
+            periods.clear();
         }
-
-        LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, loanScheduleParams);
+        LoanScheduleModel loanScheduleModel = generate(mc, loanApplicationTerms, loan.charges(), holidayDetailDTO, loanScheduleParams);
         for (LoanScheduleModelPeriod loanScheduleModelPeriod : loanScheduleModel.getPeriods()) {
             if (loanScheduleModelPeriod.isRepaymentPeriod()) {
                 // adding newly created repayment periods to installments
@@ -2515,6 +2344,41 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
         return LoanScheduleDTO.from(retainedInstallments, loanScheduleModelwithPeriodChanges);
     }
 
+    public List<LoanRepaymentScheduleInstallment> fetchRetainedInstallments(
+            final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final LocalDate rescheduleFrom,
+            MonetaryCurrency currency) {
+        List<LoanRepaymentScheduleInstallment> newRepaymentScheduleInstallments = new ArrayList<>();
+        int lastInterestAvilablePeriod = 0;
+        int processedPeriod = 0;
+        for (LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
+            if (installment.getDueDate().isBefore(rescheduleFrom)) {
+                newRepaymentScheduleInstallments.add(installment);
+                if(installment.getInterestCharged(currency).isGreaterThanZero()){
+                    lastInterestAvilablePeriod = installment.getInstallmentNumber();
+                }
+                processedPeriod = installment.getInstallmentNumber();
+            } else {
+                break;
+            }
+        }
+        
+        // this block is to remove the periods till last interest available
+        // period.
+        // if the last retained period is interest grace period then we
+        // can't get the interest of last period without calculating again
+        // to fix this adjusting retained periods
+        if(lastInterestAvilablePeriod != processedPeriod){
+            final List<LoanRepaymentScheduleInstallment> retainRepaymentScheduleInstallments = new ArrayList<>();
+            for (LoanRepaymentScheduleInstallment installment : newRepaymentScheduleInstallments) {
+                if(installment.getInstallmentNumber() <= lastInterestAvilablePeriod){
+                    retainRepaymentScheduleInstallments.add(installment);
+                }
+            }
+            newRepaymentScheduleInstallments.retainAll(retainRepaymentScheduleInstallments);
+        }
+        return newRepaymentScheduleInstallments;
+    }
+
     /**
      * Method identifies the early paid amounts for a installment and update the
      * principal map for further calculations
@@ -2630,7 +2494,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
             earlyPaidAmount = earlyPaidAmount.zero();
         }
 
-        if (isEarlyPaid) {
+        if (isEarlyPaid && applicationTerms.getRescheduleStrategyMethod() != null) {
             switch (applicationTerms.getRescheduleStrategyMethod()) {
                 case REDUCE_EMI_AMOUNT:
                     adjustInstallmentOrPrincipalAmount(applicationTerms, totalCumulativePrincipal, periodNumber, mc);
@@ -2727,21 +2591,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
      */
     @Override
     public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final MonetaryCurrency currency, final LocalDate onDate,
-            final LoanApplicationTerms loanApplicationTerms, final MathContext mc, final Set<LoanCharge> charges,
-            final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> loanTransactions,
-            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
-            final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments) {
+            final LoanApplicationTerms loanApplicationTerms, final MathContext mc, Loan loan, final HolidayDetailDTO holidayDetailDTO,
+            final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
 
         LocalDate calculateTill = onDate;
         if (loanApplicationTerms.getPreClosureInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
             calculateTill = getNextRestScheduleDate(onDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
         }
 
-        LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, charges, holidayDetailDTO, loanTransactions,
-                loanRepaymentScheduleTransactionProcessor, repaymentScheduleInstallments, onDate, calculateTill);
+        LoanScheduleDTO loanScheduleDTO = rescheduleNextInstallments(mc, loanApplicationTerms, loan, holidayDetailDTO, loanRepaymentScheduleTransactionProcessor,
+                onDate, calculateTill);
+        List<LoanTransaction> loanTransactions = loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals();
 
         loanRepaymentScheduleTransactionProcessor.handleTransaction(loanApplicationTerms.getExpectedDisbursementDate(), loanTransactions,
-                currency, loanScheduleDTO.getInstallments(), charges);
+                currency, loanScheduleDTO.getInstallments(), loan.charges());
         Money feeCharges = Money.zero(currency);
         Money penaltyCharges = Money.zero(currency);
         Money totalPrincipal = Money.zero(currency);
@@ -2759,15 +2622,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
                 feeCharges.getAmount(), penaltyCharges.getAmount(), false, compoundingDetails);
     }
 
-    /**
-     * set the value to zero if the provided value is null
-     * 
-     * @return integer value equal/greater than 0
-     **/
-    private Integer defaultToZeroIfNull(Integer value) {
-
-        return (value != null) ? value : 0;
-    }
 
     private final class LoanTermVariationParams {
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
index 1bbefa4..1bbf261 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
@@ -73,6 +73,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
         Money balanceForInterestCalculation = outstandingBalance;
         Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace;
         Map<LocalDate, BigDecimal> interestRates = new HashMap<>(termVariations.size());
+        
         for (LoanTermVariationsData loanTermVariation : termVariations) {
             if (loanTermVariation.getTermVariationType().isInterestRateVariation()
                     && loanTermVariation.isApplicable(periodStartDate, periodEndDate)) {
@@ -86,6 +87,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
                 }
             }
         }
+
         if (principalVariation != null) {
 
             for (Map.Entry<LocalDate, Money> principal : principalVariation.entrySet()) {
@@ -99,6 +101,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
                         interestForThisInstallment = interestForThisInstallment.plus(result.interest());
                         cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
                         interestStartDate = principal.getKey();
+
                     }
                     Money compoundFee = totalCumulativePrincipal.zero();
                     if (compoundingMap.containsKey(principal.getKey())) {
@@ -118,13 +121,14 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
                         loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey()));
                     }
                 }
-
             }
         }
+        
 
         final PrincipalInterest result = loanApplicationTerms.calculateTotalInterestForPeriod(calculator,
                 interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, cumulatingInterestDueToGrace,
                 balanceForInterestCalculation, interestStartDate, periodEndDate);
+        
         interestForThisInstallment = interestForThisInstallment.plus(result.interest());
         cumulatingInterestDueToGrace = result.interestPaymentDueToGrace();
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/fe617123/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java
index 31cdbe5..5f3d8d7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/FlatInterestLoanScheduleGenerator.java
@@ -32,11 +32,12 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
     @Override
     public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
             final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
-            final Money totalCumulativeInterest, final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
+            Money totalCumulativeInterest, Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
             final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
             @SuppressWarnings("unused") TreeMap<LocalDate, Money> principalVariation,
             @SuppressWarnings("unused") Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate,
             @SuppressWarnings("unused") Collection<LoanTermVariationsData> termVariations) {
+        
         Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
                 periodNumber, mc, null);
 
@@ -54,6 +55,11 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
         principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
                 totalCumulativePrincipalToDate, periodNumber);
 
+        // totalCumulativeInterest from partial schedule generation for multi rescheduling
+        /*if (loanApplicationTerms.getPartialTotalCumulativeInterest() != null && loanApplicationTerms.getTotalInterestDue() != null) {
+            totalInterestDueForLoan = loanApplicationTerms.getTotalInterestDue();
+            totalInterestDueForLoan = totalInterestDueForLoan.plus(loanApplicationTerms.getPartialTotalCumulativeInterest());
+        }*/
         interestForThisInstallment = loanApplicationTerms.adjustInterestIfLastRepaymentPeriod(interestForThisInstallment,
                 totalCumulativeInterestToDate, totalInterestDueForLoan, periodNumber);
 


[5/5] incubator-fineract git commit: Adding Apache License Text in V313__multi_rescheduling_script.sql

Posted by na...@apache.org.
Adding Apache License Text in V313__multi_rescheduling_script.sql


Project: http://git-wip-us.apache.org/repos/asf/incubator-fineract/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-fineract/commit/c44cf55f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-fineract/tree/c44cf55f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-fineract/diff/c44cf55f

Branch: refs/heads/develop
Commit: c44cf55fcee7422671422dd94f2462637d254170
Parents: fe61712
Author: Nazeer Hussain Shaik <na...@confluxtechnologies.com>
Authored: Mon Aug 1 19:08:21 2016 +0530
Committer: Nazeer Hussain Shaik <na...@confluxtechnologies.com>
Committed: Mon Aug 1 19:08:21 2016 +0530

----------------------------------------------------------------------
 .../core_db/V313__multi_rescheduling_script.sql  | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/c44cf55f/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
index 1fca1d1..367ee0c 100644
--- a/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
+++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V313__multi_rescheduling_script.sql
@@ -1,3 +1,22 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
 ALTER TABLE `m_loan_term_variations`
 	ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT '1' AFTER `applied_on_loan_status`;