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

Reply via email to