http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/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 2f8b728..d06b3f1 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
@@ -45,6 +45,7 @@ import 
org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 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;
@@ -124,7 +125,8 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         LocalDate firstRepaymentdate = 
this.scheduledDateGenerator.generateNextRepaymentDate(
                 loanApplicationTerms.getExpectedDisbursementDate(), 
loanApplicationTerms, isFirstRepayment, holidayDetailDTO);
         final LocalDate idealDisbursementDate = 
this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate(
-                loanApplicationTerms.getLoanTermPeriodFrequencyType(), 
loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate);
+                loanApplicationTerms.getLoanTermPeriodFrequencyType(), 
loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate,
+                loanApplicationTerms.getLoanCalendar(), 
loanApplicationTerms.getHolidayDetailDTO(), loanApplicationTerms);
 
         if (!scheduleParams.isPartialUpdate()) {
             // Set Fixed Principal Amount
@@ -203,13 +205,6 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             if (scheduleParams.getPeriodStartDate().isAfter(scheduledDueDate)) 
{ throw new ScheduleDateException(
                     "Due date can't be before period start date", 
scheduledDueDate); }
 
-            if (!scheduleParams.getLatePaymentMap().isEmpty()) {
-                
populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), 
scheduledDueDate, currentDate, loanApplicationTerms,
-                        holidayDetailDTO, scheduleParams.getCompoundingMap(), 
loanCharges, currency);
-                
scheduleParams.getCompoundingDateVariations().put(scheduleParams.getPeriodStartDate(),
-                        new TreeMap<>(scheduleParams.getCompoundingMap()));
-            }
-
             if (extendTermForDailyRepayments) {
                 scheduleParams.setActualRepaymentDate(scheduledDueDate);
             }
@@ -220,6 +215,10 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 scheduledDueDate = scheduleParams.getScheduleTillDate();
                 isNextRepaymentAvailable = false;
             }
+            if (loanApplicationTerms.isInterestRecalculationEnabled()) {
+                
populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), 
scheduledDueDate, loanApplicationTerms,
+                        holidayDetailDTO, scheduleParams, loanCharges, 
currency);
+            }
 
             // populates the collection with transactions till the due date of
             // the period for interest recalculation enabled loans
@@ -316,6 +315,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     currentPeriodParams.getFeeChargesForInstallment(), 
currentPeriodParams.getPenaltyChargesForInstallment(),
                     totalInstallmentDue, false);
 
+            
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), 
installment);
             // apply loan transactions on installments to identify early/late
             // payments for interest recalculation
             installment = handleRecalculationForTransactions(mc, 
loanApplicationTerms, holidayDetailDTO, currency, scheduleParams,
@@ -327,7 +327,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             // Updates principal paid map with efective date for reducing
             // the amount from outstanding balance(interest calculation)
             updateAmountsWithEffectiveDate(loanApplicationTerms, 
holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams,
-                    installment);
+                    installment, lastRestDate);
 
             // handle cumulative fields
 
@@ -337,7 +337,6 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             scheduleParams.setPeriodStartDate(scheduledDueDate);
             scheduleParams.incrementInstalmentNumber();
             scheduleParams.incrementPeriodNumber();
-            scheduleParams.getCompoundingDateVariations().clear();
             if (termVariationParams.isRecalculateAmounts()) {
                 loanApplicationTerms.setCurrentPeriodFixedEmiAmount(null);
                 
loanApplicationTerms.setCurrentPeriodFixedPrincipalAmount(null);
@@ -377,6 +376,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         final BigDecimal totalPrincipalPaid = BigDecimal.ZERO;
         final BigDecimal totalOutstanding = BigDecimal.ZERO;
 
+        updateCompoundingDetails(periods, scheduleParams, 
loanApplicationTerms);
         return LoanScheduleModel.from(periods, applicationCurrency, 
scheduleParams.getLoanTermInDays(),
                 scheduleParams.getPrincipalToBeScheduled(), 
scheduleParams.getTotalCumulativePrincipal().getAmount(), totalPrincipalPaid,
                 scheduleParams.getTotalCumulativeInterest().getAmount(), 
scheduleParams.getTotalFeeChargesCharged().getAmount(),
@@ -384,6 +384,30 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 totalOutstanding);
     }
 
+    private void updateCompoundingDetails(final 
Collection<LoanScheduleModelPeriod> periods, final LoanScheduleParams params,
+            final LoanApplicationTerms loanApplicationTerms) {
+        final Map<LocalDate, Map<LocalDate, Money>> compoundingDetails = 
params.getCompoundingDateVariations();
+        if (compoundingDetails.isEmpty()) { return; }
+        for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
+            if (loanScheduleModelPeriod.isRepaymentPeriod() && 
loanScheduleModelPeriod.getLoanCompoundingDetails().isEmpty()) {
+                Map<LocalDate, Money> periodCompoundingDetails = 
compoundingDetails.get(loanScheduleModelPeriod.periodFromDate());
+                if (periodCompoundingDetails != null) {
+                    for (Map.Entry<LocalDate, Money> entry : 
periodCompoundingDetails.entrySet()) {
+                        if (entry.getValue().isGreaterThanZero() && 
!entry.getKey().isAfter(loanScheduleModelPeriod.periodDueDate())) {
+                            LocalDate effectiveDate = entry.getKey();
+                            if (loanApplicationTerms.allowCompoundingOnEod()) {
+                                effectiveDate = effectiveDate.minusDays(1);
+                            }
+                            LoanInterestRecalcualtionAdditionalDetails 
additionalDetails = new LoanInterestRecalcualtionAdditionalDetails(
+                                    effectiveDate, 
entry.getValue().getAmount());
+                            
loanScheduleModelPeriod.getLoanCompoundingDetails().add(additionalDetails);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     private void applyChargesForCurrentPeriod(final Set<LoanCharge> 
loanCharges, final MonetaryCurrency currency,
             LoanScheduleParams scheduleParams, LocalDate scheduledDueDate, 
ScheduleCurrentPeriodParams currentPeriodParams) {
         PrincipalInterest principalInterest = new 
PrincipalInterest(currentPeriodParams.getPrincipalForThisPeriod(),
@@ -422,13 +446,14 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
 
     private void updateAmountsWithEffectiveDate(final LoanApplicationTerms 
loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
             LoanScheduleParams scheduleParams, LocalDate scheduledDueDate, 
ScheduleCurrentPeriodParams currentPeriodParams,
-            LoanScheduleModelPeriod installment) {
+            LoanScheduleModelPeriod installment, LocalDate lastRestDate) {
         LocalDate amountApplicableDate = installment.periodDueDate();
         if (loanApplicationTerms.isInterestRecalculationEnabled()) {
             amountApplicableDate = 
getNextRestScheduleDate(installment.periodDueDate().minusDays(1), 
loanApplicationTerms, holidayDetailDTO);
         }
         updateMapWithAmount(scheduleParams.getPrincipalPortionMap(),
                 
currentPeriodParams.getPrincipalForThisPeriod().minus(currentPeriodParams.getReducedBalance()),
 amountApplicableDate);
+        updateCompoundingMap(loanApplicationTerms, holidayDetailDTO, 
scheduleParams, lastRestDate, scheduledDueDate);
 
         // update outstanding balance for interest calculation
         updateOutstandingBalanceAsPerRest(scheduleParams, scheduledDueDate);
@@ -445,7 +470,6 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         LoanScheduleModelPeriod modifiedInstallment = installment;
         if (scheduleParams.applyInterestRecalculation() && 
loanRepaymentScheduleTransactionProcessor != null) {
             Money principalProcessed = Money.zero(currency);
-            
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), 
modifiedInstallment);
             for (RecalculationDetail detail : applicableTransactions) {
                 if (!detail.isProcessed()) {
                     LocalDate transactionDate = detail.getTransactionDate();
@@ -489,10 +513,12 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                         }
 
                     }
+                    adjustCompoundedAmountWithPaidDetail(scheduleParams, 
lastRestDate, currentTransactions, loanApplicationTerms,
+                            holidayDetailDTO);
                 }
             }
             updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, 
currency, scheduleParams.getLatePaymentMap(), scheduledDueDate,
-                    scheduleParams.getInstallments(), true, lastRestDate, 
scheduleParams.getCompoundingMap());
+                    scheduleParams.getInstallments(), true, lastRestDate);
             
currentPeriodParams.minusPrincipalForThisPeriod(principalProcessed);
         }
         return modifiedInstallment;
@@ -600,9 +626,6 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             scheduleParams.getCompoundingMap().clear();
             scheduleParams.getCompoundingMap().putAll(
                     
scheduleParams.getCompoundingDateVariations().get(periodStartDateApplicableForInterest));
-        } else {
-            
scheduleParams.getCompoundingDateVariations().put(periodStartDateApplicableForInterest,
-                    new TreeMap<>(scheduleParams.getCompoundingMap()));
         }
     }
 
@@ -656,10 +679,6 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                             if (daysInPeriodApplicable > 0) {
                                 // 5 determine interest till the transaction
                                 // date
-                                if 
(!scheduleParams.getCompoundingDateVariations().containsKey(periodStartDateApplicableForInterest))
 {
-                                    
scheduleParams.getCompoundingDateVariations().put(periodStartDateApplicableForInterest,
-                                            new 
TreeMap<>(scheduleParams.getCompoundingMap()));
-                                }
                                 PrincipalInterest 
principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                                         this.paymentPeriodsInOneYearCalculator,
                                         
currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(), 
scheduleParams
@@ -700,6 +719,8 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                                     scheduleParams.getOutstandingBalance(), 
interestForThisinstallment, feeChargesForInstallment,
                                     penaltyChargesForInstallment, 
totalInstallmentDue, true);
                             periods.add(installment);
+                            
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), 
installment);
+                            updateCompoundingMap(loanApplicationTerms, 
holidayDetailDTO, scheduleParams, lastRestDate, scheduledDueDate);
 
                             // update outstanding balance for interest
                             // calculation as per the rest
@@ -716,10 +737,12 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                             periodStartDateApplicableForInterest = 
scheduleParams.getPeriodStartDate();
                             updateLatePaymentMap = true;
                             scheduleParams.incrementInstalmentNumber();
+                            
populateCompoundingDatesInPeriod(scheduleParams.getPeriodStartDate(), 
scheduledDueDate, loanApplicationTerms,
+                                    holidayDetailDTO, scheduleParams, 
loanCharges, currency);
                             // creates and insert Loan repayment schedule
                             // for
                             // the period
-                            
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), 
installment);
+
                         } else if (installment == null) {
                             installment = ((List<LoanScheduleModelPeriod>) 
periods).get(periods.size() - 1);
                         }
@@ -762,12 +785,10 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                         // identify late payments and add compounding
                         // details to
                         // map for interest calculation
-                        handleLatePayments(loanApplicationTerms, 
holidayDetailDTO, currency, scheduleParams, lastRestDate,
-                                periodStartDateApplicableForInterest, detail);
+                        handleLatePayments(loanApplicationTerms, 
holidayDetailDTO, currency, scheduleParams, lastRestDate, detail);
                         if (updateLatePaymentMap) {
                             updateLatePaymentsToMap(loanApplicationTerms, 
holidayDetailDTO, currency, scheduleParams.getLatePaymentMap(),
-                                    scheduledDueDate, 
scheduleParams.getInstallments(), true, lastRestDate,
-                                    scheduleParams.getCompoundingMap());
+                                    scheduledDueDate, 
scheduleParams.getInstallments(), true, lastRestDate);
                         }
                     } else if 
(scheduleParams.getLoanRepaymentScheduleTransactionProcessor() != null) {
                         LocalDate applicableDate = 
getNextRestScheduleDate(transactionDate.minusDays(1), loanApplicationTerms,
@@ -776,9 +797,8 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                             List<LoanTransaction> currentTransactions = 
createCurrentTransactionList(detail);
                             Money unprocessed = 
scheduleParams.getLoanRepaymentScheduleTransactionProcessor().handleRepaymentSchedule(
                                     currentTransactions, currency, 
scheduleParams.getInstallments());
-                            Money arrears = 
fetchCompoundedArrears(loanApplicationTerms, currency, detail.getTransaction());
+                            Money arrears = fetchArrears(loanApplicationTerms, 
currency, detail.getTransaction());
                             if (unprocessed.isGreaterThanZero()) {
-                                arrears = 
getTotalAmount(scheduleParams.getLatePaymentMap(), currency);
                                 
updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed, 
applicableDate);
                                 
currentPeriodParams.plusEarlyPaidAmount(unprocessed);
 
@@ -790,10 +810,6 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                                                 
.calculateTillRestFrequencyEnabled()) {
 
                                     LocalDate calculateTill = transactionDate;
-                                    if 
(!scheduleParams.getCompoundingDateVariations().containsKey(periodStartDateApplicableForInterest))
 {
-                                        
scheduleParams.getCompoundingDateVariations().put(periodStartDateApplicableForInterest,
-                                                new 
TreeMap<>(scheduleParams.getCompoundingMap()));
-                                    }
                                     PrincipalInterest 
principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
                                             
this.paymentPeriodsInOneYearCalculator,
                                             
currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(), 
scheduleParams
@@ -836,8 +852,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
 
                             }
                             if (arrears.isGreaterThanZero() && 
applicableDate.isBefore(lastRestDate)) {
-                                handleLatePayments(loanApplicationTerms, 
holidayDetailDTO, currency, scheduleParams, lastRestDate,
-                                        periodStartDateApplicableForInterest, 
detail);
+                                handleLatePayments(loanApplicationTerms, 
holidayDetailDTO, currency, scheduleParams, lastRestDate, detail);
                             }
                         }
 
@@ -862,12 +877,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
      * @param detail
      */
     private void handleLatePayments(final LoanApplicationTerms 
loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
-            final MonetaryCurrency currency, LoanScheduleParams 
scheduleParams, LocalDate lastRestDate,
-            LocalDate periodStartDateApplicableForInterest, 
RecalculationDetail detail) {
+            final MonetaryCurrency currency, LoanScheduleParams 
scheduleParams, LocalDate lastRestDate, RecalculationDetail detail) {
         updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), 
loanApplicationTerms, currency, holidayDetailDTO, lastRestDate,
                 scheduleParams);
-        
scheduleParams.getCompoundingDateVariations().put(periodStartDateApplicableForInterest,
-                new TreeMap<>(scheduleParams.getCompoundingMap()));
     }
 
     private void updateAmountsBasedOnEarlyPayment(final LoanApplicationTerms 
loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
@@ -1064,9 +1076,16 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         return isAmountChanged;
     }
 
-    private Money fetchCompoundedArrears(final LoanApplicationTerms 
loanApplicationTerms, final MonetaryCurrency currency,
+    private Money fetchArrears(final LoanApplicationTerms 
loanApplicationTerms, final MonetaryCurrency currency,
             final LoanTransaction transaction) {
         Money arrears = transaction.getPrincipalPortion(currency);
+        arrears = arrears.plus(fetchCompoundedArrears(loanApplicationTerms, 
currency, transaction));
+        return arrears;
+    }
+
+    private Money fetchCompoundedArrears(final LoanApplicationTerms 
loanApplicationTerms, final MonetaryCurrency currency,
+            final LoanTransaction transaction) {
+        Money arrears = Money.zero(currency);
         if 
(loanApplicationTerms.getInterestRecalculationCompoundingMethod().isInterestCompoundingEnabled())
 {
             arrears = arrears.plus(transaction.getInterestPortion(currency));
         }
@@ -1096,7 +1115,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             final Collection<RecalculationDetail> transactions, final 
Set<LoanCharge> loanCharges, final LoanScheduleParams params) {
         boolean isFirstRepayment = false;
         LocalDate startDate = params.getPeriodStartDate();
-        Money outstanding = Money.zero(currency);
+        Money outstanding = params.getOutstandingBalanceAsPerRest();
         Money totalInterest = Money.zero(currency);
         Money totalCumulativeInterest = Money.zero(currency);
         double interestCalculationGraceOnRepaymentPeriodFraction = 
Double.valueOf(0);
@@ -1111,15 +1130,12 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             if (params.getActualRepaymentDate().isAfter(currentDate)) {
                 params.setActualRepaymentDate(currentDate);
             }
-            outstanding = 
updateOutstandingFromLatePayment(params.getPeriodStartDate(), 
params.getLatePaymentMap(), outstanding);
 
             Collection<RecalculationDetail> applicableTransactions = 
getApplicableTransactionsForPeriod(
                     params.applyInterestRecalculation(), 
params.getActualRepaymentDate(), transactions);
 
-            if (!params.getLatePaymentMap().isEmpty()) {
-                populateCompoundingDatesInPeriod(params.getPeriodStartDate(), 
params.getActualRepaymentDate(), currentDate,
-                        loanApplicationTerms, holidayDetailDTO, 
params.getCompoundingMap(), loanCharges, currency);
-            }
+            populateCompoundingDatesInPeriod(params.getPeriodStartDate(), 
params.getActualRepaymentDate(), loanApplicationTerms,
+                    holidayDetailDTO, params, loanCharges, currency);
 
             for (RecalculationDetail detail : applicableTransactions) {
                 if (detail.isProcessed()) {
@@ -1146,22 +1162,30 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     totalCumulativeInterest = 
totalCumulativeInterest.plus(totalInterest);
                     totalInterest = totalInterest.zero();
                     
addLoanRepaymentScheduleInstallment(params.getInstallments(), installment);
+                    updateCompoundingMap(loanApplicationTerms, 
holidayDetailDTO, params, lastRestDate, transactionDate);
+                    
populateCompoundingDatesInPeriod(installment.periodDueDate(), 
params.getActualRepaymentDate(), loanApplicationTerms,
+                            holidayDetailDTO, params, loanCharges, currency);
+                    
params.setCompoundedInLastInstallment(params.getUnCompoundedAmount());
                     params.setPeriodStartDate(transactionDate);
                     startDate = transactionDate;
                 }
                 
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
 currency, params.getInstallments());
+                updateLatePaidAmountsToPrincipalMap(detail.getTransaction(), 
loanApplicationTerms, currency, holidayDetailDTO,
+                        lastRestDate, params);
                 updateLatePaymentsToMap(loanApplicationTerms, 
holidayDetailDTO, currency, params.getLatePaymentMap(), currentDate,
-                        params.getInstallments(), false, lastRestDate, 
params.getCompoundingMap());
-                outstanding = outstanding.zero();
-                outstanding = 
updateOutstandingFromLatePayment(params.getPeriodStartDate(), 
params.getLatePaymentMap(), outstanding);
-                outstanding = 
updateBalanceForInterestCalculation(params.getPrincipalPortionMap(), 
params.getPeriodStartDate(),
-                        outstanding, false);
-                if (params.getLatePaymentMap().isEmpty() && 
!outstanding.isGreaterThanZero()) {
+                        params.getInstallments(), false, lastRestDate);
+                if (params.getLatePaymentMap().isEmpty() && 
isCompleted(params.getInstallments())) {
+                    outstanding = outstanding.zero();
+                } else {
+                    outstanding = 
updateBalanceForInterestCalculation(params.getPrincipalPortionMap(), 
params.getPeriodStartDate(),
+                            outstanding, false);
+                }
+                if (params.getLatePaymentMap().isEmpty() && 
outstanding.isZero()) {
                     break;
                 }
             }
 
-            if (outstanding.isGreaterThanZero()) {
+            if (!outstanding.isZero()) {
                 PrincipalInterest principalInterestForThisPeriod = 
calculatePrincipalInterestComponentsForPeriod(
                         this.paymentPeriodsInOneYearCalculator, 
interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(),
                         totalInterest.zero(), totalInterest.zero(), 
totalInterest.zero(), outstanding, loanApplicationTerms,
@@ -1169,15 +1193,31 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                         params.getActualRepaymentDate(), applicableVariations);
                 Money interest = principalInterestForThisPeriod.interest();
                 totalInterest = totalInterest.plus(interest);
-                if 
(loanApplicationTerms.getInterestRecalculationCompoundingMethod().isInterestCompoundingEnabled())
 {
-                    LocalDate compoundingEffectiveDate = 
getNextCompoundScheduleDate(params.getActualRepaymentDate().minusDays(1),
-                            loanApplicationTerms, holidayDetailDTO);
-                    params.getLatePaymentMap().put(compoundingEffectiveDate, 
interest);
 
+                Money uncompounded = params.getUnCompoundedAmount();
+                Money compounded = uncompounded.zero();
+                for (Map.Entry<LocalDate, Money> mapEntry : 
params.getCompoundingMap().entrySet()) {
+                    if 
(mapEntry.getKey().isAfter(params.getPeriodStartDate())) {
+                        compounded = compounded.plus(mapEntry.getValue());
+                    }
+                }
+                Money compoundedForThisPeriod = compounded.minus(uncompounded);
+                Money uncompoundedForThisPeriod = 
interest.minus(compoundedForThisPeriod);
+                params.setUnCompoundedAmount(uncompoundedForThisPeriod);
+                LocalDate compoundingDate = params.getPeriodStartDate();
+                if (loanApplicationTerms.allowCompoundingOnEod()) {
+                    compoundingDate = compoundingDate.minusDays(1);
+                }
+                compoundingDate = getNextCompoundScheduleDate(compoundingDate, 
loanApplicationTerms, holidayDetailDTO);
+                if(compoundingDate.isEqual(params.getActualRepaymentDate())){
+                    params.getCompoundingMap().put(compoundingDate, 
uncompoundedForThisPeriod);
+                    
params.setUnCompoundedAmount(uncompoundedForThisPeriod.zero());
                 }
+                
+
             }
             params.setPeriodStartDate(params.getActualRepaymentDate());
-        } while (params.getActualRepaymentDate().isBefore(currentDate) && 
outstanding.isGreaterThanZero());
+        } while (params.getActualRepaymentDate().isBefore(currentDate) && 
!outstanding.isZero());
 
         if (totalInterest.isGreaterThanZero()) {
             LoanScheduleModelRepaymentPeriod installment = 
LoanScheduleModelRepaymentPeriod.repayment(params.getInstalmentNumber(),
@@ -1185,11 +1225,23 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     totalInterest.zero(), totalInterest.zero(), totalInterest, 
true);
             params.incrementInstalmentNumber();
             periods.add(installment);
+            params.getCompoundingDateVariations().put(startDate, new 
TreeMap<>(params.getCompoundingMap()));
             totalCumulativeInterest = 
totalCumulativeInterest.plus(totalInterest);
         }
         return totalCumulativeInterest;
     }
 
+    private boolean isCompleted(List<LoanRepaymentScheduleInstallment> 
installments) {
+        boolean isCompleted = true;
+        for (LoanRepaymentScheduleInstallment installment : installments) {
+            if (installment.isNotFullyPaidOff()) {
+                isCompleted = false;
+                break;
+            }
+        }
+        return isCompleted;
+    }
+
     private Collection<RecalculationDetail> 
getApplicableTransactionsForPeriod(final boolean applyInterestRecalculation,
             LocalDate repaymentDate, final Collection<RecalculationDetail> 
transactions) {
         Collection<RecalculationDetail> applicableTransactions = new 
ArrayList<>();
@@ -1221,21 +1273,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         currentTransactions.add(detail.getTransaction());
         detail.setProcessed(true);
         return currentTransactions;
-    }
 
-    private Money updateOutstandingFromLatePayment(LocalDate periodStartDate, 
Map<LocalDate, Money> latePaymentMap, Money outstanding) {
-        Map<LocalDate, Money> retainEntries = new HashMap<>();
-        for (Map.Entry<LocalDate, Money> mapEntry : latePaymentMap.entrySet()) 
{
-            if (!mapEntry.getKey().isAfter(periodStartDate)) {
-                outstanding = outstanding.plus(mapEntry.getValue());
-            } else {
-                retainEntries.put(mapEntry.getKey(), mapEntry.getValue());
-            }
-        }
-        latePaymentMap.clear();
-        latePaymentMap.putAll(retainEntries);
-        retainEntries.clear();
-        return outstanding;
     }
 
     /**
@@ -1315,22 +1353,13 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 holidayDetailDTO);
 
         Money principalPortion = loanTransaction.getPrincipalPortion(currency);
-        Money compoundedLatePayments = Money.zero(currency);
-        if 
(applicationTerms.getInterestRecalculationCompoundingMethod().isInterestCompoundingEnabled())
 {
-            compoundedLatePayments = 
compoundedLatePayments.plus(loanTransaction.getInterestPortion(currency));
-        }
-        if 
(applicationTerms.getInterestRecalculationCompoundingMethod().isFeeCompoundingEnabled())
 {
-            compoundedLatePayments = 
compoundedLatePayments.plus(loanTransaction.getFeeChargesPortion(currency)).plus(
-                    loanTransaction.getPenaltyChargesPortion(currency));
-        }
 
-        updateCompoundingAmount(params.getPrincipalPortionMap(), 
params.getLatePaymentMap(), currency, lastRestDate, principalPortion,
-                applicableDate);
-        updateCompoundingAmount(params.getPrincipalPortionMap(), 
params.getCompoundingMap(), currency, lastRestDate,
-                compoundedLatePayments, applicableDate);
+        updateLatePaymentCompoundingAmount(params.getPrincipalPortionMap(), 
params.getLatePaymentMap(), currency, lastRestDate,
+                principalPortion, applicableDate);
+        adjustCompoundedAmountWithPaidDetail(params, lastRestDate, 
applicableDate, loanTransaction, applicationTerms);
     }
 
-    private void updateCompoundingAmount(final Map<LocalDate, Money> 
principalVariationMap,
+    private void updateLatePaymentCompoundingAmount(final Map<LocalDate, 
Money> principalVariationMap,
             final Map<LocalDate, Money> latePaymentCompoundingMap, final 
MonetaryCurrency currency, final LocalDate lastRestDate,
             Money compoundedPortion, final LocalDate applicableDate) {
         Money appliedOnPrincipalVariationMap = Money.zero(currency);
@@ -1368,17 +1397,11 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
      */
     private void updateLatePaymentsToMap(final LoanApplicationTerms 
loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
             final MonetaryCurrency currency, final Map<LocalDate, Money> 
latePaymentMap, final LocalDate scheduledDueDate,
-            List<LoanRepaymentScheduleInstallment> installments, boolean 
applyRestFrequencyForPrincipal, final LocalDate lastRestDate,
-            final TreeMap<LocalDate, Money> compoundingMap) {
+            List<LoanRepaymentScheduleInstallment> installments, boolean 
applyRestFrequencyForPrincipal, final LocalDate lastRestDate) {
         latePaymentMap.clear();
         LocalDate currentDate = DateUtils.getLocalDateOfTenant();
 
         Money totalCompoundingAmount = Money.zero(currency);
-        Money compoundedMoney = Money.zero(currency);
-        if (!compoundingMap.isEmpty()) {
-            compoundedMoney = compoundingMap.get(lastRestDate);
-        }
-        boolean clearCompoundingMap = true;
         for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment 
: installments) {
             if (loanRepaymentScheduleInstallment.isNotFullyPaidOff()
                     && 
!loanRepaymentScheduleInstallment.getDueDate().isAfter(scheduledDueDate)
@@ -1395,104 +1418,181 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                             
.plus(loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency));
                 }
 
-                final Money changedCompoundedMoney = 
updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, 
currency,
-                        compoundingMap, loanRepaymentScheduleInstallment, 
lastRestDate, compoundedMoney, scheduledDueDate);
-                if (compoundedMoney.isZero() || 
!compoundedMoney.isEqualTo(changedCompoundedMoney)) {
-                    compoundedMoney = changedCompoundedMoney;
-                    clearCompoundingMap = false;
-                }
             }
         }
         if (totalCompoundingAmount.isGreaterThanZero()) {
             updateMapWithAmount(latePaymentMap, 
totalCompoundingAmount.negated(), lastRestDate);
         }
-        if (clearCompoundingMap) {
-            compoundingMap.clear();
-        }
     }
 
-    private Money updateMapWithCompoundingDetails(final LoanApplicationTerms 
loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
-            final MonetaryCurrency currency, final TreeMap<LocalDate, Money> 
compoundingMap,
-            final LoanRepaymentScheduleInstallment 
loanRepaymentScheduleInstallment, final LocalDate lastRestDate,
-            final Money compoundedMoney, final LocalDate scheduledDueDate) {
-        Money ignoreMoney = compoundedMoney;
-        if 
(loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled())
 {
-            LocalDate compoundingEffectiveDate = 
getNextCompoundScheduleDate(loanRepaymentScheduleInstallment.getDueDate().minusDays(1),
-                    loanApplicationTerms, holidayDetailDTO);
+    private void updateCompoundingMap(final LoanApplicationTerms 
loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
+            final LoanScheduleParams params, final LocalDate lastRestDate, 
final LocalDate scheduledDueDate) {
+        if (loanApplicationTerms.isInterestRecalculationEnabled()
+                && 
loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled())
 {
+            final MonetaryCurrency currency = params.getCurrency();
+            Money totalCompoundedAmount = Money.zero(currency);
+            boolean lastInstallmentIsPastDate = false;
+            for (LoanRepaymentScheduleInstallment 
loanRepaymentScheduleInstallment : params.getInstallments()) {
+                if 
(params.getCompoundingDateVariations().containsKey(loanRepaymentScheduleInstallment.getFromDate()))
 {
+                    lastInstallmentIsPastDate = 
params.applyInterestRecalculation()
+                            && 
loanRepaymentScheduleInstallment.getDueDate().isBefore(DateUtils.getLocalDateOfTenant());
+                } else {
+                    final boolean isPastDate = 
params.applyInterestRecalculation()
+                            && 
loanRepaymentScheduleInstallment.getDueDate().isBefore(DateUtils.getLocalDateOfTenant());
+                    boolean periodHasCompoundingDate = false;
+                    Money amountCharged = 
getIncomeForCompounding(loanApplicationTerms, currency, 
loanRepaymentScheduleInstallment);
+                    final Map<LocalDate, Money> compoundingMap = 
params.getCompoundingMap();
+                    LocalDate effectiveStartDate = 
loanRepaymentScheduleInstallment.getFromDate();
+                    if (loanApplicationTerms.allowCompoundingOnEod()) {
+                        effectiveStartDate = 
loanRepaymentScheduleInstallment.getFromDate().minusDays(1);
+                    }
+                    LocalDate compoundingEffectiveDate = 
getNextCompoundScheduleDate(effectiveStartDate, loanApplicationTerms,
+                            holidayDetailDTO);
+                    final LocalDate restDate = 
getNextRestScheduleDate(scheduledDueDate.minusDays(1), loanApplicationTerms,
+                            holidayDetailDTO);
+                    if 
(!compoundingEffectiveDate.isAfter(loanRepaymentScheduleInstallment.getDueDate()))
 {
+                        totalCompoundedAmount = 
totalCompoundedAmount.minus(params.getUnCompoundedAmount());
+                        periodHasCompoundingDate = true;
+                    }
+                    while 
(!compoundingEffectiveDate.isAfter(loanRepaymentScheduleInstallment.getDueDate()))
 {
+                        if 
(compoundingEffectiveDate.isEqual(loanRepaymentScheduleInstallment.getDueDate()))
 {
+                            Money amountToBeCompounding = 
amountCharged.minus(totalCompoundedAmount);
+                            updateMapWithAmount(compoundingMap, 
amountToBeCompounding, compoundingEffectiveDate);
+                            totalCompoundedAmount = 
totalCompoundedAmount.plus(amountToBeCompounding);
+
+                        } else if 
(compoundingMap.containsKey(compoundingEffectiveDate)) {
+                            Money compounedAmount = 
compoundingMap.get(compoundingEffectiveDate);
+                            totalCompoundedAmount = 
totalCompoundedAmount.plus(compounedAmount);
+                        }
 
-            if 
(compoundingEffectiveDate.isBefore(DateUtils.getLocalDateOfTenant())) {
-                Money amount = Money.zero(currency);
-                switch 
(loanApplicationTerms.getInterestRecalculationCompoundingMethod()) {
-                    case INTEREST:
-                        amount = 
amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
-                    break;
-                    case FEE:
-                        amount = 
amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
-                        amount = 
amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
-                    break;
-                    case INTEREST_AND_FEE:
-                        amount = 
amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
-                        amount = 
amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
-                        amount = 
amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
-                    break;
-                    default:
-                    break;
-                }
-                if (compoundingEffectiveDate.isBefore(scheduledDueDate)) {
-                    ignoreMoney = ignoreMoney.plus(amount);
-                    if (ignoreMoney.isGreaterThanZero()) {
-                        updateMapWithAmount(compoundingMap, ignoreMoney, 
compoundingEffectiveDate);
-                        updateMapWithAmount(compoundingMap, 
ignoreMoney.negated(), lastRestDate);
-                        ignoreMoney = ignoreMoney.zero();
+                        if (!loanApplicationTerms.allowCompoundingOnEod()) {
+                            compoundingEffectiveDate = 
compoundingEffectiveDate.plusDays(1);
+                        }
+                        compoundingEffectiveDate = 
getNextCompoundScheduleDate(compoundingEffectiveDate, loanApplicationTerms,
+                                holidayDetailDTO);
                     }
-                } else {
-                    if (ignoreMoney.isLessThanZero()) {
-                        LocalDate firstKey = compoundingMap.firstKey();
-                        updateMapWithAmount(compoundingMap, ignoreMoney, 
firstKey);
-                        updateMapWithAmount(compoundingMap, 
ignoreMoney.negated(), lastRestDate);
-                        ignoreMoney = ignoreMoney.zero();
+                    if (periodHasCompoundingDate) {
+                        if (isPastDate) {
+                            
updateMapWithAmount(params.getPrincipalPortionMap(),
+                                    
totalCompoundedAmount.plus(params.getUnCompoundedAmount()), lastRestDate);
+                        } else {
+                            Money amountToBeEffected = amountCharged;
+                            if (lastInstallmentIsPastDate) {
+                                amountToBeEffected = 
amountToBeEffected.plus(params.getUnCompoundedAmount());
+                            }
+                            
updateMapWithAmount(params.getPrincipalPortionMap(), amountToBeEffected, 
restDate);
+                        }
+                    }
+                    if (totalCompoundedAmount.isGreaterThanZero()) {
+                        
params.getCompoundingDateVariations().put(loanRepaymentScheduleInstallment.getFromDate(),
+                                new TreeMap<>(params.getCompoundingMap()));
+                        for (Map.Entry<LocalDate, Money> mapEntry : 
params.getCompoundingMap().entrySet()) {
+                            if 
(!mapEntry.getKey().isAfter(loanRepaymentScheduleInstallment.getDueDate())) {
+                                
updateMapWithAmount(params.getPrincipalPortionMap(), 
mapEntry.getValue().negated(), mapEntry.getKey());
+                            }else if(params.getUnCompoundedAmount().isEqualTo( 
mapEntry.getValue())){
+                                totalCompoundedAmount = 
totalCompoundedAmount.plus(params.getUnCompoundedAmount());
+                            }
+                        }
+                        
params.minusUnCompoundedAmount(params.getUnCompoundedAmount());
+                        params.getCompoundingMap().clear();
+                        
params.addUnCompoundedAmount(amountCharged.minus(totalCompoundedAmount.minus(params.getCompoundedInLastInstallment())));
+                    } else {
+                        params.getCompoundingMap().clear();
+                        
params.getCompoundingDateVariations().put(loanRepaymentScheduleInstallment.getFromDate(),
+                                new TreeMap<>(params.getCompoundingMap()));
+                        params.addUnCompoundedAmount(amountCharged);
                     }
-                    updateMapWithAmount(compoundingMap, amount, 
compoundingEffectiveDate);
-                    updateMapWithAmount(compoundingMap, amount.negated(), 
lastRestDate);
+                    
params.setCompoundedInLastInstallment(amountCharged.zero());
+                    lastInstallmentIsPastDate = isPastDate;
                 }
+
             }
         }
-        return ignoreMoney;
+
+    }
+
+    private Money getIncomeForCompounding(final LoanApplicationTerms 
loanApplicationTerms, final MonetaryCurrency currency,
+            LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment) 
{
+        Money interestCharged = Money.zero(currency);
+        Money feeCharged = Money.zero(currency);
+        Money penaltyCharged = Money.zero(currency);
+        Money amountCharged = Money.zero(currency);
+        switch 
(loanApplicationTerms.getInterestRecalculationCompoundingMethod()) {
+            case INTEREST:
+                interestCharged = 
interestCharged.plus(loanRepaymentScheduleInstallment.getInterestCharged(currency));
+            break;
+            case FEE:
+                feeCharged = 
feeCharged.plus(loanRepaymentScheduleInstallment.getFeeChargesCharged(currency));
+                penaltyCharged = 
penaltyCharged.plus(loanRepaymentScheduleInstallment.getPenaltyChargesCharged(currency));
+            break;
+            case INTEREST_AND_FEE:
+                interestCharged = 
interestCharged.plus(loanRepaymentScheduleInstallment.getInterestCharged(currency));
+                feeCharged = 
feeCharged.plus(loanRepaymentScheduleInstallment.getFeeChargesCharged(currency));
+                penaltyCharged = 
penaltyCharged.plus(loanRepaymentScheduleInstallment.getPenaltyChargesCharged(currency));
+            break;
+            default:
+            break;
+        }
+        amountCharged = interestCharged.plus(feeCharged).plus(penaltyCharged);
+        return amountCharged;
+    }
+
+    private void adjustCompoundedAmountWithPaidDetail(final LoanScheduleParams 
params, final LocalDate lastRestDate,
+            final Collection<LoanTransaction> transactions, final 
LoanApplicationTerms loanApplicationTerms,
+            HolidayDetailDTO holidayDetailDTO) {
+        for (LoanTransaction loanTransaction : transactions) {
+            final LocalDate amountApplicableDate = 
getNextRestScheduleDate(loanTransaction.getTransactionDate().minusDays(1),
+                    loanApplicationTerms, holidayDetailDTO);
+            adjustCompoundedAmountWithPaidDetail(params, lastRestDate, 
amountApplicableDate, loanTransaction, loanApplicationTerms);
+        }
+    }
+
+    private void adjustCompoundedAmountWithPaidDetail(final LoanScheduleParams 
params, final LocalDate lastRestDate,
+            final LocalDate amountApplicableDate, final LoanTransaction 
transaction, final LoanApplicationTerms loanApplicationTerms) {
+        adjustCompoundedAmountWithPaidDetail(params.getPrincipalPortionMap(), 
lastRestDate, amountApplicableDate, transaction,
+                loanApplicationTerms, params.getCurrency());
+    }
+
+    private void adjustCompoundedAmountWithPaidDetail(final Map<LocalDate, 
Money> principalPortionMap, final LocalDate lastRestDate,
+            final LocalDate amountApplicableDate, final LoanTransaction 
transaction, final LoanApplicationTerms loanApplicationTerms,
+            final MonetaryCurrency currency) {
+        if (!amountApplicableDate.isEqual(lastRestDate)) {
+            Money compoundedIncome = 
fetchCompoundedArrears(loanApplicationTerms, currency, transaction);
+            updateMapWithAmount(principalPortionMap, compoundedIncome, 
amountApplicableDate);
+            updateMapWithAmount(principalPortionMap, 
compoundedIncome.negated(), lastRestDate);
+        }
     }
 
-    private void populateCompoundingDatesInPeriod(final LocalDate startDate, 
final LocalDate endDate, final LocalDate currentDate,
+    private void populateCompoundingDatesInPeriod(final LocalDate startDate, 
final LocalDate endDate,
             final LoanApplicationTerms loanApplicationTerms, final 
HolidayDetailDTO holidayDetailDTO,
-            final Map<LocalDate, Money> compoundingMap, final Set<LoanCharge> 
charges, MonetaryCurrency currency) {
+            final LoanScheduleParams scheduleParams, final Set<LoanCharge> 
charges, MonetaryCurrency currency) {
         if 
(loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled())
 {
+            final Map<LocalDate, Money> compoundingMap = 
scheduleParams.getCompoundingMap();
             LocalDate lastCompoundingDate = startDate;
             LocalDate compoundingDate = startDate;
-            while (compoundingDate.isBefore(endDate) && 
compoundingDate.isBefore(currentDate)) {
+            boolean addUncompounded = true;
+            while (compoundingDate.isBefore(endDate)) {
+                if (loanApplicationTerms.allowCompoundingOnEod()) {
+                    compoundingDate = compoundingDate.minusDays(1);
+                }
                 compoundingDate = getNextCompoundScheduleDate(compoundingDate, 
loanApplicationTerms, holidayDetailDTO);
-                if (!compoundingDate.isBefore(currentDate)) {
-                    break;
-                } else if (compoundingDate.isAfter(endDate)) {
-                    updateMapWithAmount(compoundingMap, Money.zero(currency), 
compoundingDate);
-                } else {
+
+                if (compoundingDate.isBefore(endDate)) {
                     Money feeChargesForInstallment = 
cumulativeFeeChargesDueWithin(lastCompoundingDate, compoundingDate, charges, 
currency,
                             null, loanApplicationTerms.getPrincipal(), null, 
false);
                     Money penaltyChargesForInstallment = 
cumulativePenaltyChargesDueWithin(lastCompoundingDate, compoundingDate, charges,
                             currency, null, 
loanApplicationTerms.getPrincipal(), null, false);
-                    updateMapWithAmount(compoundingMap, 
feeChargesForInstallment.plus(penaltyChargesForInstallment), compoundingDate);
+                    Money compoundAmount = 
feeChargesForInstallment.plus(penaltyChargesForInstallment);
+                    if (addUncompounded) {
+                        compoundAmount = 
compoundAmount.plus(scheduleParams.getUnCompoundedAmount());
+                        addUncompounded = false;
+                    }
+                    updateMapWithAmount(compoundingMap, compoundAmount, 
compoundingDate);
                 }
-                lastCompoundingDate = compoundingDate;
-            }
-        }
-    }
 
-    protected void clearMapDetails(final LocalDate startDate, final 
Map<LocalDate, Money> compoundingMap) {
-        Map<LocalDate, Money> temp = new HashMap<>();
-        for (LocalDate date : compoundingMap.keySet()) {
-            if (!date.isBefore(startDate)) {
-                temp.put(date, compoundingMap.get(date));
+                lastCompoundingDate = compoundingDate;
             }
         }
-        compoundingMap.clear();
-        compoundingMap.putAll(temp);
     }
 
     /**
@@ -1580,16 +1680,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             principalPaid = map.get(amountApplicableDate).plus(principalPaid);
         }
         map.put(amountApplicableDate, principalPaid);
-    }
 
-    private Money getTotalAmount(final Map<LocalDate, Money> map, final 
MonetaryCurrency currency) {
-        Money total = Money.zero(currency);
-        for (Map.Entry<LocalDate, Money> mapEntry : map.entrySet()) {
-            if (mapEntry.getKey().isBefore(DateUtils.getLocalDateOfTenant())) {
-                total = total.plus(mapEntry.getValue());
-            }
-        }
-        return total;
     }
 
     @Override
@@ -1737,8 +1828,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             }
 
             // get the loan application terms from the Loan object
-            final LoanApplicationTerms loanApplicationTerms = 
loan.getLoanApplicationTerms(applicationCurrency, restCalendarInstance,
-                    compoundingCalendarInstance, loanCalendar, 
floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays);
+            final LoanApplicationTerms loanApplicationTerms = loan
+                    .getLoanApplicationTerms(applicationCurrency, 
restCalendarInstance, compoundingCalendarInstance, loanCalendar,
+                            floatingRateDTO, isSkipRepaymentonmonthFirst, 
numberofdays, holidayDetailDTO);
 
             // for applying variations
             Collection<LoanTermVariationsData> loanTermVariations = 
loanApplicationTerms.getLoanTermVariations().getInterestRateChanges();
@@ -2117,6 +2209,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             // action will be performed on this value
             Money reducePrincipal = outstandingBalanceAsPerRest.zero();
 
+            Money uncompoundedAmount = outstandingBalanceAsPerRest.zero();
             // principal changes will be added along with date(after applying
             // rest)
             // from when these amounts will effect the outstanding balance for
@@ -2133,6 +2226,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             // from when these amounts will effect the outstanding balance for
             // interest calculation
             final TreeMap<LocalDate, Money> compoundingMap = new TreeMap<>();
+            final Map<LocalDate, Map<LocalDate, Money>> 
compoundingDateVariations = new HashMap<>();
             LocalDate currentDate = DateUtils.getLocalDateOfTenant();
             LocalDate lastRestDate = currentDate;
             if (loanApplicationTerms.getRestCalendarInstance() != null) {
@@ -2179,6 +2273,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 // process the installment only if recalculate from date is
                 // greater than due date
                 if (installment.getDueDate().isAfter(lastInstallmentDate)) {
+                    if 
(totalCumulativePrincipal.isGreaterThanOrEqualTo(loanApplicationTerms.getTotalDisbursedAmount()))
 {
+                        break;
+                    }
                     LocalDate previousRepaymentDate = actualRepaymentDate;
                     actualRepaymentDate = 
this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, 
loanApplicationTerms,
                             isFirstRepayment, holidayDetailDTO);
@@ -2272,6 +2369,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 // 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);
@@ -2284,7 +2384,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
 
             // updates the map with over due amounts
             updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, 
currency, latePaymentMap, lastInstallmentDate,
-                    newRepaymentScheduleInstallments, true, lastRestDate, 
compoundingMap);
+                    newRepaymentScheduleInstallments, true, lastRestDate);
 
             // for partial schedule generation
             if (!newRepaymentScheduleInstallments.isEmpty() && 
totalCumulativeInterest.isGreaterThanZero()) {
@@ -2293,10 +2393,11 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                         loanTermInDays, periodStartDate, actualRepaymentDate, 
totalCumulativePrincipal, totalCumulativeInterest,
                         totalFeeChargesCharged, totalPenaltyChargesCharged, 
totalRepaymentExpected,
                         totalOutstandingInterestPaymentDueToGrace, 
reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap,
-                        disburseDetailMap, principalToBeScheduled, 
outstandingBalance, outstandingBalanceAsPerRest,
+                        uncompoundedAmount, disburseDetailMap, 
principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest,
                         newRepaymentScheduleInstallments, 
recalculationDetails, loanRepaymentScheduleTransactionProcessor,
                         scheduleTillDate, currency, 
applyInterestRecalculation);
                 retainedInstallments.addAll(newRepaymentScheduleInstallments);
+                
loanScheduleParams.getCompoundingDateVariations().putAll(compoundingDateVariations);
             }
 
         }
@@ -2328,6 +2429,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             final Map<LocalDate, Money> principalPortionMap, 
LoanRepaymentScheduleInstallment installment,
             Collection<RecalculationDetail> applicableTransactions, Money 
actualPrincipalPortion) {
         Money unprocessed = Money.zero(currency);
+        Money totalUnprocessed = Money.zero(currency);
         for (RecalculationDetail detail : applicableTransactions) {
             if (!detail.isProcessed()) {
                 Money principalProcessed = 
installment.getPrincipalCompleted(currency);
@@ -2357,10 +2459,53 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 LocalDate applicableDate = 
getNextRestScheduleDate(detail.getTransactionDate().minusDays(1), 
loanApplicationTerms,
                         holidayDetailDTO);
                 updateMapWithAmount(principalPortionMap, unprocessed, 
applicableDate);
+                totalUnprocessed = totalUnprocessed.plus(unprocessed);
+
+            }
+        }
+        return totalUnprocessed;
+    }
 
+    private Money updateCompoundingDetailsForPartialScheduleGeneration(final 
LoanRepaymentScheduleInstallment installment,
+            LoanApplicationTerms loanApplicationTerms, Map<LocalDate, Money> 
principalMap,
+            final Map<LocalDate, Map<LocalDate, Money>> 
compoundingDateVariations, final Money uncompoundedAmount,
+            final Collection<RecalculationDetail> applicableTransactions, 
LocalDate lastRestDate, HolidayDetailDTO holidayDetailDTO) {
+        Money uncompounded = uncompoundedAmount;
+        MonetaryCurrency currency = uncompoundedAmount.getCurrency();
+        for (RecalculationDetail detail : applicableTransactions) {
+            LocalDate applicableDate = 
getNextRestScheduleDate(detail.getTransactionDate().minusDays(1), 
loanApplicationTerms,
+                    holidayDetailDTO);
+            adjustCompoundedAmountWithPaidDetail(principalMap, lastRestDate, 
applicableDate, detail.getTransaction(), loanApplicationTerms,
+                    currency);
+        }
+        Money amountCharged = getIncomeForCompounding(loanApplicationTerms, 
currency, installment);
+        final List<LoanInterestRecalcualtionAdditionalDetails> details = 
installment.getLoanCompoundingDetails();
+        Money totalCompounded = Money.zero(currency);
+        Map<LocalDate, Money> compoundingMap = new TreeMap<>();
+        for (LoanInterestRecalcualtionAdditionalDetails additionalDetails : 
details) {
+            LocalDate effectiveDate = additionalDetails.getEffectiveDate();
+            if (loanApplicationTerms.allowCompoundingOnEod()) {
+                effectiveDate = effectiveDate.plusDays(1);
+            }
+            compoundingMap.put(effectiveDate, Money.of(currency, 
additionalDetails.getAmount()));
+            totalCompounded = 
totalCompounded.plus(additionalDetails.getAmount());
+            updateMapWithAmount(principalMap, Money.of(currency, 
additionalDetails.getAmount()).negated(), effectiveDate);
+        }
+        compoundingDateVariations.put(installment.getFromDate(), 
compoundingMap);
+        if (totalCompounded.isGreaterThanZero()) {
+            final boolean isPastDate = 
installment.getDueDate().isBefore(DateUtils.getLocalDateOfTenant());
+            final LocalDate restDate = 
getNextRestScheduleDate(installment.getDueDate().minusDays(1), 
loanApplicationTerms,
+                    holidayDetailDTO);
+            if (isPastDate) {
+                updateMapWithAmount(principalMap, totalCompounded, 
lastRestDate);
+            } else {
+                updateMapWithAmount(principalMap, totalCompounded, restDate);
             }
+            uncompounded = 
amountCharged.plus(uncompounded).minus(totalCompounded);
+        } else {
+            uncompounded = uncompounded.plus(amountCharged);
         }
-        return unprocessed;
+        return uncompounded;
     }
 
     private void updateAmortization(final MathContext mc, final 
LoanApplicationTerms loanApplicationTerms, int periodNumber,
@@ -2432,7 +2577,7 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     scheduledLoanInstallment.periodFromDate(), 
scheduledLoanInstallment.periodDueDate(),
                     scheduledLoanInstallment.principalDue(), 
scheduledLoanInstallment.interestDue(),
                     scheduledLoanInstallment.feeChargesDue(), 
scheduledLoanInstallment.penaltyChargesDue(),
-                    
scheduledLoanInstallment.isRecalculatedInterestComponent());
+                    
scheduledLoanInstallment.isRecalculatedInterestComponent(), 
scheduledLoanInstallment.getLoanCompoundingDetails());
             installments.add(installment);
         }
         return installment;
@@ -2473,6 +2618,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
         } else {
             CalendarInstance calendarInstance = 
loanApplicationTerms.getCompoundingCalendarInstance();
             nextScheduleDate = 
CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
+            if (loanApplicationTerms.allowCompoundingOnEod()) {
+                nextScheduleDate = nextScheduleDate.plusDays(1);
+            }
         }
 
         return nextScheduleDate;
@@ -2510,9 +2658,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 penaltyCharges = 
penaltyCharges.plus(currentInstallment.getPenaltyChargesOutstanding(currency));
             }
         }
-
+        final List<LoanInterestRecalcualtionAdditionalDetails> 
compoundingDetails = null;
         return new LoanRepaymentScheduleInstallment(null, 0, onDate, onDate, 
totalPrincipal.getAmount(), totalInterest.getAmount(),
-                feeCharges.getAmount(), penaltyCharges.getAmount(), false);
+                feeCharges.getAmount(), penaltyCharges.getAmount(), false, 
compoundingDetails);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/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 f53a50e..1bbefa4 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
@@ -25,7 +25,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.TreeMap;
 
-import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
@@ -70,7 +69,6 @@ public class DecliningBalanceInterestLoanScheduleGenerator 
extends AbstractLoanS
 
         LocalDate interestStartDate = periodStartDate;
         Money interestForThisInstallment = totalCumulativePrincipal.zero();
-        Money compoundedMoney = totalCumulativePrincipal.zero();
         Money compoundedInterest = totalCumulativePrincipal.zero();
         Money balanceForInterestCalculation = outstandingBalance;
         Money cumulatingInterestDueToGrace = 
cumulatingInterestPaymentDueToGrace;
@@ -89,13 +87,6 @@ public class DecliningBalanceInterestLoanScheduleGenerator 
extends AbstractLoanS
             }
         }
         if (principalVariation != null) {
-            // identifies rest date after current date for reducing all
-            // compounding
-            // values
-            LocalDate compoundingEndDate = 
principalVariation.ceilingKey(DateUtils.getLocalDateOfTenant());
-            if (compoundingEndDate == null) {
-                compoundingEndDate = DateUtils.getLocalDateOfTenant();
-            }
 
             for (Map.Entry<LocalDate, Money> principal : 
principalVariation.entrySet()) {
 
@@ -120,7 +111,7 @@ public class DecliningBalanceInterestLoanScheduleGenerator 
extends AbstractLoanS
                         }
                         // fee compounding will be done after calculation
                         compoundFee = compoundingMap.get(principal.getKey());
-                        compoundedMoney = 
compoundedMoney.plus(interestToBeCompounded).plus(compoundFee);
+                        compoundingMap.put(principal.getKey(), 
interestToBeCompounded.plus(compoundFee));
                     }
                     balanceForInterestCalculation = 
balanceForInterestCalculation.plus(principal.getValue()).plus(compoundFee);
                     if (interestRates.containsKey(principal.getKey())) {
@@ -129,14 +120,6 @@ public class DecliningBalanceInterestLoanScheduleGenerator 
extends AbstractLoanS
                 }
 
             }
-            if (!periodEndDate.isBefore(compoundingEndDate)) {
-                balanceForInterestCalculation = 
balanceForInterestCalculation.minus(compoundedMoney);
-                compoundingMap.clear();
-            } else if (compoundedMoney.isGreaterThanZero()) {
-                compoundingMap.put(periodEndDate, compoundedMoney);
-                compoundingMap.put(compoundingEndDate, 
compoundedMoney.negated());
-                clearMapDetails(periodEndDate, compoundingMap);
-            }
         }
 
         final PrincipalInterest result = 
loanApplicationTerms.calculateTotalInterestForPeriod(calculator,

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
index 7afc002..b57f215 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
@@ -62,8 +62,8 @@ public class DefaultScheduledDateGenerator implements 
ScheduledDateGenerator {
         } else {
             Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
             dueRepaymentPeriodDate = 
getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
-                    loanApplicationTerms.getRepaymentEvery(), 
lastRepaymentDate, loanApplicationTerms.getNthDay(),
-                    loanApplicationTerms.getWeekDayType());
+                    loanApplicationTerms.getRepaymentEvery(), 
lastRepaymentDate, null,
+                    null);
             dueRepaymentPeriodDate = 
CalendarUtils.adjustDate(dueRepaymentPeriodDate, 
loanApplicationTerms.getSeedDate(),
                     loanApplicationTerms.getRepaymentPeriodFrequencyType());
             if (currentCalendar != null) {
@@ -219,7 +219,8 @@ public class DefaultScheduledDateGenerator implements 
ScheduledDateGenerator {
 
     @Override
     public LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(final 
PeriodFrequencyType repaymentPeriodFrequencyType,
-            final int repaidEvery, final LocalDate firstRepaymentDate) {
+            final int repaidEvery, final LocalDate firstRepaymentDate, final 
Calendar loanCalendar, final HolidayDetailDTO holidayDetailDTO, 
+            final LoanApplicationTerms loanApplicationTerms) {
 
         LocalDate idealDisbursementDate = null;
 
@@ -231,7 +232,15 @@ public class DefaultScheduledDateGenerator implements 
ScheduledDateGenerator {
                 idealDisbursementDate = 
firstRepaymentDate.minusWeeks(repaidEvery);
             break;
             case MONTHS:
-                idealDisbursementDate = 
firstRepaymentDate.minusMonths(repaidEvery);
+                if (loanCalendar == null) {
+                    idealDisbursementDate = 
firstRepaymentDate.minusMonths(repaidEvery);
+                } else {
+                    idealDisbursementDate = 
CalendarUtils.getNewRepaymentMeetingDate(loanCalendar.getRecurrence(),
+                            firstRepaymentDate.minusMonths(repaidEvery), 
firstRepaymentDate.minusMonths(repaidEvery), repaidEvery,
+                            
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType),
+                            holidayDetailDTO.getWorkingDays(), 
loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(),
+                            loanApplicationTerms.getNumberOfdays());
+                }
             break;
             case YEARS:
                 idealDisbursementDate = 
firstRepaymentDate.minusYears(repaidEvery);

Reply via email to