[FINERACT-131] Topup Loan Feature

This commit is contained in:
satish-conflux
2016-08-16 14:32:27 +05:30
committed by Adi Narayana Raju
parent 11cd6ef333
commit 961aa3df86
33 changed files with 740 additions and 174 deletions
@@ -51,6 +51,8 @@ public class LoanTransactionDTO {
private final boolean isAccountTransfer; private final boolean isAccountTransfer;
private boolean isLoanToLoanTransfer;
public LoanTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate, public LoanTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest, final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest,
final BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final boolean reversed, final BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final boolean reversed,
@@ -132,4 +134,11 @@ public class LoanTransactionDTO {
return this.isAccountTransfer; return this.isAccountTransfer;
} }
public void setIsLoanToLoanTransfer(boolean isLoanToLoanTransfer) {
this.isLoanToLoanTransfer = isLoanToLoanTransfer;
}
public boolean isLoanToLoanTransfer(){
return this.isLoanToLoanTransfer;
}
} }
@@ -170,7 +170,12 @@ public class AccountingProcessorHelper {
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId, final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed, transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed,
feePaymentDetails, penaltyPaymentDetails, isAccountTransfer); feePaymentDetails, penaltyPaymentDetails, isAccountTransfer);
Boolean isLoanToLoanTransfer = (Boolean) accountingBridgeData.get("isLoanToLoanTransfer");
if(isLoanToLoanTransfer != null && isLoanToLoanTransfer){
transaction.setIsLoanToLoanTransfer(true);
} else {
transaction.setIsLoanToLoanTransfer(false);
}
newLoanTransactions.add(transaction); newLoanTransactions.add(transaction);
} }
@@ -124,7 +124,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// create journal entries for the disbursement (or disbursement // create journal entries for the disbursement (or disbursement
// reversal) // reversal)
if (loanTransactionDTO.isAccountTransfer()) { if(loanTransactionDTO.isLoanToLoanTransfer()){
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversed);
} else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversed); paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversed);
@@ -296,7 +300,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal); transactionDate, totalDebitAmount, isReversal);
} else { } else {
if (loanTransactionDTO.isAccountTransfer()) { if(loanTransactionDTO.isLoanToLoanTransfer()){
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal);
} else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId, FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal); transactionDate, totalDebitAmount, isReversal);
@@ -132,7 +132,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount(); final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
final boolean isReversal = loanTransactionDTO.isReversed(); final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
if (loanTransactionDTO.isAccountTransfer()) { if(loanTransactionDTO.isLoanToLoanTransfer()){
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversal);
} else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversal); paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount, isReversal);
@@ -240,7 +244,10 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
} }
/*** create a single debit entry (or reversal) for the entire amount **/ /*** create a single debit entry (or reversal) for the entire amount **/
if (loanTransactionDTO.isAccountTransfer()) { if(loanTransactionDTO.isLoanToLoanTransfer()){
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
} else if (loanTransactionDTO.isAccountTransfer()) {
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(),
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
} else { } else {
@@ -50,6 +50,8 @@ public class AccountTransferDTO {
private final String noteText; private final String noteText;
private final String txnExternalId; private final String txnExternalId;
private final Loan loan; private final Loan loan;
private final Loan fromLoan;
private final Loan toLoan;
private final SavingsAccount toSavingsAccount; private final SavingsAccount toSavingsAccount;
private final SavingsAccount fromSavingsAccount; private final SavingsAccount fromSavingsAccount;
private final Boolean isRegularTransaction; private final Boolean isRegularTransaction;
@@ -81,12 +83,46 @@ public class AccountTransferDTO {
this.noteText = noteText; this.noteText = noteText;
this.txnExternalId = txnExternalId; this.txnExternalId = txnExternalId;
this.loan = loan; this.loan = loan;
this.fromLoan = null;
this.toLoan = null;
this.toSavingsAccount = toSavingsAccount; this.toSavingsAccount = toSavingsAccount;
this.fromSavingsAccount = fromSavingsAccount; this.fromSavingsAccount = fromSavingsAccount;
this.isRegularTransaction = isRegularTransaction; this.isRegularTransaction = isRegularTransaction;
this.isExceptionForBalanceCheck = isExceptionForBalanceCheck; this.isExceptionForBalanceCheck = isExceptionForBalanceCheck;
} }
public AccountTransferDTO(final LocalDate transactionDate, final BigDecimal transactionAmount,
final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType, final Long fromAccountId,
final Long toAccountId, final String description, final Locale locale, final DateTimeFormatter fmt,
final Integer fromTransferType, final Integer toTransferType, final String txnExternalId,
final Loan fromLoan, final Loan toLoan) {
this.transactionDate = transactionDate;
this.transactionAmount = transactionAmount;
this.fromAccountType = fromAccountType;
this.toAccountType = toAccountType;
this.fromAccountId = fromAccountId;
this.toAccountId = toAccountId;
this.description = description;
this.locale = locale;
this.fmt = fmt;
this.paymentDetail = null;
this.fromTransferType = fromTransferType;
this.toTransferType = toTransferType;
this.chargeId = null;
this.loanInstallmentNumber = null;
this.transferType = null;
this.accountTransferDetails = null;
this.noteText = null;
this.txnExternalId = txnExternalId;
this.fromLoan = fromLoan;
this.toLoan = toLoan;
this.loan = null;
this.toSavingsAccount = null;
this.fromSavingsAccount = null;
this.isRegularTransaction = null;
this.isExceptionForBalanceCheck = null;
}
public LocalDate getTransactionDate() { public LocalDate getTransactionDate() {
return this.transactionDate; return this.transactionDate;
} }
@@ -163,6 +199,14 @@ public class AccountTransferDTO {
return this.loan; return this.loan;
} }
public Loan getFromLoan() {
return this.fromLoan;
}
public Loan getToLoan() {
return this.toLoan;
}
public SavingsAccount getToSavingsAccount() { public SavingsAccount getToSavingsAccount() {
return this.toSavingsAccount; return this.toSavingsAccount;
} }
@@ -145,4 +145,19 @@ public class AccountTransferAssembler {
return accountTransferDetails; return accountTransferDetails;
} }
public AccountTransferDetails assembleLoanToLoanTransfer(final AccountTransferDTO accountTransferDTO, final Loan fromLoanAccount,
final Loan toLoanAccount, final LoanTransaction disburseTransaction, final LoanTransaction repaymentTransaction) {
final Money transactionMonetaryAmount = Money.of(fromLoanAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
AccountTransferDetails accountTransferDetails = accountTransferDTO.getAccountTransferDetails();
if (accountTransferDetails == null) {
accountTransferDetails = this.accountTransferDetailAssembler.assembleLoanToLoanTransfer(fromLoanAccount, toLoanAccount,
accountTransferDTO.getFromTransferType());
}
AccountTransferTransaction accountTransferTransaction = AccountTransferTransaction.LoanToLoanTransfer(accountTransferDetails,
disburseTransaction, repaymentTransaction, accountTransferDTO.getTransactionDate(), transactionMonetaryAmount,
accountTransferDTO.getDescription());
accountTransferDetails.addAccountTransferTransaction(accountTransferTransaction);
return accountTransferDetails;
}
} }
@@ -201,4 +201,14 @@ public class AccountTransferDetailAssembler {
return AccountTransferDetails.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount, return AccountTransferDetails.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
transferType); transferType);
} }
public AccountTransferDetails assembleLoanToLoanTransfer(Loan fromLoanAccount, Loan toLoanAccount, Integer transferType) {
final Office fromOffice = fromLoanAccount.getOffice();
final Client fromClient = fromLoanAccount.client();
final Office toOffice = toLoanAccount.getOffice();
final Client toClient = toLoanAccount.client();
return AccountTransferDetails.LoanToLoanTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toLoanAccount,
transferType);
}
} }
@@ -156,4 +156,9 @@ public class AccountTransferDetails extends AbstractPersistable<Long> {
return AccountTransferType.fromInt(this.transferType); return AccountTransferType.fromInt(this.transferType);
} }
public static AccountTransferDetails LoanToLoanTransfer(Office fromOffice, Client fromClient, Loan fromLoanAccount, Office toOffice, Client toClient,
Loan toLoanAccount, Integer transferType) {
return new AccountTransferDetails(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, null, toLoanAccount,
transferType, null);
}
} }
@@ -145,4 +145,10 @@ public class AccountTransferTransaction extends AbstractPersistable<Long> {
public AccountTransferDetails accountTransferDetails() { public AccountTransferDetails accountTransferDetails() {
return this.accountTransferDetails; return this.accountTransferDetails;
} }
public static AccountTransferTransaction LoanToLoanTransfer(AccountTransferDetails accountTransferDetails, LoanTransaction disburseTransaction,
LoanTransaction repaymentTransaction, LocalDate transactionDate, Money transactionMonetaryAmount, String description) {
return new AccountTransferTransaction(accountTransferDetails, null, null, repaymentTransaction, disburseTransaction, transactionDate,
transactionMonetaryAmount, description);
}
} }
@@ -24,6 +24,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO; import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
public interface AccountTransfersWritePlatformService { public interface AccountTransfersWritePlatformService {
@@ -41,4 +42,6 @@ public interface AccountTransfersWritePlatformService {
CommandProcessingResult refundByTransfer(JsonCommand command); CommandProcessingResult refundByTransfer(JsonCommand command);
void reverseTransfersWithFromAccountTransactions(Collection<Long> fromTransactionIds, PortfolioAccountType accountTypeId); void reverseTransfersWithFromAccountTransactions(Collection<Long> fromTransactionIds, PortfolioAccountType accountTypeId);
AccountTransferDetails repayLoanWithTopup(AccountTransferDTO accountTransferDTO);
} }
@@ -33,6 +33,7 @@ import java.util.Locale;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO; import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
@@ -253,7 +254,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
for (final AccountTransferTransaction accountTransfer : acccountTransfers) { for (final AccountTransferTransaction accountTransfer : acccountTransfers) {
if (accountTransfer.getFromLoanTransaction() != null) { if (accountTransfer.getFromLoanTransaction() != null) {
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction()); this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
} else if (accountTransfer.getToLoanTransaction() != null) { }
if (accountTransfer.getToLoanTransaction() != null) {
this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction()); this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction());
} }
if (accountTransfer.getFromTransaction() != null) { if (accountTransfer.getFromTransaction() != null) {
@@ -409,11 +411,47 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
toSavingsAccount, deposit, loanTransaction); toSavingsAccount, deposit, loanTransaction);
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails); this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
transferTransactionId = accountTransferDetails.getId(); transferTransactionId = accountTransferDetails.getId();
} else {
throw new GeneralPlatformDomainRuleException("error.msg.accounttransfer.loan.to.loan.not.supported",
"Account transfer from loan to another loan is not supported");
} }
return transferTransactionId; return transferTransactionId;
} }
@Override
public AccountTransferDetails repayLoanWithTopup(AccountTransferDTO accountTransferDTO) {
final boolean isAccountTransfer = true;
Loan fromLoanAccount = null;
if (accountTransferDTO.getFromLoan() == null) {
fromLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getFromAccountId());
} else {
fromLoanAccount = accountTransferDTO.getFromLoan();
this.loanAccountAssembler.setHelpers(fromLoanAccount);
}
Loan toLoanAccount = null;
if (accountTransferDTO.getToLoan() == null) {
toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
} else {
toLoanAccount = accountTransferDTO.getToLoan();
this.loanAccountAssembler.setHelpers(toLoanAccount);
}
LoanTransaction disburseTransaction = this.loanAccountDomainService.makeDisburseTransaction(accountTransferDTO.getFromAccountId(),
accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
accountTransferDTO.getPaymentDetail(), accountTransferDTO.getNoteText(), accountTransferDTO.getTxnExternalId(), true);
LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(),
accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
accountTransferDTO.getPaymentDetail(), null, null, false, isAccountTransfer,null,false, true);
AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToLoanTransfer(accountTransferDTO, fromLoanAccount,
toLoanAccount, disburseTransaction, repayTransaction);
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
return accountTransferDetails;
}
@Override @Override
@Transactional @Transactional
public void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) { public void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
@@ -32,4 +32,6 @@ public interface AccountDetailsReadPlatformService {
public Collection<LoanAccountSummaryData> retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId); public Collection<LoanAccountSummaryData> retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId);
public Collection<LoanAccountSummaryData> retrieveGroupLoanAccountsByLoanOfficerId(final Long groupId, final Long loanOfficerId); public Collection<LoanAccountSummaryData> retrieveGroupLoanAccountsByLoanOfficerId(final Long groupId, final Long loanOfficerId);
public Collection<LoanAccountSummaryData> retrieveClientActiveLoanAccountSummary(final Long clientId);
} }
@@ -111,6 +111,11 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
return retrieveLoanAccountDetails(loanWhereClause, new Object[] { groupId, loanOfficerId }); return retrieveLoanAccountDetails(loanWhereClause, new Object[] { groupId, loanOfficerId });
} }
@Override public Collection<LoanAccountSummaryData> retrieveClientActiveLoanAccountSummary(final Long clientId) {
final String loanWhereClause = " where l.client_id = ? and l.loan_status_id = 300 ";
return retrieveLoanAccountDetails(loanWhereClause, new Object[] { clientId });
}
private List<LoanAccountSummaryData> retrieveLoanAccountDetails(final String loanwhereClause, final Object[] inputs) { private List<LoanAccountSummaryData> retrieveLoanAccountDetails(final String loanwhereClause, final Object[] inputs) {
final LoanAccountSummaryDataMapper rm = new LoanAccountSummaryDataMapper(); final LoanAccountSummaryDataMapper rm = new LoanAccountSummaryDataMapper();
final String sql = "select " + rm.loanAccountSummarySchema() + loanwhereClause; final String sql = "select " + rm.loanAccountSummarySchema() + loanwhereClause;
@@ -117,4 +117,10 @@ public interface LoanApiConstants {
public static final String transactionDateParamName = "transactionDate"; public static final String transactionDateParamName = "transactionDate";
public static final String noteParamName = "note"; public static final String noteParamName = "note";
public static final String canUseForTopup = "canUseForTopup";
public static final String clientActiveLoanOptions = "clientActiveLoanOptions";
public static final String isTopup = "isTopup";
public static final String loanIdToClose = "loanIdToClose";
public static final String topupAmount = "topupAmount";
} }
@@ -66,6 +66,8 @@ import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData; import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService;
import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService; import org.apache.fineract.portfolio.account.service.PortfolioAccountReadPlatformService;
import org.apache.fineract.portfolio.accountdetails.data.LoanAccountSummaryData;
import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
import org.apache.fineract.portfolio.calendar.data.CalendarData; import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService; import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService;
@@ -136,7 +138,8 @@ public class LoansApiResource {
"termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
"loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions",
"syncDisbursementWithMeeting", "loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount", "syncDisbursementWithMeeting", "loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount",
"interestRateDifferential", "isFloatingInterestRate", "interestRatesPeriods")); "interestRateDifferential", "isFloatingInterestRate", "interestRatesPeriods", LoanApiConstants.canUseForTopup,
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, LoanApiConstants.clientActiveLoanOptions));
private final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount")); private final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
private final String resourceNameForPermissions = "LOAN"; private final String resourceNameForPermissions = "LOAN";
@@ -164,6 +167,7 @@ public class LoansApiResource {
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService; private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService; private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
private final AccountDetailsReadPlatformService accountDetailsReadPlatformService;
@Autowired @Autowired
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService, public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
@@ -182,7 +186,8 @@ public class LoansApiResource {
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl, final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService) { final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
final AccountDetailsReadPlatformService accountDetailsReadPlatformService) {
this.context = context; this.context = context;
this.loanReadPlatformService = loanReadPlatformService; this.loanReadPlatformService = loanReadPlatformService;
this.loanProductReadPlatformService = loanProductReadPlatformService; this.loanProductReadPlatformService = loanProductReadPlatformService;
@@ -206,6 +211,7 @@ public class LoansApiResource {
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl; this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl;
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService; this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService; this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
} }
/* /*
@@ -514,6 +520,7 @@ public class LoansApiResource {
Collection<CalendarData> calendarOptions = null; Collection<CalendarData> calendarOptions = null;
Collection<PortfolioAccountData> accountLinkingOptions = null; Collection<PortfolioAccountData> accountLinkingOptions = null;
PaidInAdvanceData paidInAdvanceTemplate = null; PaidInAdvanceData paidInAdvanceTemplate = null;
Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
if (template) { if (template) {
@@ -568,6 +575,11 @@ public class LoansApiResource {
calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId()); calendarOptions = this.loanReadPlatformService.retrieveCalendars(loanBasicDetails.groupId());
} }
if(loanBasicDetails.product().canUseForTopup() && loanBasicDetails.clientId() != null){
clientActiveLoanOptions = this.accountDetailsReadPlatformService.retrieveClientActiveLoanAccountSummary(loanBasicDetails.clientId());
}
} }
Collection<ChargeData> overdueCharges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanBasicDetails.loanProductId(), Collection<ChargeData> overdueCharges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanBasicDetails.loanProductId(),
@@ -581,7 +593,7 @@ public class LoansApiResource {
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, loanCollateralOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, loanCollateralOptions,
calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods); overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(), final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters); mandatoryResponseParameters);
@@ -31,6 +31,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData; import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.account.data.PortfolioAccountData; import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.accountdetails.data.LoanAccountSummaryData;
import org.apache.fineract.portfolio.calendar.data.CalendarData; import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.collateral.data.CollateralData; import org.apache.fineract.portfolio.collateral.data.CollateralData;
@@ -39,7 +40,6 @@ import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
import org.apache.fineract.portfolio.fund.data.FundData; import org.apache.fineract.portfolio.fund.data.FundData;
import org.apache.fineract.portfolio.group.data.GroupGeneralData; import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
import org.apache.fineract.portfolio.loanaccount.guarantor.data.GuarantorData; import org.apache.fineract.portfolio.loanaccount.guarantor.data.GuarantorData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData; import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
@@ -180,6 +180,12 @@ public class LoanAccountData {
private final Boolean canDisburse; private final Boolean canDisburse;
private final Collection<LoanTermVariationsData> emiAmountVariations; private final Collection<LoanTermVariationsData> emiAmountVariations;
private final Collection<LoanAccountSummaryData> clientActiveLoanOptions;
private final Boolean canUseForTopup;
private final boolean isTopup;
private final Long closureLoanId;
private final String closureLoanAccountNo;
private final BigDecimal topupAmount;
private LoanProductData product; private LoanProductData product;
@@ -319,6 +325,12 @@ public class LoanAccountData {
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE; final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
final Integer minimumGap = null; final Integer minimumGap = null;
final Integer maximumGap = null; final Integer maximumGap = null;
final Boolean canUseForTopup = null;
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
final boolean isTopup = false;
final Long closureLoanId = null;
final String closureLoanAccountNo = null;
final BigDecimal topupAmount = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -338,7 +350,7 @@ public class LoanAccountData {
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
maximumGap, subStatus); maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
} }
@@ -453,6 +465,12 @@ public class LoanAccountData {
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE; final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
final Integer minimumGap = null; final Integer minimumGap = null;
final Integer maximumGap = null; final Integer maximumGap = null;
final Boolean canUseForTopup = null;
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
final boolean isTopup = false;
final Long closureLoanId = null;
final String closureLoanAccountNo = null;
final BigDecimal topupAmount = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -472,7 +490,7 @@ public class LoanAccountData {
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
maximumGap, subStatus); maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
} }
@@ -501,7 +519,8 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
/** /**
@@ -617,6 +636,12 @@ public class LoanAccountData {
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE; final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
final Integer minimumGap = null; final Integer minimumGap = null;
final Integer maximumGap = null; final Integer maximumGap = null;
final Boolean canUseForTopup = null;
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
final boolean isTopup = false;
final Long closureLoanId = null;
final String closureLoanAccountNo = null;
final BigDecimal topupAmount = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -636,7 +661,7 @@ public class LoanAccountData {
maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
maximumGap, subStatus); maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
} }
@@ -665,7 +690,8 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
@@ -678,7 +704,7 @@ public class LoanAccountData {
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions, final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions, final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions,
final Collection<CodeValueData> loanPurposeOptions, final Collection<CodeValueData> loanCollateralOptions, final Collection<CodeValueData> loanPurposeOptions, final Collection<CodeValueData> loanCollateralOptions,
final Integer loanCycleNumber) { final Integer loanCycleNumber, final Collection<LoanAccountSummaryData> clientActiveLoanOptions) {
final Long id = null; final Long id = null;
final String accountNo = null; final String accountNo = null;
@@ -790,6 +816,11 @@ public class LoanAccountData {
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE; final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
final Integer minimumGap = null; final Integer minimumGap = null;
final Integer maximumGap = null; final Integer maximumGap = null;
final Boolean canUseForTopup = product.canUseForTopup();
final boolean isTopup = false;
final Long closureLoanId = null;
final String closureLoanAccountNo = null;
final BigDecimal topupAmount = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(), loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
@@ -814,7 +845,8 @@ public class LoanAccountData {
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(), product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
product.getMaximumGapBetweenInstallments(), subStatus); product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
closureLoanAccountNo, topupAmount);
} }
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) { public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -873,7 +905,8 @@ public class LoanAccountData {
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement, product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
paidInAdvance, acc.interestRatesPeriods, product.isVariableInstallmentsAllowed(), paidInAdvance, acc.interestRatesPeriods, product.isVariableInstallmentsAllowed(),
product.getMinimumGapBetweenInstallments(), product.getMaximumGapBetweenInstallments(), acc.subStatus); product.getMinimumGapBetweenInstallments(), product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
@@ -903,7 +936,9 @@ public class LoanAccountData {
final Integer graceOnArrearsAgeing, final Boolean isNPA, final EnumOptionData daysInMonthType, final Integer graceOnArrearsAgeing, final Boolean isNPA, final EnumOptionData daysInMonthType,
final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement, final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement,
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus) { final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus,
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
final BigDecimal topupAmount) {
final LoanScheduleData repaymentSchedule = null; final LoanScheduleData repaymentSchedule = null;
final Collection<LoanTransactionData> transactions = null; final Collection<LoanTransactionData> transactions = null;
@@ -939,6 +974,7 @@ public class LoanAccountData {
final LoanScheduleData originalSchedule = null; final LoanScheduleData originalSchedule = null;
final PaidInAdvanceData paidInAdvance = null; final PaidInAdvanceData paidInAdvance = null;
final Collection<InterestRatePeriodData> interestRatesPeriods = null; final Collection<InterestRatePeriodData> interestRatesPeriods = null;
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -958,7 +994,7 @@ public class LoanAccountData {
outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
maximumGap, subStatus); maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
} }
/* /*
@@ -981,7 +1017,8 @@ public class LoanAccountData {
final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions, final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions,
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails, final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges, final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods) { final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods,
final Collection<LoanAccountSummaryData> clientActiveLoanOptions) {
LoanProductConfigurableAttributes loanProductConfigurableAttributes = null; LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
if (acc.product != null) { if (acc.product != null) {
loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes(); loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
@@ -1009,7 +1046,8 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed, acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus); acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions, public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
@@ -1022,7 +1060,7 @@ public class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions, allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance, acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance,
acc.interestRatesPeriods); acc.interestRatesPeriods, acc.clientActiveLoanOptions);
} }
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) { public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
@@ -1050,7 +1088,8 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) { public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
@@ -1114,7 +1153,8 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
@@ -1147,7 +1187,8 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule, acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) { public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
@@ -1174,7 +1215,8 @@ public class LoanAccountData {
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.originalSchedule, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) { public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
@@ -1202,40 +1244,24 @@ public class LoanAccountData {
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods,
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus); acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup,
acc.clientActiveLoanOptions, acc.isTopup, acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount);
} }
private LoanAccountData( private LoanAccountData(final Long id, //
final Long id, //
final String accountNo, // final String accountNo, //
final LoanStatusEnumData status, // final LoanStatusEnumData status, //
final String externalId, // final String externalId, //
final Long clientId, final Long clientId, final String clientAccountNo, final String clientName, final Long clientOfficeId, //
final String clientAccountNo, final GroupGeneralData group, final EnumOptionData loanType, final Long loanProductId, final String loanProductName,
final String clientName,
final Long clientOfficeId, //
final GroupGeneralData group,
final EnumOptionData loanType,
final Long loanProductId,
final String loanProductName,
final String loanProductDescription, // final String loanProductDescription, //
final boolean isLoanProductLinkedToFloatingRate, final boolean isLoanProductLinkedToFloatingRate, final Long fundId, final String fundName, final Long loanPurposeId,
final Long fundId,
final String fundName,
final Long loanPurposeId,
final String loanPurposeName, // final String loanPurposeName, //
final Long loanOfficerId, final Long loanOfficerId, final String loanOfficerName, //
final String loanOfficerName, // final CurrencyData currency, BigDecimal proposedPrincipal, final BigDecimal principal, final BigDecimal approvedPrincipal,
final CurrencyData currency,
BigDecimal proposedPrincipal,
final BigDecimal principal,
final BigDecimal approvedPrincipal,
final BigDecimal totalOverpaid, // final BigDecimal totalOverpaid, //
final BigDecimal inArrearsTolerance, final BigDecimal inArrearsTolerance, final Integer termFrequency, //
final Integer termFrequency, // final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
final EnumOptionData termPeriodFrequencyType,
final Integer numberOfRepayments,
final Integer repaymentEvery,
final EnumOptionData repaymentFrequencyType, // final EnumOptionData repaymentFrequencyType, //
final EnumOptionData repaymentFrequencyNthDayType, final EnumOptionData repaymentFrequencyDayOfWeekType, final EnumOptionData repaymentFrequencyNthDayType, final EnumOptionData repaymentFrequencyDayOfWeekType,
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
@@ -1244,12 +1270,10 @@ public class LoanAccountData {
final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType, final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType,
final Boolean allowPartialPeriodInterestCalcualtion, final LocalDate expectedFirstRepaymentOnDate, final Boolean allowPartialPeriodInterestCalcualtion, final LocalDate expectedFirstRepaymentOnDate,
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData summary, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData summary, final BigDecimal feeChargesDueAtDisbursementCharged,
final BigDecimal feeChargesDueAtDisbursementCharged, final LoanScheduleData repaymentSchedule, final LoanScheduleData repaymentSchedule, final Collection<LoanTransactionData> transactions, final Collection<LoanChargeData> charges,
final Collection<LoanTransactionData> transactions, final Collection<LoanChargeData> charges, final Collection<CollateralData> collateral, final Collection<GuarantorData> guarantors, final CalendarData meeting, final Collection<LoanProductData> productOptions,
final Collection<CollateralData> collateral, final Collection<GuarantorData> guarantors, final CalendarData meeting, final Collection<EnumOptionData> termFrequencyTypeOptions, final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
final Collection<LoanProductData> productOptions, final Collection<EnumOptionData> termFrequencyTypeOptions,
final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
final Collection<EnumOptionData> repaymentFrequencyNthDayTypeOptions, final Collection<EnumOptionData> repaymentFrequencyNthDayTypeOptions,
final Collection<EnumOptionData> repaymentFrequencyDaysOfWeekTypeOptions, final Collection<EnumOptionData> repaymentFrequencyDaysOfWeekTypeOptions,
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions, final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions,
@@ -1269,7 +1293,9 @@ public class LoanAccountData {
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule, final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance, final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance,
final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed, final Collection<InterestRatePeriodData> interestRatesPeriods, final Boolean isVariableInstallmentsAllowed,
final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus) { final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup,
final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount) {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
@@ -1445,6 +1471,13 @@ public class LoanAccountData {
this.isVariableInstallmentsAllowed = isVariableInstallmentsAllowed; this.isVariableInstallmentsAllowed = isVariableInstallmentsAllowed;
this.minimumGap = minimumGap; this.minimumGap = minimumGap;
this.maximumGap = maximumGap; this.maximumGap = maximumGap;
this.canUseForTopup = canUseForTopup;
this.clientActiveLoanOptions = clientActiveLoanOptions;
this.isTopup = isTopup;
this.closureLoanId = closureLoanId;
this.closureLoanAccountNo = closureLoanAccountNo;
this.topupAmount = topupAmount;
} }
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() { public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -21,37 +21,9 @@ package org.apache.fineract.portfolio.loanaccount.domain;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.persistence.CascadeType; import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@@ -101,33 +73,10 @@ import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException; import org.apache.fineract.portfolio.loanaccount.exception.*;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
import org.apache.fineract.portfolio.loanaccount.exception.InvalidRefundDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanDisbursalException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignmentDateException;
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
import org.apache.fineract.portfolio.loanaccount.exception.UndoLastTrancheDisbursementException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleDTO;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.*;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; import org.apache.fineract.portfolio.loanproduct.domain.*;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
@@ -397,6 +346,14 @@ public class Loan extends AbstractPersistable<Long> {
@Column(name = "loan_sub_status_id", nullable = true) @Column(name = "loan_sub_status_id", nullable = true)
private Integer loanSubStatus; private Integer loanSubStatus;
@Column(name = "is_topup", nullable = false)
private boolean isTopup = false;
// @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.EAGER)
// @JoinColumn(name = "loan_id", referencedColumnName= "id" , nullable = true)
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loan", optional = true, orphanRemoval = true, fetch=FetchType.EAGER)
private LoanTopupDetails loanTopupDetails;
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType, public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
@@ -2769,6 +2726,11 @@ public class Loan extends AbstractPersistable<Long> {
} }
} }
if(this.isTopup){
this.loanTopupDetails.setAccountTransferDetails(null);
this.loanTopupDetails.setTopupAmount(null);
}
actualChanges.put("actualDisbursementDate", ""); actualChanges.put("actualDisbursementDate", "");
updateLoanSummaryDerivedFields(); updateLoanSummaryDerivedFields();
@@ -4765,6 +4727,12 @@ public class Loan extends AbstractPersistable<Long> {
defaultUserMessage); defaultUserMessage);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if(isOpen() && this.isTopup()){
final String defaultUserMessage = "Loan Undo disbursal is not allowed on Topup Loans";
final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.undo.disbursal.not.allowed.on.topup.loan",
defaultUserMessage);
dataValidationErrors.add(error);
}
break; break;
case LOAN_REPAYMENT_OR_WAIVER: case LOAN_REPAYMENT_OR_WAIVER:
if (!isOpen()) { if (!isOpen()) {
@@ -6353,4 +6321,32 @@ public class Loan extends AbstractPersistable<Long> {
return retData.size()>0?retData:null; return retData.size()>0?retData:null;
} }
public void setIsTopup(final boolean isTopup) {
this.isTopup = isTopup;
}
public boolean isTopup() {
return this.isTopup;
}
public BigDecimal getFirstDisbursalAmount() {
BigDecimal firstDisbursalAmount;
if(this.isMultiDisburmentLoan()){
List<DisbursementData> disbursementData = getDisbursmentData();
Collections.sort(disbursementData);
firstDisbursalAmount = disbursementData.get(disbursementData.size()-1).amount();
}else{
firstDisbursalAmount = this.getLoanRepaymentScheduleDetail().getPrincipal().getAmount();
}
return firstDisbursalAmount;
}
public void setTopupLoanDetails(LoanTopupDetails topupLoanDetails) {
this.loanTopupDetails = topupLoanDetails;
}
public LoanTopupDetails getTopupLoanDetails() {
return this.loanTopupDetails;
}
} }
@@ -25,6 +25,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.springframework.transaction.annotation.Transactional;
public interface LoanAccountDomainService { public interface LoanAccountDomainService {
@@ -35,6 +36,9 @@ public interface LoanAccountDomainService {
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate, LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId); BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
LoanTransaction makeDisburseTransaction(Long loanId, LocalDate transactionDate, BigDecimal transactionAmount,
PaymentDetail paymentDetail, String noteText, String txnExternalId, boolean isLoanToLoanTransfer);
void reverseTransfer(LoanTransaction loanTransaction); void reverseTransfer(LoanTransaction loanTransaction);
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount, LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
@@ -55,6 +59,10 @@ public interface LoanAccountDomainService {
*/ */
void recalculateAccruals(Loan loan); void recalculateAccruals(Loan loan);
LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, boolean isRecoveryRepayment,
boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, boolean isLoanToLoanTransfer);
void saveLoanWithDataIntegrityViolationChecks(Loan loan); void saveLoanWithDataIntegrityViolationChecks(Loan loan);
Map<String, Object> foreCloseLoan(final Loan loan, final LocalDate foreClourseDate, String noteText); Map<String, Object> foreCloseLoan(final Loan loan, final LocalDate foreClourseDate, String noteText);
@@ -139,6 +139,16 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
Boolean isHolidayValidationDone) { Boolean isHolidayValidationDone) {
return makeRepayment(loan, builderResult, transactionDate, transactionAmount, paymentDetail, noteText,
txnExternalId, isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone, false);
}
@Transactional
@Override
public LoanTransaction makeRepayment(final Loan loan, final CommandProcessingResultBuilder builderResult,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
Boolean isHolidayValidationDone, final boolean isLoanToLoanTransfer) {
AppUser currentUser = getAppUserIfPresent(); AppUser currentUser = getAppUserIfPresent();
checkClientOrGroupActive(loan); checkClientOrGroupActive(loan);
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT,
@@ -208,13 +218,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note); this.noteRepository.save(note);
} }
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
recalculateAccruals(loan); recalculateAccruals(loan);
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT, this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT,
constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, newRepaymentTransaction)); constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, newRepaymentTransaction));
// disable all active standing orders linked to this loan if status changes to closed // disable all active standing orders linked to this loan if status changes to closed
disableStandingInstructionsLinkedToClosedLoan(loan); disableStandingInstructionsLinkedToClosedLoan(loan);
@@ -336,12 +346,18 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds, private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) { final List<Long> existingReversedTransactionIds, boolean isAccountTransfer) {
postJournalEntries(loanAccount,existingTransactionIds,existingReversedTransactionIds,isAccountTransfer, false);
}
private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, boolean isAccountTransfer, boolean isLoanToLoanTransfer) {
final MonetaryCurrency currency = loanAccount.getCurrency(); final MonetaryCurrency currency = loanAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(), final Map<String, Object> accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
accountingBridgeData.put("isLoanToLoanTransfer", isLoanToLoanTransfer);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
} }
@@ -409,6 +425,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
@Override @Override
public LoanTransaction makeDisburseTransaction(final Long loanId, final LocalDate transactionDate, final BigDecimal transactionAmount, public LoanTransaction makeDisburseTransaction(final Long loanId, final LocalDate transactionDate, final BigDecimal transactionAmount,
final PaymentDetail paymentDetail, final String noteText, final String txnExternalId) { final PaymentDetail paymentDetail, final String noteText, final String txnExternalId) {
return makeDisburseTransaction(loanId, transactionDate, transactionAmount, paymentDetail, noteText, txnExternalId, false);
}
@Transactional
@Override
public LoanTransaction makeDisburseTransaction(final Long loanId, final LocalDate transactionDate, final BigDecimal transactionAmount,
final PaymentDetail paymentDetail, final String noteText, final String txnExternalId, final boolean isLoanToLoanTransfer) {
AppUser currentUser = getAppUserIfPresent(); AppUser currentUser = getAppUserIfPresent();
final Loan loan = this.loanAccountAssembler.assembleFrom(loanId); final Loan loan = this.loanAccountAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan); checkClientOrGroupActive(loan);
@@ -428,7 +451,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
this.noteRepository.save(note); this.noteRepository.save(note);
} }
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
return disbursementTransaction; return disbursementTransaction;
} }
@@ -71,6 +71,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
public static final String FIND_BY_ACCOUNT_NUMBER = "from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)"; public static final String FIND_BY_ACCOUNT_NUMBER = "from Loan loan where loan.accountNumber = :accountNumber and loan.loanStatus in (100,200,300,303,304)";
public static final String FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT = "from Loan loan where loan.id = :loanId and loan.loanStatus = 300 and loan.client.id = :clientId";
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER) @Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId, List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType); @Param("loanType") Integer loanType);
@@ -149,4 +151,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
@Query(FIND_BY_ACCOUNT_NUMBER) @Query(FIND_BY_ACCOUNT_NUMBER)
Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber); Loan findNonClosedLoanByAccountNumber(@Param("accountNumber") String accountNumber);
@Query(FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT)
Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId);
} }
@@ -0,0 +1,68 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.domain;
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.stereotype.Component;
import javax.persistence.*;
import java.math.BigDecimal;
@Entity
@Table(name = "m_loan_topup")
public class LoanTopupDetails extends AbstractPersistable<Long> {
@OneToOne
@JoinColumn(name = "loan_id", nullable = false)
private Loan loan;
@Column(name = "closure_loan_id", nullable = false)
private Long closureLoanId;
@Column(name = "account_transfer_details_id", nullable = true)
private Long accountTransferDetailsId;
@Column(name = "topup_amount", nullable = true)
private BigDecimal topupAmount;
protected LoanTopupDetails(){};
public LoanTopupDetails(final Loan loan, final Long loanIdToClose) {
this.loan = loan;
this.closureLoanId = loanIdToClose;
}
public Long getLoanIdToClose(){
return this.closureLoanId;
}
public BigDecimal getTopupAmount() {
return this.topupAmount;
}
public void setTopupAmount(BigDecimal topupAmount) {
this.topupAmount = topupAmount;
}
public void setAccountTransferDetails(Long accountTransferDetailsId) {
this.accountTransferDetailsId = accountTransferDetailsId;
}
}
@@ -68,7 +68,8 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName, LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName, LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName,
LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName)); LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -91,7 +91,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
LoanApiConstants.syncDisbursementWithMeetingParameterName,// optional LoanApiConstants.syncDisbursementWithMeetingParameterName,// optional
LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName, LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName, LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName)); LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper; private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
@@ -469,6 +470,18 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance) baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
.ignoreIfNull().positiveAmount(); .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); validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct); validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -899,6 +912,19 @@ public final class LoanApplicationCommandFromApiJsonHelper {
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance) baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
.ignoreIfNull().positiveAmount(); .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); validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct); validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
@@ -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.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; 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.entityaccess.exception.NotOfficeSpecificProductException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; 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.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData; import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.apache.fineract.portfolio.loanaccount.domain.*;
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.exception.LoanApplicationDateException; import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted; import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified; import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
@@ -287,6 +279,43 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(), productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
newLoanApplication); 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); this.loanRepository.save(newLoanApplication);
if (loanProduct.isInterestRecalculationEnabled()) { if (loanProduct.isInterestRecalculationEnabled()) {
@@ -555,6 +584,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final Set<LoanCollateral> possiblyModifedLoanCollateralItems = this.loanCollateralAssembler final Set<LoanCollateral> possiblyModifedLoanCollateralItems = this.loanCollateralAssembler
.fromParsedJson(command.parsedJson()); .fromParsedJson(command.parsedJson());
final Map<String, Object> changes = existingLoanApplication.loanApplicationModification(command, possiblyModifedLoanCharges, final Map<String, Object> changes = existingLoanApplication.loanApplicationModification(command, possiblyModifedLoanCharges,
possiblyModifedLoanCollateralItems, this.aprCalculator, isChargeModified); possiblyModifedLoanCollateralItems, this.aprCalculator, isChargeModified);
@@ -620,6 +650,56 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
updateProductRelatedDetails(productRelatedDetail, existingLoanApplication); 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"; final String fundIdParamName = "fundId";
if (changes.containsKey(fundIdParamName)) { if (changes.containsKey(fundIdParamName)) {
final Long fundId = command.longValueOfParameterNamed(fundIdParamName); final Long fundId = command.longValueOfParameterNamed(fundIdParamName);
@@ -981,6 +1061,22 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser); 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); saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
@@ -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.data.StaffData;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService; import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.account.data.AccountTransferData; 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.domain.AccountType;
import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
import org.apache.fineract.portfolio.accountdetails.service.AccountEnumerations; import org.apache.fineract.portfolio.accountdetails.service.AccountEnumerations;
import org.apache.fineract.portfolio.calendar.data.CalendarData; import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
@@ -153,6 +155,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService; private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
private final LoanUtilService loanUtilService; private final LoanUtilService loanUtilService;
private final ConfigurationDomainService configurationDomainService; private final ConfigurationDomainService configurationDomainService;
private final AccountDetailsReadPlatformService accountDetailsReadPlatformService;
@Autowired @Autowired
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository, public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
@@ -166,7 +169,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final PaymentTypeReadPlatformService paymentTypeReadPlatformService, final PaymentTypeReadPlatformService paymentTypeReadPlatformService,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService, final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService,
final ConfigurationDomainService configurationDomainService) { final ConfigurationDomainService configurationDomainService,
final AccountDetailsReadPlatformService accountDetailsReadPlatformService) {
this.context = context; this.context = context;
this.loanRepository = loanRepository; this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository; this.loanTransactionRepository = loanTransactionRepository;
@@ -187,6 +191,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService; this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
this.loanUtilService = loanUtilService; this.loanUtilService = loanUtilService;
this.configurationDomainService = configurationDomainService; this.configurationDomainService = configurationDomainService;
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
} }
@Override @Override
@@ -618,7 +623,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
+ " l.is_floating_interest_rate as isFloatingInterestRate, " + " l.is_floating_interest_rate as isFloatingInterestRate, "
+ " l.interest_rate_differential as interestRateDifferential, " + " l.interest_rate_differential as interestRateDifferential, "
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement, " + " 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" // + " from m_loan l" //
+ " join m_product_loan lp on lp.id = l.product_id" // + " join m_product_loan lp on lp.id = l.product_id" //
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.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 cv on cv.id = l.loanpurpose_cv_id"
+ " left join m_code_value codev on codev.id = l.writeoff_reason_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 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); 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, return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
clientOfficeId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, clientOfficeId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -944,7 +962,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears, loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears,
graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
interestRecalculationData, createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, 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()) { if (!this.disbursement.isDisbursed()) {
excludePastUndisbursed = false; excludePastUndisbursed = false;
} }
for(DisbursementData disbursementData : disbursementData){ for(DisbursementData data : disbursementData){
if(disbursementData.getChargeAmount() != null){ if(data.getChargeAmount() != null){
disbursementChargeAmount = disbursementChargeAmount.subtract(disbursementData.getChargeAmount()); disbursementChargeAmount = disbursementChargeAmount.subtract(data.getChargeAmount());
} }
} }
this.outstandingLoanPrincipalBalance = BigDecimal.ZERO; 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, return LoanAccountData.loanProductWithTemplateDefaults(loanProduct, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDaysOfWeekTypeOptions, repaymentStrategyOptions, repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDaysOfWeekTypeOptions, repaymentStrategyOptions,
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions, loanCycleCounter); fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions, loanCycleCounter, clientActiveLoanOptions);
} }
@Override @Override
@@ -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.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; 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.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException; import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; 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.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException; import org.apache.fineract.portfolio.loanaccount.exception.*;
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.guarantor.service.GuarantorDomainService; 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.data.OverdueLoanScheduleData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
@@ -362,16 +354,37 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ChangedTransactionDetail changedTransactionDetail = null; ChangedTransactionDetail changedTransactionDetail = null;
if (canDisburse) { if (canDisburse) {
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate); Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
Money amountToDisburse = disburseAmount.copy();
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal()); boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); 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) { if (isAccountTransfer) {
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail); disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
existingTransactionIds.addAll(loan.findExistingTransactionIds()); existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
} else { } else {
existingTransactionIds.addAll(loan.findExistingTransactionIds()); existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds()); 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); actualDisbursementDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
disbursementTransaction.updateLoan(loan); disbursementTransaction.updateLoan(loan);
loan.addLoanTransaction(disbursementTransaction); loan.addLoanTransaction(disbursementTransaction);
@@ -1685,6 +1698,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.withSavingsId(portfolioAccountData.accountId()).build(); .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) { public void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
@@ -129,6 +129,8 @@ public interface LoanProductConstants {
public static final String allowPartialPeriodInterestCalcualtionParamName = "allowPartialPeriodInterestCalcualtion"; public static final String allowPartialPeriodInterestCalcualtionParamName = "allowPartialPeriodInterestCalcualtion";
public static final String canUseForTopup = "canUseForTopup";
} }
@@ -91,7 +91,7 @@ public class LoanProductsApiResource {
"interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
"accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates", "accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates",
"floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate", "defaultDifferentialLendingRate", "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", private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
"allowedProducts", "productOptions")); "allowedProducts", "productOptions"));
@@ -126,6 +126,7 @@ public class LoanProductData {
private final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle; private final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle;
// accounting // accounting
private final EnumOptionData accountingRule; private final EnumOptionData accountingRule;
private final boolean canUseForTopup;
private Map<String, Object> accountingMappings; private Map<String, Object> accountingMappings;
private Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings; private Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings;
private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings; private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
@@ -257,6 +258,7 @@ public class LoanProductData {
final Integer installmentAmountInMultiplesOf = null; final Integer installmentAmountInMultiplesOf = null;
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null; final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
final boolean syncExpectedWithDisbursementDate = false; final boolean syncExpectedWithDisbursementDate = false;
final boolean canUseForTopup = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -272,7 +274,7 @@ public class LoanProductData {
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
syncExpectedWithDisbursementDate); syncExpectedWithDisbursementDate, canUseForTopup);
} }
@@ -348,6 +350,7 @@ public class LoanProductData {
final Integer installmentAmountInMultiplesOf = null; final Integer installmentAmountInMultiplesOf = null;
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null; final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
final boolean syncExpectedWithDisbursementDate = false; final boolean syncExpectedWithDisbursementDate = false;
final boolean canUseForTopup = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -363,7 +366,7 @@ public class LoanProductData {
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
syncExpectedWithDisbursementDate); syncExpectedWithDisbursementDate, canUseForTopup);
} }
@@ -446,6 +449,7 @@ public class LoanProductData {
final Integer installmentAmountInMultiplesOf = null; final Integer installmentAmountInMultiplesOf = null;
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null; final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
final boolean syncExpectedWithDisbursementDate = false; final boolean syncExpectedWithDisbursementDate = false;
final boolean canUseForTopup = false;
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
@@ -461,9 +465,8 @@ public class LoanProductData {
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap, maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
syncExpectedWithDisbursementDate); syncExpectedWithDisbursementDate, canUseForTopup);
} }
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings, 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 EnumOptionData interestCalculationPeriodType, final Boolean allowPartialPeriodInterestCalcualtion, final Long fundId,
final String fundName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName, final String fundName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final Collection<ChargeData> charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle, final Collection<ChargeData> charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle, boolean useBorrowerCycle, final LocalDate startDate,
boolean useBorrowerCycle, final LocalDate startDate, final LocalDate closeDate, final String status, final String externalId, final LocalDate closeDate, final String status, final String externalId,
Collection<LoanProductBorrowerCycleVariationData> principalVariations, Collection<LoanProductBorrowerCycleVariationData> principalVariations,
Collection<LoanProductBorrowerCycleVariationData> interestRateVariations, Collection<LoanProductBorrowerCycleVariationData> interestRateVariations,
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations, Boolean multiDisburseLoan, Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations, Boolean multiDisburseLoan,
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing, Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA,
final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment, final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment,
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount, final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount,
@@ -502,7 +504,7 @@ public class LoanProductData {
BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed, boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments, final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
final boolean syncExpectedWithDisbursementDate) { final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.shortName = shortName; this.shortName = shortName;
@@ -605,6 +607,7 @@ public class LoanProductData {
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
this.preClosureInterestCalculationStrategyOptions = null; this.preClosureInterestCalculationStrategyOptions = null;
this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate; this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate;
this.canUseForTopup = canUseForTopup;
} }
@@ -740,6 +743,7 @@ public class LoanProductData {
this.installmentAmountInMultiplesOf = productData.installmentAmountInMultiplesOf; this.installmentAmountInMultiplesOf = productData.installmentAmountInMultiplesOf;
this.preClosureInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions; this.preClosureInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions;
this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate; this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate;
this.canUseForTopup = productData.canUseForTopup;
} }
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) { private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -1093,4 +1097,7 @@ public class LoanProductData {
return syncExpectedWithDisbursementDate; return syncExpectedWithDisbursementDate;
} }
public boolean canUseForTopup() {
return this.canUseForTopup;
}
} }
@@ -179,6 +179,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
@Column(name = "sync_expected_with_disbursement_date") @Column(name = "sync_expected_with_disbursement_date")
private boolean syncExpectedWithDisbursementDate; 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, public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) { 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 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, return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
annualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery, annualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery,
@@ -336,7 +345,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate, installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate,
interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate, interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments, 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 repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments, final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments,
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges, final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges, final AccountingRuleType accountingRuleType,
final AccountingRuleType accountingRuleType, final boolean includeInBorrowerCycle, final LocalDate startDate, final boolean includeInBorrowerCycle, final LocalDate startDate, final LocalDate closeDate, final String externalId, final boolean useBorrowerCycle,
final LocalDate closeDate, final String externalId, final boolean useBorrowerCycle, final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan, final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance,
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan, final Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
final boolean isInterestRecalculationEnabled, final boolean isInterestRecalculationEnabled,
final LoanProductInterestRecalculationDetails productInterestRecalculationDetails, final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
@@ -568,7 +575,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed, Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments, final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
final boolean syncExpectedWithDisbursementDate) { final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup) {
this.fund = fund; this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy; this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim(); this.name = name.trim();
@@ -644,6 +651,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
this.syncExpectedWithDisbursementDate = this.syncExpectedWithDisbursementDate =
syncExpectedWithDisbursementDate; syncExpectedWithDisbursementDate;
this.canUseForTopup = canUseForTopup;
} }
public MonetaryCurrency getCurrency() { public MonetaryCurrency getCurrency() {
@@ -1023,6 +1031,12 @@ public class LoanProduct extends AbstractPersistable<Long> {
this.installmentAmountInMultiplesOf = newValue; 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; return actualChanges;
} }
@@ -1347,4 +1361,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.allowVariabeInstallments; return this.allowVariabeInstallments;
} }
public boolean canUseForTopup(){
return this.canUseForTopup;
}
} }
@@ -107,7 +107,8 @@ public final class LoanProductDataValidator {
LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName, LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName,
LoanProductConstants.recalculationRestFrequencyWeekdayParamName, LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName, LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName,
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName)); LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
LoanProductConstants.canUseForTopup));
private final FromJsonHelper fromApiJsonHelper; private final FromJsonHelper fromApiJsonHelper;
@@ -621,6 +622,12 @@ public final class LoanProductDataValidator {
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, null); 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); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -1440,6 +1447,12 @@ public final class LoanProductDataValidator {
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct); 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); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
@@ -224,7 +224,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
+ "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed, " + "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed, "
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, " + "lp.allow_variabe_installments as isVariableIntallmentsAllowed, "
+ "lvi.minimum_gap as minimumGap, " + "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 " + " from m_product_loan lp "
+ " left join m_fund f on f.id = lp.fund_id " + " 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 " + " 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 accountMovesOutOfNPAOnlyOnArrearsCompletion = rs.getBoolean("accountMovesOutOfNPAOnlyOnArrearsCompletion");
final boolean syncExpectedWithDisbursementDate = rs.getBoolean("syncExpectedWithDisbursementDate"); final boolean syncExpectedWithDisbursementDate = rs.getBoolean("syncExpectedWithDisbursementDate");
final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance, return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
@@ -459,7 +462,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId, installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap, maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
maximumGap, syncExpectedWithDisbursementDate); maximumGap, syncExpectedWithDisbursementDate, canUseForTopup);
} }
} }
@@ -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
;