Repository: incubator-fineract
Updated Branches:
  refs/heads/develop 11cd6ef33 -> 961aa3df8


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
index 7d4a8f4..8f871b0 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java
@@ -91,7 +91,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
             LoanApiConstants.syncDisbursementWithMeetingParameterName,// 
optional
             LoanApiConstants.linkAccountIdParameterName, 
LoanApiConstants.disbursementDataParameterName,
             LoanApiConstants.emiAmountParameterName, 
LoanApiConstants.maxOutstandingBalanceParameterName,
-            LoanProductConstants.graceOnArrearsAgeingParameterName, 
LoanApiConstants.createStandingInstructionAtDisbursementParameterName));
+            LoanProductConstants.graceOnArrearsAgeingParameterName, 
LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
+            LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose));
 
     private final FromJsonHelper fromApiJsonHelper;
     private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
@@ -469,6 +470,18 @@ public final class LoanApplicationCommandFromApiJsonHelper 
{
             
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
                     .ignoreIfNull().positiveAmount();
         }
+
+        if(loanProduct.canUseForTopup()){
+            
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isTopup, element)){
+                final Boolean isTopup = 
this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup, element);
+                
baseDataValidator.reset().parameter(LoanApiConstants.isTopup).value(isTopup).validateForBooleanValue();
+
+                if(isTopup != null && isTopup){
+                    final Long loanId = 
this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose, 
element);
+                    
baseDataValidator.reset().parameter(LoanApiConstants.loanIdToClose).value(loanId).notNull().longGreaterThanZero();
+                }
+            }
+        }
         validateLoanMultiDisbursementdate(element, baseDataValidator, 
expectedDisbursementDate, principal);
         validatePartialPeriodSupport(interestCalculationPeriodType, 
baseDataValidator, element, loanProduct);
         if (!dataValidationErrors.isEmpty()) { throw new 
PlatformApiDataValidationException(dataValidationErrors); }
@@ -899,6 +912,19 @@ public final class LoanApplicationCommandFromApiJsonHelper 
{
             
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
                     .ignoreIfNull().positiveAmount();
         }
+
+        if(loanProduct.canUseForTopup()){
+            
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isTopup, element)){
+                final Boolean isTopup = 
this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup, element);
+                
baseDataValidator.reset().parameter(LoanApiConstants.isTopup).value(isTopup).ignoreIfNull().validateForBooleanValue();
+
+                if(isTopup != null && isTopup){
+                    final Long loanId = 
this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose, 
element);
+                    
baseDataValidator.reset().parameter(LoanApiConstants.loanIdToClose).value(loanId).notNull().longGreaterThanZero();
+                }
+            }
+        }
+
         validateLoanMultiDisbursementdate(element, baseDataValidator, 
expectedDisbursementDate, principal);
         validatePartialPeriodSupport(interestCalculationPeriodType, 
baseDataValidator, element, loanProduct);
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
index 8432273..4344455 100755
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
@@ -40,6 +40,8 @@ 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.GeneralPlatformDomainRuleException;
+import 
org.apache.fineract.infrastructure.core.exceptionmapper.PlatformDomainRuleExceptionMapper;
 import 
org.apache.fineract.infrastructure.entityaccess.exception.NotOfficeSpecificProductException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
@@ -83,17 +85,7 @@ import 
org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
 import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
 import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
 import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
-import 
org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
-import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
-import 
org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
-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.LoanStatus;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.*;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
@@ -287,6 +279,43 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                     productRelatedDetail.getRepayEvery(), 
productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
                     newLoanApplication);
 
+            if(loanProduct.canUseForTopup() && clientId != null){
+                final Boolean isTopup = 
command.booleanObjectValueOfParameterNamed(LoanApiConstants.isTopup);
+                if(null == isTopup){
+                    newLoanApplication.setIsTopup(false);
+                }else{
+                    newLoanApplication.setIsTopup(isTopup);
+                }
+
+                if(newLoanApplication.isTopup()){
+                    final Long loanIdToClose = 
command.longValueOfParameterNamed(LoanApiConstants.loanIdToClose);
+                    final Loan loanToClose = 
this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
clientId);
+                    if(loanToClose == null){
+                        throw new LoanNotFoundException(loanIdToClose);
+                    }
+                    if(loanToClose.isMultiDisburmentLoan() && 
!loanToClose.isInterestRecalculationEnabledForProduct()){
+                        throw new 
GeneralPlatformDomainRuleException("error.msg.loan.topup.on.multi.tranche.loan.without.interest.recalculation.not.supported",
+                                "Topup on loan with multi-tranche disbursal 
and without interest recalculation is not supported.");
+                    }
+                    final LocalDate disbursalDateOfLoanToClose = 
loanToClose.getDisbursementDate();
+                    
if(!newLoanApplication.getSubmittedOnDate().isAfter(disbursalDateOfLoanToClose)){
+                        throw new 
GeneralPlatformDomainRuleException("error.msg.loan.submitted.date.before.topup.loan.disbursal.date",
+                                "Submitted date of this loan application 
"+newLoanApplication.getSubmittedOnDate()
+                                        +" is before the disbursed date of 
loan to be closed "+ disbursalDateOfLoanToClose);
+                    }
+                    BigDecimal loanOutstanding = 
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
+                            
newLoanApplication.getDisbursementDate()).getAmount();
+                    final BigDecimal firstDisbursalAmount = 
newLoanApplication.getFirstDisbursalAmount();
+                    if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
+                        throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
+                                "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
+                    }
+
+                    final LoanTopupDetails topupDetails = new 
LoanTopupDetails(newLoanApplication, loanIdToClose);
+                    newLoanApplication.setTopupLoanDetails(topupDetails);
+                }
+            }
+
             this.loanRepository.save(newLoanApplication);
 
             if (loanProduct.isInterestRecalculationEnabled()) {
@@ -555,6 +584,7 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
             final Set<LoanCollateral> possiblyModifedLoanCollateralItems = 
this.loanCollateralAssembler
                     .fromParsedJson(command.parsedJson());
 
+
             final Map<String, Object> changes = 
existingLoanApplication.loanApplicationModification(command, 
possiblyModifedLoanCharges,
                     possiblyModifedLoanCollateralItems, this.aprCalculator, 
isChargeModified);
 
@@ -620,6 +650,56 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                 updateProductRelatedDetails(productRelatedDetail, 
existingLoanApplication);
             }
 
+            if(existingLoanApplication.getLoanProduct().canUseForTopup() && 
existingLoanApplication.getClientId() != null){
+                final Boolean isTopup = 
command.booleanObjectValueOfParameterNamed(LoanApiConstants.isTopup);
+                
if(command.isChangeInBooleanParameterNamed(LoanApiConstants.isTopup, 
existingLoanApplication.isTopup())){
+                    existingLoanApplication.setIsTopup(isTopup);
+                    changes.put(LoanApiConstants.isTopup, isTopup);
+                }
+
+                if(existingLoanApplication.isTopup()){
+                    final Long loanIdToClose = 
command.longValueOfParameterNamed(LoanApiConstants.loanIdToClose);
+                    LoanTopupDetails existingLoanTopupDetails = 
existingLoanApplication.getTopupLoanDetails();
+                    if(existingLoanTopupDetails == null
+                            || (existingLoanTopupDetails != null && 
existingLoanTopupDetails.getLoanIdToClose() != loanIdToClose)){
+                        final Loan loanToClose = 
this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
existingLoanApplication.getClientId());
+                        if(loanToClose == null){
+                            throw new LoanNotFoundException(loanIdToClose);
+                        }
+                        if(loanToClose.isMultiDisburmentLoan() && 
!loanToClose.isInterestRecalculationEnabledForProduct()){
+                            throw new 
GeneralPlatformDomainRuleException("error.msg.loan.topup.on.multi.tranche.loan.without.interest.recalculation.not.supported",
+                                    "Topup on loan with multi-tranche 
disbursal and without interest recalculation is not supported.");
+                        }
+                        final LocalDate disbursalDateOfLoanToClose = 
loanToClose.getDisbursementDate();
+                        
if(!existingLoanApplication.getSubmittedOnDate().isAfter(disbursalDateOfLoanToClose)){
+                            throw new 
GeneralPlatformDomainRuleException("error.msg.loan.submitted.date.before.topup.loan.disbursal.date",
+                                    "Submitted date of this loan application 
"+existingLoanApplication.getSubmittedOnDate()
+                                            +" is before the disbursed date of 
loan to be closed "+ disbursalDateOfLoanToClose);
+                        }
+                        BigDecimal loanOutstanding = 
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
+                                
existingLoanApplication.getDisbursementDate()).getAmount();
+                        final BigDecimal firstDisbursalAmount = 
existingLoanApplication.getFirstDisbursalAmount();
+                        if(loanOutstanding.compareTo(firstDisbursalAmount) > 
0){
+                            throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
+                                    "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
+                        }
+
+                        final LoanTopupDetails topupDetails = new 
LoanTopupDetails(existingLoanApplication, loanIdToClose);
+                        
existingLoanApplication.setTopupLoanDetails(topupDetails);
+                        changes.put(LoanApiConstants.loanIdToClose, 
loanIdToClose);
+                    }
+                }else{
+                    existingLoanApplication.setTopupLoanDetails(null);
+                }
+            } else {
+                if(existingLoanApplication.isTopup()){
+                    existingLoanApplication.setIsTopup(false);
+                    existingLoanApplication.setTopupLoanDetails(null);
+                    changes.put(LoanApiConstants.isTopup, false);
+                }
+            }
+
+
             final String fundIdParamName = "fundId";
             if (changes.containsKey(fundIdParamName)) {
                 final Long fundId = 
command.longValueOfParameterNamed(fundIdParamName);
@@ -981,6 +1061,22 @@ public class 
LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                 loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, 
currentUser);
             }
 
+            if(loan.isTopup() && loan.getClientId() != null){
+                final Long loanIdToClose = 
loan.getTopupLoanDetails().getLoanIdToClose();
+                final Loan loanToClose = 
this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
loan.getClientId());
+                if(loanToClose == null){
+                    throw new LoanNotFoundException(loanIdToClose);
+                }
+
+                BigDecimal loanOutstanding = 
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
+                        expectedDisbursementDate).getAmount();
+                final BigDecimal firstDisbursalAmount = 
loan.getFirstDisbursalAmount();
+                if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
+                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
+                            "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
+                }
+            }
+
             saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
 
             final String noteText = 
command.stringValueOfParameterNamed("note");

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 7283ca0..a99064a 100755
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -52,7 +52,9 @@ import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.staff.data.StaffData;
 import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
 import org.apache.fineract.portfolio.account.data.AccountTransferData;
+import 
org.apache.fineract.portfolio.accountdetails.data.LoanAccountSummaryData;
 import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
+import 
org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
 import 
org.apache.fineract.portfolio.accountdetails.service.AccountEnumerations;
 import org.apache.fineract.portfolio.calendar.data.CalendarData;
 import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
@@ -153,6 +155,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
     private final FloatingRatesReadPlatformService 
floatingRatesReadPlatformService;
     private final LoanUtilService loanUtilService;
     private final ConfigurationDomainService configurationDomainService;
+    private final AccountDetailsReadPlatformService 
accountDetailsReadPlatformService;
 
     @Autowired
     public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, 
final LoanRepository loanRepository,
@@ -166,7 +169,8 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
             final PaymentTypeReadPlatformService 
paymentTypeReadPlatformService,
             final LoanRepaymentScheduleTransactionProcessorFactory 
loanRepaymentScheduleTransactionProcessorFactory,
             final FloatingRatesReadPlatformService 
floatingRatesReadPlatformService, final LoanUtilService loanUtilService,
-            final ConfigurationDomainService configurationDomainService) {
+            final ConfigurationDomainService configurationDomainService,
+            final AccountDetailsReadPlatformService 
accountDetailsReadPlatformService) {
         this.context = context;
         this.loanRepository = loanRepository;
         this.loanTransactionRepository = loanTransactionRepository;
@@ -187,6 +191,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
         this.floatingRatesReadPlatformService = 
floatingRatesReadPlatformService;
         this.loanUtilService = loanUtilService;
         this.configurationDomainService = configurationDomainService;
+        this.accountDetailsReadPlatformService = 
accountDetailsReadPlatformService;
     }
 
     @Override
@@ -618,7 +623,12 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                     + " l.is_floating_interest_rate as isFloatingInterestRate, 
"
                     + " l.interest_rate_differential as 
interestRateDifferential, "
                     + " l.create_standing_instruction_at_disbursement as 
createStandingInstructionAtDisbursement, "
-                    + " lpvi.minimum_gap as minimuminstallmentgap, 
lpvi.maximum_gap as maximuminstallmentgap "
+                    + " lpvi.minimum_gap as minimuminstallmentgap, 
lpvi.maximum_gap as maximuminstallmentgap, "
+                    + " lp.can_use_for_topup as canUseForTopup, "
+                    + " l.is_topup as isTopup, "
+                    + " topup.closure_loan_id as closureLoanId, "
+                    + " topuploan.account_no as closureLoanAccountNo, "
+                    + " topup.topup_amount as topupAmount "
                     + " from m_loan l" //
                     + " join m_product_loan lp on lp.id = l.product_id" //
                     + " left join m_loan_recalculation_details lir on 
lir.loan_id = l.id "
@@ -637,7 +647,9 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                     + " left join m_code_value cv on cv.id = 
l.loanpurpose_cv_id"
                     + " left join m_code_value codev on codev.id = 
l.writeoff_reason_cv_id"
                     + " left join ref_loan_transaction_processing_strategy lps 
on lps.id = l.loan_transaction_strategy_id"
-                    + " left join m_product_loan_variable_installment_config 
lpvi on lpvi.loan_product_id = l.product_id";
+                    + " left join m_product_loan_variable_installment_config 
lpvi on lpvi.loan_product_id = l.product_id"
+                    + " left join m_loan_topup as topup on l.id = 
topup.loan_id"
+                    + " left join m_loan as topuploan on topuploan.id = 
topup.closure_loan_id";
 
         }
 
@@ -931,6 +943,12 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                         isCompoundingToBePostedAsTransaction, 
allowCompoundingOnEod);
             }
 
+            final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
+            final boolean isTopup = rs.getBoolean("isTopup");
+            final Long closureLoanId = rs.getLong("closureLoanId");
+            final String closureLoanAccountNo = 
rs.getString("closureLoanAccountNo");
+            final BigDecimal topupAmount = rs.getBigDecimal("topupAmount");
+
             return LoanAccountData.basicLoanDetails(id, accountNo, status, 
externalId, clientId, clientAccountNo, clientName,
                     clientOfficeId, groupData, loanType, loanProductId, 
loanProductName, loanProductDescription,
                     isLoanProductLinkedToFloatingRate, fundId, fundName, 
loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -944,7 +962,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                     loanProductCounter, multiDisburseLoan, 
canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears,
                     graceOnArrearsAgeing, isNPA, daysInMonthType, 
daysInYearType, isInterestRecalculationEnabled,
                     interestRecalculationData, 
createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, 
minimumGap,
-                    maximumGap, loanSubStatus);
+                    maximumGap, loanSubStatus, canUseForTopup, isTopup, 
closureLoanId, closureLoanAccountNo, topupAmount);
         }
     }
 
@@ -1049,9 +1067,9 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                 if (!this.disbursement.isDisbursed()) {
                     excludePastUndisbursed = false;
                 }
-                for(DisbursementData disbursementData : disbursementData){
-                    if(disbursementData.getChargeAmount() != null){
-                        disbursementChargeAmount = 
disbursementChargeAmount.subtract(disbursementData.getChargeAmount());
+                for(DisbursementData data : disbursementData){
+                    if(data.getChargeAmount() != null){
+                        disbursementChargeAmount = 
disbursementChargeAmount.subtract(data.getChargeAmount());
                     }
                 }
                 this.outstandingLoanPrincipalBalance = BigDecimal.ZERO;
@@ -1425,10 +1443,15 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
             }
         }
 
+        Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
+        if(loanProduct.canUseForTopup() && clientId != null){
+            clientActiveLoanOptions = 
this.accountDetailsReadPlatformService.retrieveClientActiveLoanAccountSummary(clientId);
+        }
+
         return LoanAccountData.loanProductWithTemplateDefaults(loanProduct, 
loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
                 repaymentFrequencyNthDayTypeOptions, 
repaymentFrequencyDaysOfWeekTypeOptions, repaymentStrategyOptions,
                 interestRateFrequencyTypeOptions, amortizationTypeOptions, 
interestTypeOptions, interestCalculationPeriodTypeOptions,
-                fundOptions, chargeOptions, loanPurposeOptions, 
loanCollateralOptions, loanCycleCounter);
+                fundOptions, chargeOptions, loanPurposeOptions, 
loanCollateralOptions, loanCycleCounter, clientActiveLoanOptions);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index e3f6ccb..b879406 100755
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -41,6 +41,7 @@ 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.GeneralPlatformDomainRuleException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
@@ -142,16 +143,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementC
 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.exception.DateMismatchException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanDisbursalException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursementException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
-import 
org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
+import org.apache.fineract.portfolio.loanaccount.exception.*;
 import 
org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
@@ -362,16 +354,37 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
         ChangedTransactionDetail changedTransactionDetail = null;
         if (canDisburse) {
             Money disburseAmount = loan.adjustDisburseAmount(command, 
actualDisbursementDate);
+            Money amountToDisburse = disburseAmount.copy();
             boolean recalculateSchedule = 
amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
             final String txnExternalId = 
command.stringValueOfParameterNamedAllowingNull("externalId");
+
+            if(loan.isTopup() && loan.getClientId() != null){
+                final Long loanIdToClose = 
loan.getTopupLoanDetails().getLoanIdToClose();
+                final Loan loanToClose = 
this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, 
loan.getClientId());
+                if(loanToClose == null){
+                    throw new LoanNotFoundException(loanIdToClose);
+                }
+                BigDecimal loanOutstanding = 
this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
+                            actualDisbursementDate).getAmount();
+                final BigDecimal firstDisbursalAmount = 
loan.getFirstDisbursalAmount();
+                if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
+                    throw new 
GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
+                            "Topup loan amount should be greater than 
outstanding amount of loan to be closed.");
+                }
+
+                amountToDisburse = disburseAmount.minus(loanOutstanding);
+
+                disburseLoanToLoan(loan, command, loanOutstanding);
+            }
+
             if (isAccountTransfer) {
-                disburseLoanToSavings(loan, command, disburseAmount, 
paymentDetail);
+                disburseLoanToSavings(loan, command, amountToDisburse, 
paymentDetail);
                 
existingTransactionIds.addAll(loan.findExistingTransactionIds());
                 
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
             } else {
                 
existingTransactionIds.addAll(loan.findExistingTransactionIds());
                 
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
-                LoanTransaction disbursementTransaction = 
LoanTransaction.disbursement(loan.getOffice(), disburseAmount, paymentDetail,
+                LoanTransaction disbursementTransaction = 
LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
                         actualDisbursementDate, txnExternalId, 
DateUtils.getLocalDateTimeOfTenant(), currentUser);
                 disbursementTransaction.updateLoan(loan);
                 loan.addLoanTransaction(disbursementTransaction);
@@ -1685,6 +1698,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
                 .withSavingsId(portfolioAccountData.accountId()).build();
     }
 
+    public void disburseLoanToLoan(final Loan loan, final JsonCommand command, 
final BigDecimal amount) {
+
+        final LocalDate transactionDate = 
command.localDateValueOfParameterNamed("actualDisbursementDate");
+        final String txnExternalId = 
command.stringValueOfParameterNamedAllowingNull("externalId");
+
+        final Locale locale = command.extractLocale();
+        final DateTimeFormatter fmt = 
DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
+        final AccountTransferDTO accountTransferDTO = new 
AccountTransferDTO(transactionDate, amount,
+                PortfolioAccountType.LOAN, PortfolioAccountType.LOAN, 
loan.getId(), loan.getTopupLoanDetails().getLoanIdToClose(),
+                "Loan Topup", locale, fmt, 
LoanTransactionType.DISBURSEMENT.getValue(), 
LoanTransactionType.REPAYMENT.getValue(),
+                txnExternalId, loan, null);
+        AccountTransferDetails accountTransferDetails = 
this.accountTransfersWritePlatformService.repayLoanWithTopup(accountTransferDTO);
+        
loan.getTopupLoanDetails().setAccountTransferDetails(accountTransferDetails.getId());
+        loan.getTopupLoanDetails().setTopupAmount(amount);
+    }
+
     public void disburseLoanToSavings(final Loan loan, final JsonCommand 
command, final Money amount, final PaymentDetail paymentDetail) {
 
         final LocalDate transactionDate = 
command.localDateValueOfParameterNamed("actualDisbursementDate");

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
index f704925..5022d01 100755
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/LoanProductConstants.java
@@ -129,6 +129,8 @@ public interface LoanProductConstants {
     
     
     public static final String allowPartialPeriodInterestCalcualtionParamName 
= "allowPartialPeriodInterestCalcualtion";
+
+    public static final String canUseForTopup = "canUseForTopup";
     
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
index a269d54..80f1c3e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -91,7 +91,7 @@ public class LoanProductsApiResource {
             "interestCalculationPeriodTypeOptions", 
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
             "accountingRuleOptions", "accountingMappingOptions", 
"floatingRateOptions", "isLinkedToFloatingInterestRates",
             "floatingRatesId", "interestRateDifferential", 
"minDifferentialLendingRate", "defaultDifferentialLendingRate",
-            "maxDifferentialLendingRate", 
"isFloatingInterestRateCalculationAllowed"));
+            "maxDifferentialLendingRate", 
"isFloatingInterestRateCalculationAllowed", 
LoanProductConstants.canUseForTopup));
 
     private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new 
HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
             "allowedProducts", "productOptions"));

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index 68d1724..bf05d4e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -126,6 +126,7 @@ public class LoanProductData {
     private final Collection<LoanProductBorrowerCycleVariationData> 
numberOfRepaymentVariationsForBorrowerCycle;
     // accounting
     private final EnumOptionData accountingRule;
+    private final boolean canUseForTopup;
     private Map<String, Object> accountingMappings;
     private Collection<PaymentTypeToGLAccountMapper> 
paymentChannelToFundSourceMappings;
     private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
@@ -257,6 +258,7 @@ public class LoanProductData {
         final Integer installmentAmountInMultiplesOf = null;
         final LoanProductConfigurableAttributes 
loanProductConfigurableAttributes = null;
         final boolean syncExpectedWithDisbursementDate = false;
+        final boolean canUseForTopup = false;
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -272,7 +274,7 @@ public class LoanProductData {
                 loanProductConfigurableAttributes, 
isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
                 interestRateDifferential, minDifferentialLendingRate, 
defaultDifferentialLendingRate, maxDifferentialLendingRate,
                 isFloatingInterestRateCalculationAllowed, 
isVariableInstallmentsAllowed, minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate);
+                syncExpectedWithDisbursementDate, canUseForTopup);
 
     }
 
@@ -348,6 +350,7 @@ public class LoanProductData {
         final Integer installmentAmountInMultiplesOf = null;
         final LoanProductConfigurableAttributes 
loanProductConfigurableAttributes = null;
         final boolean syncExpectedWithDisbursementDate = false;
+        final boolean canUseForTopup = false;
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -363,7 +366,7 @@ public class LoanProductData {
                 loanProductConfigurableAttributes, 
isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
                 interestRateDifferential, minDifferentialLendingRate, 
defaultDifferentialLendingRate, maxDifferentialLendingRate,
                 isFloatingInterestRateCalculationAllowed, 
isVariableInstallmentsAllowed, minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate);
+                syncExpectedWithDisbursementDate, canUseForTopup);
 
     }
 
@@ -446,6 +449,7 @@ public class LoanProductData {
         final Integer installmentAmountInMultiplesOf = null;
         final LoanProductConfigurableAttributes 
loanProductConfigurableAttributes = null;
         final boolean syncExpectedWithDisbursementDate = false;
+        final boolean canUseForTopup = false;
 
         return new LoanProductData(id, name, shortName, description, currency, 
principal, minPrincipal, maxPrincipal, tolerance,
                 numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -461,9 +465,8 @@ public class LoanProductData {
                 installmentAmountInMultiplesOf, 
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, 
floatingRateId,
                 floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
                 maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, 
minimumGap, maximumGap,
-                syncExpectedWithDisbursementDate);
+                syncExpectedWithDisbursementDate, canUseForTopup);
 
-        
     }
 
     public static LoanProductData withAccountingDetails(final LoanProductData 
productData, final Map<String, Object> accountingMappings,
@@ -486,14 +489,13 @@ public class LoanProductData {
             final EnumOptionData interestCalculationPeriodType, final Boolean 
allowPartialPeriodInterestCalcualtion, final Long fundId,
             final String fundName, final Long transactionProcessingStrategyId, 
final String transactionProcessingStrategyName,
             final Integer graceOnPrincipalPayment, final Integer 
recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, 
final Integer graceOnInterestCharged,
-            final Collection<ChargeData> charges, final EnumOptionData 
accountingType, final boolean includeInBorrowerCycle,
-            boolean useBorrowerCycle, final LocalDate startDate, final 
LocalDate closeDate, final String status, final String externalId,
+            final Collection<ChargeData> charges, final EnumOptionData 
accountingType, final boolean includeInBorrowerCycle, boolean useBorrowerCycle, 
final LocalDate startDate,
+            final LocalDate closeDate, final String status, final String 
externalId,
             Collection<LoanProductBorrowerCycleVariationData> 
principalVariations,
             Collection<LoanProductBorrowerCycleVariationData> 
interestRateVariations,
             Collection<LoanProductBorrowerCycleVariationData> 
numberOfRepaymentVariations, Boolean multiDisburseLoan,
-            Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final 
Integer graceOnArrearsAgeing,
-            final Integer overdueDaysForNPA, final EnumOptionData 
daysInMonthType, final EnumOptionData daysInYearType,
-            final boolean isInterestRecalculationEnabled, final 
LoanProductInterestRecalculationData interestRecalculationData,
+            Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final 
Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA,
+            final EnumOptionData daysInMonthType, final EnumOptionData 
daysInYearType, final boolean isInterestRecalculationEnabled, final 
LoanProductInterestRecalculationData interestRecalculationData,
             final Integer minimumDaysBetweenDisbursalAndFirstRepayment, 
boolean holdGuaranteeFunds,
             final LoanProductGuaranteeData loanProductGuaranteeData, final 
BigDecimal principalThresholdForLastInstallment,
             final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean 
canDefineInstallmentAmount,
@@ -502,7 +504,7 @@ public class LoanProductData {
             BigDecimal minDifferentialLendingRate, BigDecimal 
defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
             boolean isFloatingInterestRateCalculationAllowed, final boolean 
isVariableInstallmentsAllowed,
             final Integer minimumGapBetweenInstallments, final Integer 
maximumGapBetweenInstallments, 
-            final boolean syncExpectedWithDisbursementDate) {
+            final boolean syncExpectedWithDisbursementDate, final boolean 
canUseForTopup) {
         this.id = id;
         this.name = name;
         this.shortName = shortName;
@@ -605,6 +607,7 @@ public class LoanProductData {
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.preClosureInterestCalculationStrategyOptions = null;
         this.syncExpectedWithDisbursementDate = 
syncExpectedWithDisbursementDate;
+        this.canUseForTopup = canUseForTopup;
 
     }
 
@@ -740,6 +743,7 @@ public class LoanProductData {
         this.installmentAmountInMultiplesOf = 
productData.installmentAmountInMultiplesOf;
         this.preClosureInterestCalculationStrategyOptions = 
preCloseInterestCalculationStrategyOptions;
         this.syncExpectedWithDisbursementDate = 
productData.syncExpectedWithDisbursementDate;
+        this.canUseForTopup = productData.canUseForTopup;
     }
 
     private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> 
charges) {
@@ -1093,4 +1097,7 @@ public class LoanProductData {
                return syncExpectedWithDisbursementDate;
        }
     
+        public boolean canUseForTopup() {
+            return this.canUseForTopup;
+        }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
index 2cdb0d1..f461b17 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/LoanProduct.java
@@ -179,6 +179,10 @@ public class LoanProduct extends AbstractPersistable<Long> 
{
     @Column(name = "sync_expected_with_disbursement_date")
     private boolean syncExpectedWithDisbursementDate;
 
+
+    @Column(name = "can_use_for_topup", nullable = false)
+    private boolean canUseForTopup = false;
+
     public static LoanProduct assembleFromJson(final Fund fund, final 
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
             final List<Charge> productCharges, final JsonCommand command, 
final AprCalculator aprCalculator, FloatingRate floatingRate) {
 
@@ -323,6 +327,11 @@ public class LoanProduct extends AbstractPersistable<Long> 
{
 
         final boolean syncExpectedWithDisbursementDate = 
command.booleanPrimitiveValueOfParameterNamed("syncExpectedWithDisbursementDate");
         
+        
+               final boolean canUseForTopup = 
command.parameterExists(LoanProductConstants.canUseForTopup)
+                               ? 
command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canUseForTopup)
+                               : false;
+
         return new LoanProduct(fund, loanTransactionProcessingStrategy, name, 
shortName, description, currency, principal, minPrincipal,
                 maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, 
maxInterestRatePerPeriod, interestFrequencyType,
                 annualInterestRate, interestMethod, 
interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, 
repaymentEvery,
@@ -336,7 +345,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
                 installmentAmountInMultiplesOf, loanConfigurableAttributes, 
isLinkedToFloatingInterestRates, floatingRate,
                 interestRateDifferential, minDifferentialLendingRate, 
maxDifferentialLendingRate, defaultDifferentialLendingRate,
                 isFloatingInterestRateCalculationAllowed, 
isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
-                maximumGapBetweenInstallments, 
syncExpectedWithDisbursementDate);
+                maximumGapBetweenInstallments, 
syncExpectedWithDisbursementDate, canUseForTopup);
 
     }
 
@@ -552,12 +561,10 @@ public class LoanProduct extends 
AbstractPersistable<Long> {
             final Integer repayEvery, final PeriodFrequencyType 
repaymentFrequencyType, final Integer defaultNumberOfInstallments,
             final Integer defaultMinNumberOfInstallments, final Integer 
defaultMaxNumberOfInstallments,
             final Integer graceOnPrincipalPayment, final Integer 
recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, 
final Integer graceOnInterestCharged,
-            final AmortizationMethod amortizationMethod, final BigDecimal 
inArrearsTolerance, final List<Charge> charges,
-            final AccountingRuleType accountingRuleType, final boolean 
includeInBorrowerCycle, final LocalDate startDate,
-            final LocalDate closeDate, final String externalId, final boolean 
useBorrowerCycle,
-            final Set<LoanProductBorrowerCycleVariations> 
loanProductBorrowerCycleVariations, final boolean multiDisburseLoan,
-            final Integer maxTrancheCount, final BigDecimal 
outstandingLoanBalance, final Integer graceOnArrearsAgeing,
-            final Integer overdueDaysForNPA, final DaysInMonthType 
daysInMonthType, final DaysInYearType daysInYearType,
+            final AmortizationMethod amortizationMethod, final BigDecimal 
inArrearsTolerance, final List<Charge> charges, final AccountingRuleType 
accountingRuleType,
+            final boolean includeInBorrowerCycle, final LocalDate startDate, 
final LocalDate closeDate, final String externalId, final boolean 
useBorrowerCycle,
+            final Set<LoanProductBorrowerCycleVariations> 
loanProductBorrowerCycleVariations, final boolean multiDisburseLoan, final 
Integer maxTrancheCount, final BigDecimal outstandingLoanBalance,
+            final Integer graceOnArrearsAgeing, final Integer 
overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType 
daysInYearType,
             final boolean isInterestRecalculationEnabled,
             final LoanProductInterestRecalculationDetails 
productInterestRecalculationDetails,
             final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final 
boolean holdGuarantorFunds,
@@ -568,7 +575,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
             BigDecimal minDifferentialLendingRate, BigDecimal 
maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
             Boolean isFloatingInterestRateCalculationAllowed, final Boolean 
isVariableInstallmentsAllowed,
             final Integer minimumGapBetweenInstallments, final Integer 
maximumGapBetweenInstallments,
-            final boolean syncExpectedWithDisbursementDate) {
+            final boolean syncExpectedWithDisbursementDate, final boolean 
canUseForTopup) {
         this.fund = fund;
         this.transactionProcessingStrategy = transactionProcessingStrategy;
         this.name = name.trim();
@@ -644,6 +651,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.syncExpectedWithDisbursementDate = 
                        syncExpectedWithDisbursementDate;
+        this.canUseForTopup = canUseForTopup;
     }
 
     public MonetaryCurrency getCurrency() {
@@ -1023,6 +1031,12 @@ public class LoanProduct extends 
AbstractPersistable<Long> {
             this.installmentAmountInMultiplesOf = newValue;
         }
 
+        if 
(command.isChangeInBooleanParameterNamed(LoanProductConstants.canUseForTopup, 
this.canUseForTopup)) {
+            final boolean newValue = 
command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canUseForTopup);
+            actualChanges.put(LoanProductConstants.canUseForTopup, newValue);
+            this.canUseForTopup = newValue;
+        }
+
         return actualChanges;
     }
 
@@ -1347,4 +1361,8 @@ public class LoanProduct extends 
AbstractPersistable<Long> {
         return this.allowVariabeInstallments;
     }
 
+    public boolean canUseForTopup(){
+        return this.canUseForTopup;
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index f9c673b..e0a3f2e 100755
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -107,7 +107,8 @@ public final class LoanProductDataValidator {
             
LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName,
             LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
             LoanProductConstants.recalculationRestFrequencyNthDayParamName, 
LoanProductConstants.recalculationRestFrequencyOnDayParamName,
-            
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, 
LoanProductConstants.allowCompoundingOnEodParamName));
+            
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, 
LoanProductConstants.allowCompoundingOnEodParamName,
+            LoanProductConstants.canUseForTopup));
 
     private final FromJsonHelper fromApiJsonHelper;
 
@@ -621,6 +622,12 @@ public final class LoanProductDataValidator {
 
         validatePartialPeriodSupport(interestCalculationPeriodType, 
baseDataValidator, element, null);
 
+        
if(this.fromApiJsonHelper.parameterExists(LoanProductConstants.canUseForTopup, 
element)){
+            final Boolean canUseForTopup = 
this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.canUseForTopup,
+                    element);
+            
baseDataValidator.reset().parameter(LoanProductConstants.canUseForTopup).value(canUseForTopup).validateForBooleanValue();
+        }
+
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 
@@ -1440,6 +1447,12 @@ public final class LoanProductDataValidator {
 
         validatePartialPeriodSupport(interestCalculationPeriodType, 
baseDataValidator, element, loanProduct);
 
+        
if(this.fromApiJsonHelper.parameterExists(LoanProductConstants.canUseForTopup, 
element)){
+            final Boolean canUseForTopup = 
this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.canUseForTopup,
+                    element);
+            
baseDataValidator.reset().parameter(LoanProductConstants.canUseForTopup).value(canUseForTopup).validateForBooleanValue();
+        }
+
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
index e3a511d..00a3c59 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java
@@ -224,7 +224,8 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
                     + "lfr.is_floating_interest_rate_calculation_allowed as 
isFloatingInterestRateCalculationAllowed, "
                     + "lp.allow_variabe_installments as 
isVariableIntallmentsAllowed, "
                     + "lvi.minimum_gap as minimumGap, "
-                    + "lvi.maximum_gap as maximumGap "
+                    + "lvi.maximum_gap as maximumGap, "
+                    + "lp.can_use_for_topup as canUseForTopup "
                     + " from m_product_loan lp "
                     + " left join m_fund f on f.id = lp.fund_id "
                     + " left join m_product_loan_recalculation_details lpr on 
lpr.product_id=lp.id "
@@ -444,6 +445,8 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
             final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = 
rs.getBoolean("accountMovesOutOfNPAOnlyOnArrearsCompletion");
             final boolean syncExpectedWithDisbursementDate = 
rs.getBoolean("syncExpectedWithDisbursementDate");
             
+            final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
+
             return new LoanProductData(id, name, shortName, description, 
currency, principal, minPrincipal, maxPrincipal, tolerance,
                     numberOfRepayments, minNumberOfRepayments, 
maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
                     minInterestRatePerPeriod, maxInterestRatePerPeriod, 
annualInterestRate, repaymentFrequencyType,
@@ -459,7 +462,7 @@ public class LoanProductReadPlatformServiceImpl implements 
LoanProductReadPlatfo
                     installmentAmountInMultiplesOf, allowAttributeOverrides, 
isLinkedToFloatingInterestRates, floatingRateId,
                     floatingRateName, interestRateDifferential, 
minDifferentialLendingRate, defaultDifferentialLendingRate,
                     maxDifferentialLendingRate, 
isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, 
minimumGap,
-                    maximumGap, syncExpectedWithDisbursementDate);
+                    maximumGap, syncExpectedWithDisbursementDate, 
canUseForTopup);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/961aa3df/fineract-provider/src/main/resources/sql/migrations/core_db/V318__topuploan.sql
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/resources/sql/migrations/core_db/V318__topuploan.sql
 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V318__topuploan.sql
new file mode 100644
index 0000000..d89d771
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V318__topuploan.sql
@@ -0,0 +1,40 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+ALTER TABLE `m_product_loan`
+       ADD COLUMN `can_use_for_topup` TINYINT(1) NOT NULL DEFAULT 0 AFTER 
`instalment_amount_in_multiples_of`;
+
+ALTER TABLE `m_loan`
+       ADD COLUMN `is_topup` TINYINT(1) NOT NULL DEFAULT 0 AFTER 
`loan_sub_status_id`;
+
+CREATE TABLE `m_loan_topup` (
+       `id` BIGINT NOT NULL AUTO_INCREMENT,
+       `loan_id` BIGINT NOT NULL,
+       `closure_loan_id` BIGINT NOT NULL,
+       `account_transfer_details_id` BIGINT NULL,
+       `topup_amount` DECIMAL(19,6) NULL DEFAULT NULL,
+       PRIMARY KEY (`id`),
+       CONSTRAINT `m_loan_topup_FK_loan_id` FOREIGN KEY (`loan_id`) REFERENCES 
`m_loan` (`id`),
+       CONSTRAINT `m_loan_topup_FK_closure_loan_id` FOREIGN KEY 
(`closure_loan_id`) REFERENCES `m_loan` (`id`),
+       CONSTRAINT `m_loan_topup_FK_account_transfer_details_id` FOREIGN KEY 
(`account_transfer_details_id`) REFERENCES `m_account_transfer_details` (`id`)
+)
+COLLATE='utf8_general_ci'
+ENGINE=InnoDB
+;
+

Reply via email to