support repayment strategy approach for deciding how loan transactions should be processed against the loan schedule.
This commit is contained in:
+5
@@ -18,6 +18,11 @@ ADD COLUMN `loan_transaction_strategy_id` bigint(20) DEFAULT NULL,
|
||||
ADD KEY `FK_ltp_strategy` (`loan_transaction_strategy_id`),
|
||||
ADD CONSTRAINT `FK_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`);
|
||||
|
||||
ALTER TABLE `portfolio_loan`
|
||||
ADD COLUMN `loan_transaction_strategy_id` bigint(20) DEFAULT NULL,
|
||||
ADD KEY `FK_loan_ltp_strategy` (`loan_transaction_strategy_id`),
|
||||
ADD CONSTRAINT `FK_loan_ltp_strategy` FOREIGN KEY (`loan_transaction_strategy_id`) REFERENCES `ref_loan_transaction_processing_strategy` (`id`);
|
||||
|
||||
-- update ref table with out-of-the-box strategies
|
||||
|
||||
INSERT INTO `ref_loan_transaction_processing_strategy`
|
||||
|
||||
+8
-1
@@ -15,6 +15,7 @@ public class SubmitLoanApplicationCommand {
|
||||
private final String externalId;
|
||||
|
||||
private final Long fundId;
|
||||
private final Long transactionProcessingStrategyId;
|
||||
|
||||
private final BigDecimal principal;
|
||||
private final BigDecimal inArrearsTolerance;
|
||||
@@ -39,7 +40,8 @@ public class SubmitLoanApplicationCommand {
|
||||
private LoanSchedule loanSchedule;
|
||||
|
||||
public SubmitLoanApplicationCommand(
|
||||
final Long clientId, final Long productId, final String externalId, final Long fundId,
|
||||
final Long clientId, final Long productId, final String externalId,
|
||||
final Long fundId, final Long transactionProcessingStrategyId,
|
||||
final LocalDate submittedOnDate, final String submittedOnNote,
|
||||
final LocalDate expectedDisbursementDate,
|
||||
final LocalDate repaymentsStartingFromDate,
|
||||
@@ -55,6 +57,7 @@ public class SubmitLoanApplicationCommand {
|
||||
this.productId = productId;
|
||||
this.externalId = externalId;
|
||||
this.fundId = fundId;
|
||||
this.transactionProcessingStrategyId = transactionProcessingStrategyId;
|
||||
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.submittedOnNote = submittedOnNote;
|
||||
@@ -104,6 +107,10 @@ public class SubmitLoanApplicationCommand {
|
||||
public Long getFundId() {
|
||||
return fundId;
|
||||
}
|
||||
|
||||
public Long getTransactionProcessingStrategyId() {
|
||||
return transactionProcessingStrategyId;
|
||||
}
|
||||
|
||||
public BigDecimal getPrincipal() {
|
||||
return principal;
|
||||
|
||||
+4
-2
@@ -872,7 +872,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
Map<String, String> requestMap = gsonConverter.fromJson(json, typeOfMap);
|
||||
|
||||
Set<String> supportedParams = new HashSet<String>(
|
||||
Arrays.asList("clientId", "productId", "externalId", "fundId",
|
||||
Arrays.asList("clientId", "productId", "externalId", "fundId", "transactionProcessingStrategyId",
|
||||
"principal", "inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments",
|
||||
"repaymentFrequencyType", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
|
||||
"expectedDisbursementDate", "repaymentsStartingFromDate", "interestChargedFromDate", "submittedOnDate", "submittedOnNote",
|
||||
@@ -886,6 +886,7 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
Long clientId = extractLongParameter("clientId", requestMap, modifiedParameters);
|
||||
Long productId = extractLongParameter("productId", requestMap, modifiedParameters);
|
||||
Long fundId = extractLongParameter("fundId", requestMap, modifiedParameters);
|
||||
Long transactionProcessingStrategyId = extractLongParameter("transactionProcessingStrategyId", requestMap, modifiedParameters);
|
||||
String externalId = extractStringParameter("externalId", requestMap, modifiedParameters);
|
||||
|
||||
BigDecimal principal = extractBigDecimalParameter("principal", requestMap, modifiedParameters);
|
||||
@@ -908,7 +909,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
|
||||
|
||||
String submittedOnNote = extractStringParameter("submittedOnNote", requestMap, modifiedParameters);
|
||||
|
||||
return new SubmitLoanApplicationCommand(clientId, productId, externalId, fundId, submittedOnDate, submittedOnNote,
|
||||
return new SubmitLoanApplicationCommand(clientId, productId, externalId, fundId, transactionProcessingStrategyId,
|
||||
submittedOnDate, submittedOnNote,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, interestChargedFromDate,
|
||||
principal, interestRatePerPeriod, interestRateFrequencyTypeValue, interestTypeValue, interestCalculationPeriodTypeValue,
|
||||
repaymentEvery, repaymentFrequencyType, numberOfRepayments, amortizationTypeValue, inArrearsToleranceValue);
|
||||
|
||||
@@ -50,6 +50,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "fund_id", nullable = true)
|
||||
private Fund fund;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
|
||||
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
|
||||
|
||||
@Column(name = "external_id")
|
||||
private String externalId;
|
||||
@@ -136,11 +140,10 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
@Transient
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessor = new LoanRepaymentScheduleTransactionProcessorFactory();
|
||||
|
||||
public static Loan createNew(final Fund fund,
|
||||
public static Loan createNew(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||
final LoanProduct loanProduct, final Client client,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail) {
|
||||
return new Loan(client, fund, loanProduct, loanRepaymentScheduleDetail,
|
||||
null);
|
||||
return new Loan(client, fund, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, null);
|
||||
}
|
||||
|
||||
public Loan() {
|
||||
@@ -149,15 +152,12 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.loanRepaymentScheduleDetail = null;
|
||||
}
|
||||
|
||||
public Loan(final Client client, Fund fund, final LoanProduct loanProduct,
|
||||
public Loan(final Client client, Fund fund, LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProduct loanProduct,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||
final LoanStatus loanStatus) {
|
||||
this.client = client;
|
||||
if (fund != null) {
|
||||
this.fund = fund;
|
||||
} else {
|
||||
this.fund = loanProduct.getFund();
|
||||
}
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.loanProduct = loanProduct;
|
||||
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
|
||||
this.loanStatus = loanStatus;
|
||||
@@ -445,7 +445,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
Collections.sort(repaymentsOrWaivers, transactionComparator);
|
||||
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor();
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy);
|
||||
if (isTransactionChronologicallyLatest && !adjusted) {
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments);
|
||||
} else {
|
||||
|
||||
+18
-7
@@ -1,21 +1,32 @@
|
||||
package org.mifosng.platform.loan.domain;
|
||||
|
||||
import org.mifosng.platform.infrastructure.MifosPlatformTenant;
|
||||
import org.mifosng.platform.infrastructure.ThreadLocalContextUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class LoanRepaymentScheduleTransactionProcessorFactory {
|
||||
|
||||
public LoanRepaymentScheduleTransactionProcessor determineProcessor() {
|
||||
public LoanRepaymentScheduleTransactionProcessor determineProcessor(final LoanTransactionProcessingStrategy transactionProcessingStrategy) {
|
||||
|
||||
LoanRepaymentScheduleTransactionProcessor processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
||||
|
||||
// FIXME - Just temp way of deciding what 'repayment strategy' to use for now.
|
||||
MifosPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
|
||||
if ("mifostenant-creocore".equalsIgnoreCase(tenant.getSchemaName())) {
|
||||
processor = new CreocoreLoanRepaymentScheduleTransactionProcessor();
|
||||
if (transactionProcessingStrategy != null) {
|
||||
|
||||
if (transactionProcessingStrategy.isStandardMifosStrategy()) {
|
||||
processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
if (transactionProcessingStrategy.isHeavensfamilyStrategy()) {
|
||||
processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
if (transactionProcessingStrategy.isCreocoreStrategy()) {
|
||||
processor = new CreocoreLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
|
||||
if (transactionProcessingStrategy.isIndianRBIStrategy()) {
|
||||
processor = new AdhikarLoanRepaymentScheduleTransactionProcessor();
|
||||
}
|
||||
}
|
||||
|
||||
return processor;
|
||||
|
||||
+16
-4
@@ -25,11 +25,23 @@ public class LoanTransactionProcessingStrategy extends AbstractAuditableCustom<A
|
||||
//
|
||||
}
|
||||
|
||||
public String code() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public TransactionProcessingStrategyData toData() {
|
||||
return new TransactionProcessingStrategyData(this.getId(), this.code, this.name);
|
||||
}
|
||||
|
||||
public boolean isStandardMifosStrategy() {
|
||||
return "mifos-standard-strategy".equalsIgnoreCase(this.code);
|
||||
}
|
||||
|
||||
public boolean isHeavensfamilyStrategy() {
|
||||
return "heavensfamily-strategy".equalsIgnoreCase(this.code);
|
||||
}
|
||||
|
||||
public boolean isCreocoreStrategy() {
|
||||
return "creocore-strategy".equalsIgnoreCase(this.code);
|
||||
}
|
||||
|
||||
public boolean isIndianRBIStrategy() {
|
||||
return "rbi-india-strategy".equalsIgnoreCase(this.code);
|
||||
}
|
||||
}
|
||||
+24
-5
@@ -13,6 +13,7 @@ import org.mifosng.platform.currency.domain.MonetaryCurrency;
|
||||
import org.mifosng.platform.exceptions.ClientNotFoundException;
|
||||
import org.mifosng.platform.exceptions.FundNotFoundException;
|
||||
import org.mifosng.platform.exceptions.LoanProductNotFoundException;
|
||||
import org.mifosng.platform.exceptions.LoanTransactionProcessingStrategyNotFoundException;
|
||||
import org.mifosng.platform.fund.domain.Fund;
|
||||
import org.mifosng.platform.fund.domain.FundRepository;
|
||||
import org.mifosng.platform.loan.domain.AmortizationMethod;
|
||||
@@ -27,6 +28,8 @@ import org.mifosng.platform.loan.domain.LoanProductRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosng.platform.loan.domain.LoanStatus;
|
||||
import org.mifosng.platform.loan.domain.LoanStatusRepository;
|
||||
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosng.platform.loan.domain.LoanTransactionProcessingStrategyRepository;
|
||||
import org.mifosng.platform.loan.domain.PeriodFrequencyType;
|
||||
import org.mifosng.platform.loanschedule.domain.AprCalculator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -40,18 +43,22 @@ public class LoanAssembler {
|
||||
private final ClientRepository clientRepository;
|
||||
private final AprCalculator aprCalculator = new AprCalculator();
|
||||
private final FundRepository fundRepository;
|
||||
private final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanAssembler(final LoanStatusRepository loanStatusRepository,
|
||||
final LoanProductRepository loanProductRepository,
|
||||
final ClientRepository clientRepository, final FundRepository fundRepository) {
|
||||
final ClientRepository clientRepository,
|
||||
final FundRepository fundRepository,
|
||||
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository) {
|
||||
this.loanStatusRepository = loanStatusRepository;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.clientRepository = clientRepository;
|
||||
this.fundRepository = fundRepository;
|
||||
this.loanTransactionProcessingStrategyRepository = loanTransactionProcessingStrategyRepository;
|
||||
}
|
||||
|
||||
public Loan assembleFrom(SubmitLoanApplicationCommand command) {
|
||||
public Loan assembleFrom(final SubmitLoanApplicationCommand command) {
|
||||
|
||||
LoanProduct loanProduct = this.loanProductRepository.findOne(command.getProductId());
|
||||
if (loanProduct == null) {
|
||||
@@ -87,8 +94,9 @@ public class LoanAssembler {
|
||||
|
||||
// associating fund with loan product at creation is optional for now.
|
||||
Fund fund = findFundByIdIfProvided(command.getFundId());
|
||||
|
||||
Loan loan = Loan.createNew(fund, loanProduct, client, loanRepaymentScheduleDetail);
|
||||
LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(command.getTransactionProcessingStrategyId());
|
||||
|
||||
Loan loan = Loan.createNew(fund, loanTransactionProcessingStrategy, loanProduct, client, loanRepaymentScheduleDetail);
|
||||
loan.setExternalId(command.getExternalId());
|
||||
|
||||
for (ScheduledLoanInstallment scheduledLoanInstallment : loanRepaymentSchedule) {
|
||||
@@ -125,4 +133,15 @@ public class LoanAssembler {
|
||||
}
|
||||
return fund;
|
||||
}
|
||||
}
|
||||
|
||||
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
|
||||
LoanTransactionProcessingStrategy strategy = null;
|
||||
if (transactionProcessingStrategyId != null) {
|
||||
strategy = this.loanTransactionProcessingStrategyRepository.findOne(transactionProcessingStrategyId);
|
||||
if (strategy == null) {
|
||||
throw new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId);
|
||||
}
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -85,14 +85,14 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
}
|
||||
|
||||
private LoanTransactionProcessingStrategy findStrategyByIdIfProvided(final Long transactionProcessingStrategyId) {
|
||||
LoanTransactionProcessingStrategy fund = null;
|
||||
LoanTransactionProcessingStrategy strategy = null;
|
||||
if (transactionProcessingStrategyId != null) {
|
||||
fund = this.loanTransactionProcessingStrategyRepository.findOne(transactionProcessingStrategyId);
|
||||
if (fund == null) {
|
||||
strategy = this.loanTransactionProcessingStrategyRepository.findOne(transactionProcessingStrategyId);
|
||||
if (strategy == null) {
|
||||
throw new LoanTransactionProcessingStrategyNotFoundException(transactionProcessingStrategyId);
|
||||
}
|
||||
}
|
||||
return fund;
|
||||
return strategy;
|
||||
}
|
||||
|
||||
private Fund findFundByIdIfProvided(final Long fundId) {
|
||||
|
||||
Reference in New Issue
Block a user