[FINERACT-131] Topup Loan Feature
This commit is contained in:
committed by
Adi Narayana Raju
parent
11cd6ef333
commit
961aa3df86
+9
@@ -51,6 +51,8 @@ public class LoanTransactionDTO {
|
||||
|
||||
private final boolean isAccountTransfer;
|
||||
|
||||
private boolean isLoanToLoanTransfer;
|
||||
|
||||
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 BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final boolean reversed,
|
||||
@@ -132,4 +134,11 @@ public class LoanTransactionDTO {
|
||||
return this.isAccountTransfer;
|
||||
}
|
||||
|
||||
public void setIsLoanToLoanTransfer(boolean isLoanToLoanTransfer) {
|
||||
this.isLoanToLoanTransfer = isLoanToLoanTransfer;
|
||||
}
|
||||
|
||||
public boolean isLoanToLoanTransfer(){
|
||||
return this.isLoanToLoanTransfer;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -170,7 +170,12 @@ public class AccountingProcessorHelper {
|
||||
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
|
||||
transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed,
|
||||
feePaymentDetails, penaltyPaymentDetails, isAccountTransfer);
|
||||
|
||||
Boolean isLoanToLoanTransfer = (Boolean) accountingBridgeData.get("isLoanToLoanTransfer");
|
||||
if(isLoanToLoanTransfer != null && isLoanToLoanTransfer){
|
||||
transaction.setIsLoanToLoanTransfer(true);
|
||||
} else {
|
||||
transaction.setIsLoanToLoanTransfer(false);
|
||||
}
|
||||
newLoanTransactions.add(transaction);
|
||||
|
||||
}
|
||||
|
||||
+10
-2
@@ -124,7 +124,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
||||
|
||||
// create journal entries for the disbursement (or disbursement
|
||||
// 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,
|
||||
ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId,
|
||||
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,
|
||||
transactionDate, totalDebitAmount, isReversal);
|
||||
} 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,
|
||||
FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
|
||||
transactionDate, totalDebitAmount, isReversal);
|
||||
|
||||
+9
-2
@@ -132,7 +132,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
|
||||
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
|
||||
final boolean isReversal = loanTransactionDTO.isReversed();
|
||||
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,
|
||||
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(), loanProductId,
|
||||
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 **/
|
||||
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(),
|
||||
loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
|
||||
} else {
|
||||
|
||||
+44
@@ -50,6 +50,8 @@ public class AccountTransferDTO {
|
||||
private final String noteText;
|
||||
private final String txnExternalId;
|
||||
private final Loan loan;
|
||||
private final Loan fromLoan;
|
||||
private final Loan toLoan;
|
||||
private final SavingsAccount toSavingsAccount;
|
||||
private final SavingsAccount fromSavingsAccount;
|
||||
private final Boolean isRegularTransaction;
|
||||
@@ -81,12 +83,46 @@ public class AccountTransferDTO {
|
||||
this.noteText = noteText;
|
||||
this.txnExternalId = txnExternalId;
|
||||
this.loan = loan;
|
||||
this.fromLoan = null;
|
||||
this.toLoan = null;
|
||||
this.toSavingsAccount = toSavingsAccount;
|
||||
this.fromSavingsAccount = fromSavingsAccount;
|
||||
this.isRegularTransaction = isRegularTransaction;
|
||||
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() {
|
||||
return this.transactionDate;
|
||||
}
|
||||
@@ -163,6 +199,14 @@ public class AccountTransferDTO {
|
||||
return this.loan;
|
||||
}
|
||||
|
||||
public Loan getFromLoan() {
|
||||
return this.fromLoan;
|
||||
}
|
||||
|
||||
public Loan getToLoan() {
|
||||
return this.toLoan;
|
||||
}
|
||||
|
||||
public SavingsAccount getToSavingsAccount() {
|
||||
return this.toSavingsAccount;
|
||||
}
|
||||
|
||||
+15
@@ -145,4 +145,19 @@ public class AccountTransferAssembler {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -201,4 +201,14 @@ public class AccountTransferDetailAssembler {
|
||||
return AccountTransferDetails.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
|
||||
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);
|
||||
}
|
||||
}
|
||||
+5
@@ -156,4 +156,9 @@ public class AccountTransferDetails extends AbstractPersistable<Long> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+6
@@ -145,4 +145,10 @@ public class AccountTransferTransaction extends AbstractPersistable<Long> {
|
||||
public AccountTransferDetails 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);
|
||||
}
|
||||
}
|
||||
+3
@@ -24,6 +24,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.account.PortfolioAccountType;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
|
||||
public interface AccountTransfersWritePlatformService {
|
||||
@@ -41,4 +42,6 @@ public interface AccountTransfersWritePlatformService {
|
||||
CommandProcessingResult refundByTransfer(JsonCommand command);
|
||||
|
||||
void reverseTransfersWithFromAccountTransactions(Collection<Long> fromTransactionIds, PortfolioAccountType accountTypeId);
|
||||
|
||||
AccountTransferDetails repayLoanWithTopup(AccountTransferDTO accountTransferDTO);
|
||||
}
|
||||
+39
-1
@@ -33,6 +33,7 @@ import java.util.Locale;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
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.data.AccountTransferDTO;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator;
|
||||
@@ -253,7 +254,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
for (final AccountTransferTransaction accountTransfer : acccountTransfers) {
|
||||
if (accountTransfer.getFromLoanTransaction() != null) {
|
||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
|
||||
} else if (accountTransfer.getToLoanTransaction() != null) {
|
||||
}
|
||||
if (accountTransfer.getToLoanTransaction() != null) {
|
||||
this.loanAccountDomainService.reverseTransfer(accountTransfer.getToLoanTransaction());
|
||||
}
|
||||
if (accountTransfer.getFromTransaction() != null) {
|
||||
@@ -409,11 +411,47 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
||||
toSavingsAccount, deposit, loanTransaction);
|
||||
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
|
||||
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;
|
||||
}
|
||||
|
||||
@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
|
||||
@Transactional
|
||||
public void updateLoanTransaction(final Long loanTransactionId, final LoanTransaction newLoanTransaction) {
|
||||
|
||||
+2
@@ -32,4 +32,6 @@ public interface AccountDetailsReadPlatformService {
|
||||
public Collection<LoanAccountSummaryData> retrieveClientLoanAccountsByLoanOfficerId(final Long clientId, final Long loanOfficerId);
|
||||
|
||||
public Collection<LoanAccountSummaryData> retrieveGroupLoanAccountsByLoanOfficerId(final Long groupId, final Long loanOfficerId);
|
||||
|
||||
public Collection<LoanAccountSummaryData> retrieveClientActiveLoanAccountSummary(final Long clientId);
|
||||
}
|
||||
|
||||
+5
@@ -111,6 +111,11 @@ public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements Accou
|
||||
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) {
|
||||
final LoanAccountSummaryDataMapper rm = new LoanAccountSummaryDataMapper();
|
||||
final String sql = "select " + rm.loanAccountSummarySchema() + loanwhereClause;
|
||||
|
||||
+6
@@ -117,4 +117,10 @@ public interface LoanApiConstants {
|
||||
public static final String transactionDateParamName = "transactionDate";
|
||||
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";
|
||||
|
||||
}
|
||||
|
||||
+15
-3
@@ -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.service.AccountAssociationsReadPlatformService;
|
||||
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.domain.CalendarEntityType;
|
||||
import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService;
|
||||
@@ -136,7 +138,8 @@ public class LoansApiResource {
|
||||
"termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions",
|
||||
"loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions",
|
||||
"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 String resourceNameForPermissions = "LOAN";
|
||||
@@ -164,6 +167,7 @@ public class LoansApiResource {
|
||||
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
|
||||
private final AccountDetailsReadPlatformService accountDetailsReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
||||
@@ -182,7 +186,8 @@ public class LoansApiResource {
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
|
||||
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService) {
|
||||
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
|
||||
final AccountDetailsReadPlatformService accountDetailsReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
||||
@@ -206,6 +211,7 @@ public class LoansApiResource {
|
||||
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
|
||||
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -514,6 +520,7 @@ public class LoansApiResource {
|
||||
Collection<CalendarData> calendarOptions = null;
|
||||
Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
PaidInAdvanceData paidInAdvanceTemplate = null;
|
||||
Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
|
||||
|
||||
final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
|
||||
if (template) {
|
||||
@@ -568,6 +575,11 @@ public class LoansApiResource {
|
||||
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(),
|
||||
@@ -581,7 +593,7 @@ public class LoansApiResource {
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, loanCollateralOptions,
|
||||
calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
|
||||
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods);
|
||||
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
|
||||
+84
-51
@@ -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.staff.data.StaffData;
|
||||
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.charge.data.ChargeData;
|
||||
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.group.data.GroupGeneralData;
|
||||
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.loanschedule.data.LoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
|
||||
@@ -180,6 +180,12 @@ public class LoanAccountData {
|
||||
private final Boolean canDisburse;
|
||||
|
||||
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;
|
||||
|
||||
@@ -319,6 +325,12 @@ public class LoanAccountData {
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = 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,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -338,7 +350,7 @@ public class LoanAccountData {
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
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 Integer minimumGap = 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,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -472,7 +490,7 @@ public class LoanAccountData {
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
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.isInterestRecalculationEnabled, acc.interestRecalculationData, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,6 +636,12 @@ public class LoanAccountData {
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = 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,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -636,7 +661,7 @@ public class LoanAccountData {
|
||||
maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
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.isInterestRecalculationEnabled, acc.interestRecalculationData, 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);
|
||||
|
||||
}
|
||||
|
||||
@@ -678,7 +704,7 @@ public class LoanAccountData {
|
||||
final Collection<EnumOptionData> interestTypeOptions, final Collection<EnumOptionData> interestCalculationPeriodTypeOptions,
|
||||
final Collection<FundData> fundOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<CodeValueData> loanPurposeOptions, final Collection<CodeValueData> loanCollateralOptions,
|
||||
final Integer loanCycleNumber) {
|
||||
final Integer loanCycleNumber, final Collection<LoanAccountSummaryData> clientActiveLoanOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String accountNo = null;
|
||||
@@ -790,6 +816,11 @@ public class LoanAccountData {
|
||||
final Boolean isVariableInstallmentsAllowed = Boolean.FALSE;
|
||||
final Integer minimumGap = 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,
|
||||
loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
|
||||
@@ -814,7 +845,8 @@ public class LoanAccountData {
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
|
||||
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) {
|
||||
@@ -873,7 +905,8 @@ public class LoanAccountData {
|
||||
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
|
||||
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
|
||||
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 EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
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 Collection<LoanTransactionData> transactions = null;
|
||||
@@ -939,6 +974,7 @@ public class LoanAccountData {
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
|
||||
@@ -958,7 +994,7 @@ public class LoanAccountData {
|
||||
outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
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 PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
||||
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;
|
||||
if (acc.product != null) {
|
||||
loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
|
||||
@@ -1009,7 +1046,8 @@ public class LoanAccountData {
|
||||
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
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,
|
||||
@@ -1022,7 +1060,7 @@ public class LoanAccountData {
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
|
||||
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) {
|
||||
@@ -1050,7 +1088,8 @@ public class LoanAccountData {
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, 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 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.isInterestRecalculationEnabled, acc.interestRecalculationData, 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);
|
||||
|
||||
}
|
||||
|
||||
@@ -1147,7 +1187,8 @@ public class LoanAccountData {
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, interestRecalculationData, 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 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.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
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) {
|
||||
@@ -1202,40 +1244,24 @@ public class LoanAccountData {
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, 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);
|
||||
}
|
||||
|
||||
private LoanAccountData(
|
||||
final Long id, //
|
||||
private LoanAccountData(final Long id, //
|
||||
final String accountNo, //
|
||||
final LoanStatusEnumData status, //
|
||||
final String externalId, //
|
||||
final Long clientId,
|
||||
final String clientAccountNo,
|
||||
final String clientName,
|
||||
final Long clientOfficeId, //
|
||||
final GroupGeneralData group,
|
||||
final EnumOptionData loanType,
|
||||
final Long loanProductId,
|
||||
final String loanProductName,
|
||||
final Long clientId, final String clientAccountNo, final String clientName, final Long clientOfficeId, //
|
||||
final GroupGeneralData group, final EnumOptionData loanType, final Long loanProductId, final String loanProductName,
|
||||
final String loanProductDescription, //
|
||||
final boolean isLoanProductLinkedToFloatingRate,
|
||||
final Long fundId,
|
||||
final String fundName,
|
||||
final Long loanPurposeId,
|
||||
final boolean isLoanProductLinkedToFloatingRate, final Long fundId, final String fundName, final Long loanPurposeId,
|
||||
final String loanPurposeName, //
|
||||
final Long loanOfficerId,
|
||||
final String loanOfficerName, //
|
||||
final CurrencyData currency,
|
||||
BigDecimal proposedPrincipal,
|
||||
final BigDecimal principal,
|
||||
final BigDecimal approvedPrincipal,
|
||||
final Long loanOfficerId, final String loanOfficerName, //
|
||||
final CurrencyData currency, BigDecimal proposedPrincipal, final BigDecimal principal, final BigDecimal approvedPrincipal,
|
||||
final BigDecimal totalOverpaid, //
|
||||
final BigDecimal inArrearsTolerance,
|
||||
final Integer termFrequency, //
|
||||
final EnumOptionData termPeriodFrequencyType,
|
||||
final Integer numberOfRepayments,
|
||||
final Integer repaymentEvery,
|
||||
final BigDecimal inArrearsTolerance, final Integer termFrequency, //
|
||||
final EnumOptionData termPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
final EnumOptionData repaymentFrequencyType, //
|
||||
final EnumOptionData repaymentFrequencyNthDayType, final EnumOptionData repaymentFrequencyDayOfWeekType,
|
||||
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
|
||||
@@ -1244,12 +1270,10 @@ public class LoanAccountData {
|
||||
final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType,
|
||||
final Boolean allowPartialPeriodInterestCalcualtion, final LocalDate expectedFirstRepaymentOnDate,
|
||||
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
||||
final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData summary,
|
||||
final BigDecimal feeChargesDueAtDisbursementCharged, final LoanScheduleData repaymentSchedule,
|
||||
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<EnumOptionData> termFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
|
||||
final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData summary, final BigDecimal feeChargesDueAtDisbursementCharged,
|
||||
final LoanScheduleData repaymentSchedule, 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<EnumOptionData> termFrequencyTypeOptions, final Collection<EnumOptionData> repaymentFrequencyTypeOptions,
|
||||
final Collection<EnumOptionData> repaymentFrequencyNthDayTypeOptions,
|
||||
final Collection<EnumOptionData> repaymentFrequencyDaysOfWeekTypeOptions,
|
||||
final Collection<TransactionProcessingStrategyData> transactionProcessingStrategyOptions,
|
||||
@@ -1269,7 +1293,9 @@ public class LoanAccountData {
|
||||
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
|
||||
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance,
|
||||
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.accountNo = accountNo;
|
||||
@@ -1445,6 +1471,13 @@ public class LoanAccountData {
|
||||
this.isVariableInstallmentsAllowed = isVariableInstallmentsAllowed;
|
||||
this.minimumGap = minimumGap;
|
||||
this.maximumGap = maximumGap;
|
||||
this.canUseForTopup = canUseForTopup;
|
||||
this.clientActiveLoanOptions = clientActiveLoanOptions;
|
||||
this.isTopup = isTopup;
|
||||
this.closureLoanId = closureLoanId;
|
||||
this.closureLoanAccountNo = closureLoanAccountNo;
|
||||
this.topupAmount = topupAmount;
|
||||
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
|
||||
+52
-56
@@ -21,37 +21,9 @@ package org.apache.fineract.portfolio.loanaccount.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
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 java.util.*;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
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 javax.persistence.*;
|
||||
|
||||
import org.apache.commons.lang.ObjectUtils;
|
||||
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.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||
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.exception.*;
|
||||
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.LoanApplicationTerms;
|
||||
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.loanaccount.loanschedule.domain.*;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.*;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||
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)
|
||||
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,
|
||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
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", "");
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
@@ -4765,6 +4727,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
defaultUserMessage);
|
||||
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;
|
||||
case LOAN_REPAYMENT_OR_WAIVER:
|
||||
if (!isOpen()) {
|
||||
@@ -6353,4 +6321,32 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -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.paymentdetail.domain.PaymentDetail;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
public interface LoanAccountDomainService {
|
||||
|
||||
@@ -35,6 +36,9 @@ public interface LoanAccountDomainService {
|
||||
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
|
||||
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);
|
||||
|
||||
LoanTransaction makeChargePayment(Loan loan, Long chargeId, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||
@@ -55,6 +59,10 @@ public interface LoanAccountDomainService {
|
||||
*/
|
||||
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);
|
||||
|
||||
Map<String, Object> foreCloseLoan(final Loan loan, final LocalDate foreClourseDate, String noteText);
|
||||
|
||||
+26
-3
@@ -139,6 +139,16 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
|
||||
final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
|
||||
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();
|
||||
checkClientOrGroupActive(loan);
|
||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT,
|
||||
@@ -208,13 +218,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
|
||||
|
||||
recalculateAccruals(loan);
|
||||
|
||||
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_MAKE_REPAYMENT,
|
||||
constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, newRepaymentTransaction));
|
||||
|
||||
|
||||
// disable all active standing orders linked to this loan if status changes to closed
|
||||
disableStandingInstructionsLinkedToClosedLoan(loan);
|
||||
|
||||
@@ -336,12 +346,18 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds,
|
||||
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 ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
|
||||
|
||||
final Map<String, Object> accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
accountingBridgeData.put("isLoanToLoanTransfer", isLoanToLoanTransfer);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
@@ -409,6 +425,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
@Override
|
||||
public LoanTransaction makeDisburseTransaction(final Long loanId, final LocalDate transactionDate, final BigDecimal transactionAmount,
|
||||
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();
|
||||
final Loan loan = this.loanAccountAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
@@ -428,7 +451,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
|
||||
return disbursementTransaction;
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -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_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)
|
||||
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
|
||||
@Param("loanType") Integer loanType);
|
||||
@@ -149,4 +151,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
|
||||
@Query(FIND_BY_ACCOUNT_NUMBER)
|
||||
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);
|
||||
}
|
||||
+68
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -68,7 +68,8 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||
LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName,
|
||||
LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName));
|
||||
LoanApiConstants.repaymentFrequencyNthDayTypeParameterName, LoanApiConstants.repaymentFrequencyDayOfWeekTypeParameterName,
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
|
||||
+27
-1
@@ -91,7 +91,8 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
LoanApiConstants.syncDisbursementWithMeetingParameterName,// optional
|
||||
LoanApiConstants.linkAccountIdParameterName, LoanApiConstants.disbursementDataParameterName,
|
||||
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName));
|
||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanApiConstants.createStandingInstructionAtDisbursementParameterName,
|
||||
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper;
|
||||
@@ -469,6 +470,18 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
|
||||
.ignoreIfNull().positiveAmount();
|
||||
}
|
||||
|
||||
if(loanProduct.canUseForTopup()){
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isTopup, element)){
|
||||
final Boolean isTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isTopup).value(isTopup).validateForBooleanValue();
|
||||
|
||||
if(isTopup != null && isTopup){
|
||||
final Long loanId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.loanIdToClose).value(loanId).notNull().longGreaterThanZero();
|
||||
}
|
||||
}
|
||||
}
|
||||
validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
|
||||
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
@@ -899,6 +912,19 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.maxOutstandingBalanceParameterName).value(maxOutstandingBalance)
|
||||
.ignoreIfNull().positiveAmount();
|
||||
}
|
||||
|
||||
if(loanProduct.canUseForTopup()){
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isTopup, element)){
|
||||
final Boolean isTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isTopup, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isTopup).value(isTopup).ignoreIfNull().validateForBooleanValue();
|
||||
|
||||
if(isTopup != null && isTopup){
|
||||
final Long loanId = this.fromApiJsonHelper.extractLongNamed(LoanApiConstants.loanIdToClose, element);
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.loanIdToClose).value(loanId).notNull().longGreaterThanZero();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateLoanMultiDisbursementdate(element, baseDataValidator, expectedDisbursementDate, principal);
|
||||
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
|
||||
|
||||
|
||||
+107
-11
@@ -40,6 +40,8 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exceptionmapper.PlatformDomainRuleExceptionMapper;
|
||||
import org.apache.fineract.infrastructure.entityaccess.exception.NotOfficeSpecificProductException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
@@ -83,17 +85,7 @@ import org.apache.fineract.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallmentRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.*;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
|
||||
@@ -287,6 +279,43 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
productRelatedDetail.getRepayEvery(), productRelatedDetail.getRepaymentPeriodFrequencyType().getValue(),
|
||||
newLoanApplication);
|
||||
|
||||
if(loanProduct.canUseForTopup() && clientId != null){
|
||||
final Boolean isTopup = command.booleanObjectValueOfParameterNamed(LoanApiConstants.isTopup);
|
||||
if(null == isTopup){
|
||||
newLoanApplication.setIsTopup(false);
|
||||
}else{
|
||||
newLoanApplication.setIsTopup(isTopup);
|
||||
}
|
||||
|
||||
if(newLoanApplication.isTopup()){
|
||||
final Long loanIdToClose = command.longValueOfParameterNamed(LoanApiConstants.loanIdToClose);
|
||||
final Loan loanToClose = this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, clientId);
|
||||
if(loanToClose == null){
|
||||
throw new LoanNotFoundException(loanIdToClose);
|
||||
}
|
||||
if(loanToClose.isMultiDisburmentLoan() && !loanToClose.isInterestRecalculationEnabledForProduct()){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.topup.on.multi.tranche.loan.without.interest.recalculation.not.supported",
|
||||
"Topup on loan with multi-tranche disbursal and without interest recalculation is not supported.");
|
||||
}
|
||||
final LocalDate disbursalDateOfLoanToClose = loanToClose.getDisbursementDate();
|
||||
if(!newLoanApplication.getSubmittedOnDate().isAfter(disbursalDateOfLoanToClose)){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.submitted.date.before.topup.loan.disbursal.date",
|
||||
"Submitted date of this loan application "+newLoanApplication.getSubmittedOnDate()
|
||||
+" is before the disbursed date of loan to be closed "+ disbursalDateOfLoanToClose);
|
||||
}
|
||||
BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
|
||||
newLoanApplication.getDisbursementDate()).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = newLoanApplication.getFirstDisbursalAmount();
|
||||
if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
|
||||
final LoanTopupDetails topupDetails = new LoanTopupDetails(newLoanApplication, loanIdToClose);
|
||||
newLoanApplication.setTopupLoanDetails(topupDetails);
|
||||
}
|
||||
}
|
||||
|
||||
this.loanRepository.save(newLoanApplication);
|
||||
|
||||
if (loanProduct.isInterestRecalculationEnabled()) {
|
||||
@@ -555,6 +584,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final Set<LoanCollateral> possiblyModifedLoanCollateralItems = this.loanCollateralAssembler
|
||||
.fromParsedJson(command.parsedJson());
|
||||
|
||||
|
||||
final Map<String, Object> changes = existingLoanApplication.loanApplicationModification(command, possiblyModifedLoanCharges,
|
||||
possiblyModifedLoanCollateralItems, this.aprCalculator, isChargeModified);
|
||||
|
||||
@@ -620,6 +650,56 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
updateProductRelatedDetails(productRelatedDetail, existingLoanApplication);
|
||||
}
|
||||
|
||||
if(existingLoanApplication.getLoanProduct().canUseForTopup() && existingLoanApplication.getClientId() != null){
|
||||
final Boolean isTopup = command.booleanObjectValueOfParameterNamed(LoanApiConstants.isTopup);
|
||||
if(command.isChangeInBooleanParameterNamed(LoanApiConstants.isTopup, existingLoanApplication.isTopup())){
|
||||
existingLoanApplication.setIsTopup(isTopup);
|
||||
changes.put(LoanApiConstants.isTopup, isTopup);
|
||||
}
|
||||
|
||||
if(existingLoanApplication.isTopup()){
|
||||
final Long loanIdToClose = command.longValueOfParameterNamed(LoanApiConstants.loanIdToClose);
|
||||
LoanTopupDetails existingLoanTopupDetails = existingLoanApplication.getTopupLoanDetails();
|
||||
if(existingLoanTopupDetails == null
|
||||
|| (existingLoanTopupDetails != null && existingLoanTopupDetails.getLoanIdToClose() != loanIdToClose)){
|
||||
final Loan loanToClose = this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, existingLoanApplication.getClientId());
|
||||
if(loanToClose == null){
|
||||
throw new LoanNotFoundException(loanIdToClose);
|
||||
}
|
||||
if(loanToClose.isMultiDisburmentLoan() && !loanToClose.isInterestRecalculationEnabledForProduct()){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.topup.on.multi.tranche.loan.without.interest.recalculation.not.supported",
|
||||
"Topup on loan with multi-tranche disbursal and without interest recalculation is not supported.");
|
||||
}
|
||||
final LocalDate disbursalDateOfLoanToClose = loanToClose.getDisbursementDate();
|
||||
if(!existingLoanApplication.getSubmittedOnDate().isAfter(disbursalDateOfLoanToClose)){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.submitted.date.before.topup.loan.disbursal.date",
|
||||
"Submitted date of this loan application "+existingLoanApplication.getSubmittedOnDate()
|
||||
+" is before the disbursed date of loan to be closed "+ disbursalDateOfLoanToClose);
|
||||
}
|
||||
BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
|
||||
existingLoanApplication.getDisbursementDate()).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = existingLoanApplication.getFirstDisbursalAmount();
|
||||
if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
|
||||
final LoanTopupDetails topupDetails = new LoanTopupDetails(existingLoanApplication, loanIdToClose);
|
||||
existingLoanApplication.setTopupLoanDetails(topupDetails);
|
||||
changes.put(LoanApiConstants.loanIdToClose, loanIdToClose);
|
||||
}
|
||||
}else{
|
||||
existingLoanApplication.setTopupLoanDetails(null);
|
||||
}
|
||||
} else {
|
||||
if(existingLoanApplication.isTopup()){
|
||||
existingLoanApplication.setIsTopup(false);
|
||||
existingLoanApplication.setTopupLoanDetails(null);
|
||||
changes.put(LoanApiConstants.isTopup, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final String fundIdParamName = "fundId";
|
||||
if (changes.containsKey(fundIdParamName)) {
|
||||
final Long fundId = command.longValueOfParameterNamed(fundIdParamName);
|
||||
@@ -981,6 +1061,22 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
|
||||
}
|
||||
|
||||
if(loan.isTopup() && loan.getClientId() != null){
|
||||
final Long loanIdToClose = loan.getTopupLoanDetails().getLoanIdToClose();
|
||||
final Loan loanToClose = this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, loan.getClientId());
|
||||
if(loanToClose == null){
|
||||
throw new LoanNotFoundException(loanIdToClose);
|
||||
}
|
||||
|
||||
BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
|
||||
expectedDisbursementDate).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
|
||||
if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
}
|
||||
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
final String noteText = command.stringValueOfParameterNamed("note");
|
||||
|
||||
+31
-8
@@ -52,7 +52,9 @@ import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
|
||||
import org.apache.fineract.portfolio.account.data.AccountTransferData;
|
||||
import org.apache.fineract.portfolio.accountdetails.data.LoanAccountSummaryData;
|
||||
import org.apache.fineract.portfolio.accountdetails.domain.AccountType;
|
||||
import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService;
|
||||
import org.apache.fineract.portfolio.accountdetails.service.AccountEnumerations;
|
||||
import org.apache.fineract.portfolio.calendar.data.CalendarData;
|
||||
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
|
||||
@@ -153,6 +155,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final LoanUtilService loanUtilService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final AccountDetailsReadPlatformService accountDetailsReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
|
||||
@@ -166,7 +169,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService,
|
||||
final ConfigurationDomainService configurationDomainService) {
|
||||
final ConfigurationDomainService configurationDomainService,
|
||||
final AccountDetailsReadPlatformService accountDetailsReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
@@ -187,6 +191,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
this.loanUtilService = loanUtilService;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -618,7 +623,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " l.is_floating_interest_rate as isFloatingInterestRate, "
|
||||
+ " l.interest_rate_differential as interestRateDifferential, "
|
||||
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement, "
|
||||
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap "
|
||||
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
|
||||
+ " lp.can_use_for_topup as canUseForTopup, "
|
||||
+ " l.is_topup as isTopup, "
|
||||
+ " topup.closure_loan_id as closureLoanId, "
|
||||
+ " topuploan.account_no as closureLoanAccountNo, "
|
||||
+ " topup.topup_amount as topupAmount "
|
||||
+ " from m_loan l" //
|
||||
+ " join m_product_loan lp on lp.id = l.product_id" //
|
||||
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id "
|
||||
@@ -637,7 +647,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id"
|
||||
+ " left join m_code_value codev on codev.id = l.writeoff_reason_cv_id"
|
||||
+ " left join ref_loan_transaction_processing_strategy lps on lps.id = l.loan_transaction_strategy_id"
|
||||
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id";
|
||||
+ " left join m_product_loan_variable_installment_config lpvi on lpvi.loan_product_id = l.product_id"
|
||||
+ " left join m_loan_topup as topup on l.id = topup.loan_id"
|
||||
+ " left join m_loan as topuploan on topuploan.id = topup.closure_loan_id";
|
||||
|
||||
}
|
||||
|
||||
@@ -931,6 +943,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
isCompoundingToBePostedAsTransaction, allowCompoundingOnEod);
|
||||
}
|
||||
|
||||
final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
|
||||
final boolean isTopup = rs.getBoolean("isTopup");
|
||||
final Long closureLoanId = rs.getLong("closureLoanId");
|
||||
final String closureLoanAccountNo = rs.getString("closureLoanAccountNo");
|
||||
final BigDecimal topupAmount = rs.getBigDecimal("topupAmount");
|
||||
|
||||
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
|
||||
clientOfficeId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
|
||||
isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -944,7 +962,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears,
|
||||
graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
interestRecalculationData, createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap,
|
||||
maximumGap, loanSubStatus);
|
||||
maximumGap, loanSubStatus, canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,9 +1067,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
if (!this.disbursement.isDisbursed()) {
|
||||
excludePastUndisbursed = false;
|
||||
}
|
||||
for(DisbursementData disbursementData : disbursementData){
|
||||
if(disbursementData.getChargeAmount() != null){
|
||||
disbursementChargeAmount = disbursementChargeAmount.subtract(disbursementData.getChargeAmount());
|
||||
for(DisbursementData data : disbursementData){
|
||||
if(data.getChargeAmount() != null){
|
||||
disbursementChargeAmount = disbursementChargeAmount.subtract(data.getChargeAmount());
|
||||
}
|
||||
}
|
||||
this.outstandingLoanPrincipalBalance = BigDecimal.ZERO;
|
||||
@@ -1425,10 +1443,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
}
|
||||
|
||||
Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
|
||||
if(loanProduct.canUseForTopup() && clientId != null){
|
||||
clientActiveLoanOptions = this.accountDetailsReadPlatformService.retrieveClientActiveLoanAccountSummary(clientId);
|
||||
}
|
||||
|
||||
return LoanAccountData.loanProductWithTemplateDefaults(loanProduct, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDaysOfWeekTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions, loanCycleCounter);
|
||||
fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions, loanCycleCounter, clientActiveLoanOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+41
-12
@@ -41,6 +41,7 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
@@ -142,16 +143,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementC
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanDisbursalException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursementException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.MultiDisbursementDataRequiredException;
|
||||
import org.apache.fineract.portfolio.loanaccount.exception.*;
|
||||
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDomainService;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
@@ -362,16 +354,37 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
if (canDisburse) {
|
||||
Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate);
|
||||
Money amountToDisburse = disburseAmount.copy();
|
||||
boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal());
|
||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||
|
||||
if(loan.isTopup() && loan.getClientId() != null){
|
||||
final Long loanIdToClose = loan.getTopupLoanDetails().getLoanIdToClose();
|
||||
final Loan loanToClose = this.loanRepository.findNonClosedLoanThatBelongsToClient(loanIdToClose, loan.getClientId());
|
||||
if(loanToClose == null){
|
||||
throw new LoanNotFoundException(loanIdToClose);
|
||||
}
|
||||
BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose,
|
||||
actualDisbursementDate).getAmount();
|
||||
final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
|
||||
if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
|
||||
"Topup loan amount should be greater than outstanding amount of loan to be closed.");
|
||||
}
|
||||
|
||||
amountToDisburse = disburseAmount.minus(loanOutstanding);
|
||||
|
||||
disburseLoanToLoan(loan, command, loanOutstanding);
|
||||
}
|
||||
|
||||
if (isAccountTransfer) {
|
||||
disburseLoanToSavings(loan, command, disburseAmount, paymentDetail);
|
||||
disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail);
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
} else {
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), disburseAmount, paymentDetail,
|
||||
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
|
||||
actualDisbursementDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
|
||||
disbursementTransaction.updateLoan(loan);
|
||||
loan.addLoanTransaction(disbursementTransaction);
|
||||
@@ -1685,6 +1698,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.withSavingsId(portfolioAccountData.accountId()).build();
|
||||
}
|
||||
|
||||
public void disburseLoanToLoan(final Loan loan, final JsonCommand command, final BigDecimal amount) {
|
||||
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||
final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||
|
||||
final Locale locale = command.extractLocale();
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, amount,
|
||||
PortfolioAccountType.LOAN, PortfolioAccountType.LOAN, loan.getId(), loan.getTopupLoanDetails().getLoanIdToClose(),
|
||||
"Loan Topup", locale, fmt, LoanTransactionType.DISBURSEMENT.getValue(), LoanTransactionType.REPAYMENT.getValue(),
|
||||
txnExternalId, loan, null);
|
||||
AccountTransferDetails accountTransferDetails = this.accountTransfersWritePlatformService.repayLoanWithTopup(accountTransferDTO);
|
||||
loan.getTopupLoanDetails().setAccountTransferDetails(accountTransferDetails.getId());
|
||||
loan.getTopupLoanDetails().setTopupAmount(amount);
|
||||
}
|
||||
|
||||
public void disburseLoanToSavings(final Loan loan, final JsonCommand command, final Money amount, final PaymentDetail paymentDetail) {
|
||||
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||
|
||||
+2
@@ -129,6 +129,8 @@ public interface LoanProductConstants {
|
||||
|
||||
|
||||
public static final String allowPartialPeriodInterestCalcualtionParamName = "allowPartialPeriodInterestCalcualtion";
|
||||
|
||||
public static final String canUseForTopup = "canUseForTopup";
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ public class LoanProductsApiResource {
|
||||
"interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions",
|
||||
"accountingRuleOptions", "accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates",
|
||||
"floatingRatesId", "interestRateDifferential", "minDifferentialLendingRate", "defaultDifferentialLendingRate",
|
||||
"maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed"));
|
||||
"maxDifferentialLendingRate", "isFloatingInterestRateCalculationAllowed", LoanProductConstants.canUseForTopup));
|
||||
|
||||
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
|
||||
"allowedProducts", "productOptions"));
|
||||
|
||||
+17
-10
@@ -126,6 +126,7 @@ public class LoanProductData {
|
||||
private final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle;
|
||||
// accounting
|
||||
private final EnumOptionData accountingRule;
|
||||
private final boolean canUseForTopup;
|
||||
private Map<String, Object> accountingMappings;
|
||||
private Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings;
|
||||
private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
|
||||
@@ -257,6 +258,7 @@ public class LoanProductData {
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -272,7 +274,7 @@ public class LoanProductData {
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
||||
|
||||
}
|
||||
|
||||
@@ -348,6 +350,7 @@ public class LoanProductData {
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -363,7 +366,7 @@ public class LoanProductData {
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId, floatingRateName,
|
||||
interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate, maxDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
||||
|
||||
}
|
||||
|
||||
@@ -446,6 +449,7 @@ public class LoanProductData {
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
final LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||
final boolean syncExpectedWithDisbursementDate = false;
|
||||
final boolean canUseForTopup = false;
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
@@ -461,9 +465,8 @@ public class LoanProductData {
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGap, maximumGap,
|
||||
syncExpectedWithDisbursementDate);
|
||||
syncExpectedWithDisbursementDate, canUseForTopup);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||
@@ -486,14 +489,13 @@ public class LoanProductData {
|
||||
final EnumOptionData interestCalculationPeriodType, final Boolean allowPartialPeriodInterestCalcualtion, final Long fundId,
|
||||
final String fundName, final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
|
||||
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
||||
final Collection<ChargeData> charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle,
|
||||
boolean useBorrowerCycle, final LocalDate startDate, final LocalDate closeDate, final String status, final String externalId,
|
||||
final Collection<ChargeData> charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle, boolean useBorrowerCycle, final LocalDate startDate,
|
||||
final LocalDate closeDate, final String status, final String externalId,
|
||||
Collection<LoanProductBorrowerCycleVariationData> principalVariations,
|
||||
Collection<LoanProductBorrowerCycleVariationData> interestRateVariations,
|
||||
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations, Boolean multiDisburseLoan,
|
||||
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
|
||||
final Integer overdueDaysForNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType,
|
||||
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
|
||||
Integer maxTrancheCount, BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA,
|
||||
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationData interestRecalculationData,
|
||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
|
||||
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment,
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount,
|
||||
@@ -502,7 +504,7 @@ public class LoanProductData {
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate, BigDecimal maxDifferentialLendingRate,
|
||||
boolean isFloatingInterestRateCalculationAllowed, final boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||
final boolean syncExpectedWithDisbursementDate) {
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -605,6 +607,7 @@ public class LoanProductData {
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.preClosureInterestCalculationStrategyOptions = null;
|
||||
this.syncExpectedWithDisbursementDate = syncExpectedWithDisbursementDate;
|
||||
this.canUseForTopup = canUseForTopup;
|
||||
|
||||
}
|
||||
|
||||
@@ -740,6 +743,7 @@ public class LoanProductData {
|
||||
this.installmentAmountInMultiplesOf = productData.installmentAmountInMultiplesOf;
|
||||
this.preClosureInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions;
|
||||
this.syncExpectedWithDisbursementDate = productData.syncExpectedWithDisbursementDate;
|
||||
this.canUseForTopup = productData.canUseForTopup;
|
||||
}
|
||||
|
||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||
@@ -1093,4 +1097,7 @@ public class LoanProductData {
|
||||
return syncExpectedWithDisbursementDate;
|
||||
}
|
||||
|
||||
public boolean canUseForTopup() {
|
||||
return this.canUseForTopup;
|
||||
}
|
||||
}
|
||||
+26
-8
@@ -179,6 +179,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "sync_expected_with_disbursement_date")
|
||||
private boolean syncExpectedWithDisbursementDate;
|
||||
|
||||
|
||||
@Column(name = "can_use_for_topup", nullable = false)
|
||||
private boolean canUseForTopup = false;
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
|
||||
|
||||
@@ -323,6 +327,11 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
|
||||
final boolean syncExpectedWithDisbursementDate = command.booleanPrimitiveValueOfParameterNamed("syncExpectedWithDisbursementDate");
|
||||
|
||||
|
||||
final boolean canUseForTopup = command.parameterExists(LoanProductConstants.canUseForTopup)
|
||||
? command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canUseForTopup)
|
||||
: false;
|
||||
|
||||
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, shortName, description, currency, principal, minPrincipal,
|
||||
maxPrincipal, interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType,
|
||||
annualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion, repaymentEvery,
|
||||
@@ -336,7 +345,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
installmentAmountInMultiplesOf, loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate,
|
||||
interestRateDifferential, minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed, isVariableInstallmentsAllowed, minimumGapBetweenInstallments,
|
||||
maximumGapBetweenInstallments, syncExpectedWithDisbursementDate);
|
||||
maximumGapBetweenInstallments, syncExpectedWithDisbursementDate, canUseForTopup);
|
||||
|
||||
}
|
||||
|
||||
@@ -552,12 +561,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments,
|
||||
final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments,
|
||||
final Integer graceOnPrincipalPayment, final Integer recurringMoratoriumOnPrincipalPeriods, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
||||
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges,
|
||||
final AccountingRuleType accountingRuleType, final boolean includeInBorrowerCycle, final LocalDate startDate,
|
||||
final LocalDate closeDate, final String externalId, final boolean useBorrowerCycle,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan,
|
||||
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
|
||||
final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
||||
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges, final AccountingRuleType accountingRuleType,
|
||||
final boolean includeInBorrowerCycle, final LocalDate startDate, final LocalDate closeDate, final String externalId, final boolean useBorrowerCycle,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan, final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance,
|
||||
final Integer graceOnArrearsAgeing, final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
|
||||
final boolean isInterestRecalculationEnabled,
|
||||
final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
|
||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
|
||||
@@ -568,7 +575,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
Boolean isFloatingInterestRateCalculationAllowed, final Boolean isVariableInstallmentsAllowed,
|
||||
final Integer minimumGapBetweenInstallments, final Integer maximumGapBetweenInstallments,
|
||||
final boolean syncExpectedWithDisbursementDate) {
|
||||
final boolean syncExpectedWithDisbursementDate, final boolean canUseForTopup) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.name = name.trim();
|
||||
@@ -644,6 +651,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.syncExpectedWithDisbursementDate =
|
||||
syncExpectedWithDisbursementDate;
|
||||
this.canUseForTopup = canUseForTopup;
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
@@ -1023,6 +1031,12 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
this.installmentAmountInMultiplesOf = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(LoanProductConstants.canUseForTopup, this.canUseForTopup)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.canUseForTopup);
|
||||
actualChanges.put(LoanProductConstants.canUseForTopup, newValue);
|
||||
this.canUseForTopup = newValue;
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
@@ -1347,4 +1361,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return this.allowVariabeInstallments;
|
||||
}
|
||||
|
||||
public boolean canUseForTopup(){
|
||||
return this.canUseForTopup;
|
||||
}
|
||||
|
||||
}
|
||||
+14
-1
@@ -107,7 +107,8 @@ public final class LoanProductDataValidator {
|
||||
LoanProductConstants.recalculationCompoundingFrequencyOnDayParamName,
|
||||
LoanProductConstants.recalculationRestFrequencyWeekdayParamName,
|
||||
LoanProductConstants.recalculationRestFrequencyNthDayParamName, LoanProductConstants.recalculationRestFrequencyOnDayParamName,
|
||||
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName));
|
||||
LoanProductConstants.isCompoundingToBePostedAsTransactionParamName, LoanProductConstants.allowCompoundingOnEodParamName,
|
||||
LoanProductConstants.canUseForTopup));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -621,6 +622,12 @@ public final class LoanProductDataValidator {
|
||||
|
||||
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, null);
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanProductConstants.canUseForTopup, element)){
|
||||
final Boolean canUseForTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.canUseForTopup,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.canUseForTopup).value(canUseForTopup).validateForBooleanValue();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -1440,6 +1447,12 @@ public final class LoanProductDataValidator {
|
||||
|
||||
validatePartialPeriodSupport(interestCalculationPeriodType, baseDataValidator, element, loanProduct);
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanProductConstants.canUseForTopup, element)){
|
||||
final Boolean canUseForTopup = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.canUseForTopup,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.canUseForTopup).value(canUseForTopup).validateForBooleanValue();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -224,7 +224,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed, "
|
||||
+ "lp.allow_variabe_installments as isVariableIntallmentsAllowed, "
|
||||
+ "lvi.minimum_gap as minimumGap, "
|
||||
+ "lvi.maximum_gap as maximumGap "
|
||||
+ "lvi.maximum_gap as maximumGap, "
|
||||
+ "lp.can_use_for_topup as canUseForTopup "
|
||||
+ " from m_product_loan lp "
|
||||
+ " left join m_fund f on f.id = lp.fund_id "
|
||||
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
|
||||
@@ -444,6 +445,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion = rs.getBoolean("accountMovesOutOfNPAOnlyOnArrearsCompletion");
|
||||
final boolean syncExpectedWithDisbursementDate = rs.getBoolean("syncExpectedWithDisbursementDate");
|
||||
|
||||
final boolean canUseForTopup = rs.getBoolean("canUseForTopup");
|
||||
|
||||
return new LoanProductData(id, name, shortName, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
|
||||
@@ -459,7 +462,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed, isVariableIntallmentsAllowed, minimumGap,
|
||||
maximumGap, syncExpectedWithDisbursementDate);
|
||||
maximumGap, syncExpectedWithDisbursementDate, canUseForTopup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user