added borrower cycle to loan products
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE `m_product_loan_variations_borrower_cycle` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_product_id` BIGINT(20) NOT NULL DEFAULT '0',
|
||||
`borrower_cycle_number` INT(3) NOT NULL DEFAULT '0',
|
||||
`value_condition` INT(1) NOT NULL DEFAULT '0',
|
||||
`param_type` INT(1) NOT NULL DEFAULT '0',
|
||||
`default_value` DECIMAL(19,6) NOT NULL DEFAULT '0.000000',
|
||||
`max_value` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
`min_value` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `borrower_cycle_loan_product_FK` (`loan_product_id`),
|
||||
CONSTRAINT `borrower_cycle_loan_product_FK` FOREIGN KEY (`loan_product_id`) REFERENCES `m_product_loan` (`id`)
|
||||
)
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB;
|
||||
|
||||
|
||||
ALTER TABLE `m_product_loan`
|
||||
ADD COLUMN `use_borrower_cycle` TINYINT(1) NOT NULL DEFAULT '0' AFTER `include_in_borrower_cycle`;
|
||||
+37
@@ -364,6 +364,43 @@ public class DataValidatorBuilder {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataValidatorBuilder integerGreaterThanNumber(Integer number) {
|
||||
if (this.value == null && this.ignoreNullValue) { return this; }
|
||||
|
||||
if (this.value != null) {
|
||||
final Integer intValue = Integer.valueOf(this.value.toString());
|
||||
if (intValue < number+1) {
|
||||
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
|
||||
.append(this.parameter).append(".not.greater.than.specified.number");
|
||||
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append(
|
||||
" must be greater than ").append(number);
|
||||
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
||||
defaultEnglishMessage.toString(), this.parameter, intValue, number);
|
||||
this.dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataValidatorBuilder integerSameAsNumber(Integer number) {
|
||||
if (this.value == null && this.ignoreNullValue) { return this; }
|
||||
|
||||
if (this.value != null) {
|
||||
final Integer intValue = Integer.valueOf(this.value.toString());
|
||||
if (intValue != number) {
|
||||
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
|
||||
.append(this.parameter).append(".not.equal.to.specified.number");
|
||||
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter ").append(this.parameter).append(
|
||||
" must be same as").append(number);
|
||||
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(),
|
||||
defaultEnglishMessage.toString(), this.parameter, intValue, number);
|
||||
this.dataValidationErrors.add(error);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* should be used with .notNull() before it
|
||||
|
||||
+1
-1
@@ -190,7 +190,7 @@ public class LoansApiResource {
|
||||
Long officeId = null;
|
||||
|
||||
if (productId != null) {
|
||||
newLoanAccount = this.loanReadPlatformService.retrieveLoanProductDetailsTemplate(productId);
|
||||
newLoanAccount = this.loanReadPlatformService.retrieveLoanProductDetailsTemplate(productId, clientId, groupId);
|
||||
}
|
||||
|
||||
if (templateType == null) {
|
||||
|
||||
+75
-17
@@ -29,8 +29,10 @@ import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
import org.mifosplatform.portfolio.loanaccount.guarantor.data.GuarantorData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.note.data.NoteData;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -449,7 +451,8 @@ public class LoanAccountData {
|
||||
final Collection<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<EnumOptionData> amortizationTypeOptions,
|
||||
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 Collection<CodeValueData> loanPurposeOptions, final Collection<CodeValueData> loanCollateralOptions,
|
||||
final Integer loanCycleNumber) {
|
||||
|
||||
final Long id = null;
|
||||
final String accountNo = null;
|
||||
@@ -468,16 +471,16 @@ public class LoanAccountData {
|
||||
final Long loanOfficerId = null;
|
||||
final String loanOfficerName = null;
|
||||
final CurrencyData currencyData = null;
|
||||
final BigDecimal principal = null;
|
||||
|
||||
final BigDecimal totalOverpaid = null;
|
||||
final BigDecimal inArrearsTolerance = null;
|
||||
final Integer numberOfRepayments = null;
|
||||
|
||||
final Integer repaymentEvery = null;
|
||||
final EnumOptionData repaymentFrequencyType = null;
|
||||
final Long transactionProcessingStrategyId = null;
|
||||
final String transactionProcessingStrategyName = null;
|
||||
final EnumOptionData amortizationType = null;
|
||||
final BigDecimal interestRatePerPeriod = null;
|
||||
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final EnumOptionData interestType = null;
|
||||
@@ -500,7 +503,6 @@ public class LoanAccountData {
|
||||
final Collection<CalendarData> calendarOptions = null;
|
||||
final Collection<StaffData> loanOfficerOptions = null;
|
||||
|
||||
final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery();
|
||||
final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType();
|
||||
|
||||
final Collection<LoanChargeData> charges = new ArrayList<LoanChargeData>();
|
||||
@@ -512,21 +514,50 @@ public class LoanAccountData {
|
||||
final Integer loanProductCounter = null;
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
final Collection<PortfolioAccountData> accountLinkingOptions = null;
|
||||
BigDecimal principal = null;
|
||||
BigDecimal interestRatePerPeriod = null;
|
||||
Integer numberOfRepayments = null;
|
||||
if (product.useBorrowerCycle() && loanCycleNumber > 0) {
|
||||
Collection<LoanProductBorrowerCycleVariationData> principalVariationsForBorrowerCycle = product
|
||||
.getPrincipalVariationsForBorrowerCycle();
|
||||
Collection<LoanProductBorrowerCycleVariationData> interestForVariationsForBorrowerCycle = product
|
||||
.getInterestRateVariationsForBorrowerCycle();
|
||||
Collection<LoanProductBorrowerCycleVariationData> repaymentVariationsForBorrowerCycle = product
|
||||
.getNumberOfRepaymentVariationsForBorrowerCycle();
|
||||
principal = fetchLoanCycleDefaultValue(principalVariationsForBorrowerCycle, loanCycleNumber);
|
||||
interestRatePerPeriod = fetchLoanCycleDefaultValue(interestForVariationsForBorrowerCycle, loanCycleNumber);
|
||||
BigDecimal numberofRepaymentval = fetchLoanCycleDefaultValue(repaymentVariationsForBorrowerCycle, loanCycleNumber);
|
||||
if (numberofRepaymentval != null) {
|
||||
numberOfRepayments = numberofRepaymentval.intValue();
|
||||
}
|
||||
}
|
||||
if (principal == null) {
|
||||
principal = product.getPrincipal();
|
||||
}
|
||||
if (interestRatePerPeriod == null) {
|
||||
interestRatePerPeriod = product.getInterestRatePerPeriod();
|
||||
}
|
||||
if (numberOfRepayments == null) {
|
||||
numberOfRepayments = product.getNumberOfRepayments();
|
||||
}
|
||||
|
||||
final Integer termFrequency = numberOfRepayments * product.getRepaymentEvery();
|
||||
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
|
||||
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), product.getPrincipal(), totalOverpaid,
|
||||
product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(),
|
||||
product.getRepaymentEvery(), product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(),
|
||||
transactionProcessingStrategyName, product.getAmortizationType(), product.getInterestRatePerPeriod(),
|
||||
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
|
||||
product.getInterestCalculationPeriodType(), expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(),
|
||||
product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary,
|
||||
feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions,
|
||||
interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
|
||||
fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions,
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount);
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), principal, totalOverpaid,
|
||||
product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, numberOfRepayments, product.getRepaymentEvery(),
|
||||
product.getRepaymentFrequencyType(), product.getTransactionProcessingStrategyId(), transactionProcessingStrategyName,
|
||||
product.getAmortizationType(), interestRatePerPeriod, product.getInterestRateFrequencyType(),
|
||||
product.getAnnualInterestRate(), product.getInterestType(), product.getInterestCalculationPeriodType(),
|
||||
expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(), product.getGraceOnInterestPayment(),
|
||||
product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged,
|
||||
repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions,
|
||||
repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions,
|
||||
interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions,
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -895,4 +926,31 @@ public class LoanAccountData {
|
||||
public Long clientId() {
|
||||
return this.clientId;
|
||||
}
|
||||
|
||||
private static BigDecimal fetchLoanCycleDefaultValue(Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationData,
|
||||
Integer loanCycleNumber) {
|
||||
BigDecimal defaultValue = null;
|
||||
Integer cycleNumberSelected = 0;
|
||||
for (LoanProductBorrowerCycleVariationData data : borrowerCycleVariationData) {
|
||||
if (isLoanCycleValuesWhenConditionEqual(loanCycleNumber, data)
|
||||
|| isLoanCycleValuesWhenConditionGreterthan(loanCycleNumber, cycleNumberSelected, data)) {
|
||||
cycleNumberSelected = data.getBorrowerCycleNumber();
|
||||
defaultValue = data.getDefaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private static boolean isLoanCycleValuesWhenConditionGreterthan(Integer loanCycleNumber, Integer cycleNumberSelected,
|
||||
LoanProductBorrowerCycleVariationData data) {
|
||||
return data.getBorrowerCycleNumber() < loanCycleNumber
|
||||
&& data.getValueConditionType().equals(LoanProductValueConditionType.GRETERTHAN)
|
||||
&& cycleNumberSelected < data.getBorrowerCycleNumber();
|
||||
}
|
||||
|
||||
private static boolean isLoanCycleValuesWhenConditionEqual(Integer loanCycleNumber, LoanProductBorrowerCycleVariationData data) {
|
||||
return data.getBorrowerCycleNumber() == loanCycleNumber && data.getValueConditionType().equals(LoanProductValueConditionType.EQUAL);
|
||||
}
|
||||
|
||||
}
|
||||
+39
-5
@@ -27,6 +27,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.account.domain.AccountAssociations;
|
||||
import org.mifosplatform.portfolio.account.domain.AccountAssociationsRepository;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
@@ -112,6 +113,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
private final SavingsAccountAssembler savingsAccountAssembler;
|
||||
private final AccountAssociationsRepository accountAssociationsRepository;
|
||||
private final LoanChargeRepository loanChargeRepository;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper,
|
||||
@@ -126,7 +128,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository,
|
||||
final LoanChargeRepository loanChargeRepository) {
|
||||
final LoanChargeRepository loanChargeRepository, final LoanReadPlatformService loanReadPlatformService) {
|
||||
this.context = context;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator;
|
||||
@@ -150,6 +152,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
this.savingsAccountAssembler = savingsAccountAssembler;
|
||||
this.accountAssociationsRepository = accountAssociationsRepository;
|
||||
this.loanChargeRepository = loanChargeRepository;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -172,9 +175,21 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final Long productId = this.fromJsonHelper.extractLongNamed("productId", command.parsedJson());
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, loanProduct);
|
||||
|
||||
if (loanProduct.useBorrowerCycle()) {
|
||||
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", command.parsedJson());
|
||||
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", command.parsedJson());
|
||||
Integer cycleNumber = 0;
|
||||
if (clientId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId);
|
||||
} else if (groupId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue());
|
||||
}
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
loanProduct, cycleNumber);
|
||||
} else {
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
loanProduct);
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
final Loan newLoanApplication = this.loanAssembler.assembleFrom(command, currentUser);
|
||||
@@ -292,6 +307,24 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
if (!changes.containsKey("interestRateFrequencyType")) {
|
||||
existingLoanApplication.updateInterestRateFrequencyType();
|
||||
}
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
if (loanProduct.useBorrowerCycle()) {
|
||||
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", command.parsedJson());
|
||||
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", command.parsedJson());
|
||||
Integer cycleNumber = 0;
|
||||
if (clientId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId);
|
||||
} else if (groupId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue());
|
||||
}
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
loanProduct, cycleNumber);
|
||||
} else {
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
loanProduct);
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
validateSubmittedOnDate(existingLoanApplication);
|
||||
@@ -362,7 +395,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
|
||||
for (final LoanCharge loanCharge : existingLoanApplication.charges()) {
|
||||
if (loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()) {
|
||||
final Set<LoanInstallmentCharge> chargePerInstallments = existingLoanApplication.generateInstallmentLoanCharges(loanCharge);
|
||||
final Set<LoanInstallmentCharge> chargePerInstallments = existingLoanApplication
|
||||
.generateInstallmentLoanCharges(loanCharge);
|
||||
loanCharge.addLoanInstallmentCharges(chargePerInstallments);
|
||||
this.loanChargeRepository.save(loanCharge);
|
||||
}
|
||||
|
||||
+5
-1
@@ -41,7 +41,7 @@ public interface LoanReadPlatformService {
|
||||
|
||||
LoanAccountData retrieveTemplateWithCompleteGroupAndProductDetails(Long groupId, Long productId);
|
||||
|
||||
LoanAccountData retrieveLoanProductDetailsTemplate(Long productId);
|
||||
LoanAccountData retrieveLoanProductDetailsTemplate(Long productId, Long clientId, Long groupId);
|
||||
|
||||
LoanAccountData retrieveClientDetailsTemplate(Long clientId);
|
||||
|
||||
@@ -61,4 +61,8 @@ public interface LoanReadPlatformService {
|
||||
* The 'overdue-charge' is only ever applied once to an installment and as a result overdue installments with this charge already applied are not returned.
|
||||
*/
|
||||
Collection<OverdueLoanScheduleData> retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized();
|
||||
|
||||
Integer retriveLoanCounter(Long groupId, Integer loanType);
|
||||
|
||||
Integer retriveLoanCounter(Long clientId);
|
||||
}
|
||||
+24
-3
@@ -32,6 +32,7 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
|
||||
import org.mifosplatform.portfolio.account.data.AccountTransferData;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations;
|
||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
@@ -1039,7 +1040,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId) {
|
||||
public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId, Long clientId, Long groupId) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
@@ -1063,10 +1064,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
.retrieveCodeValuesByCode("LoanCollateral");
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
|
||||
|
||||
Integer loanCycleCounter = null;
|
||||
if(loanProduct.useBorrowerCycle()){
|
||||
if(clientId == null){
|
||||
loanCycleCounter = retriveLoanCounter(groupId,AccountType.GROUP.getValue());
|
||||
}else{
|
||||
loanCycleCounter = retriveLoanCounter(clientId);
|
||||
}
|
||||
}
|
||||
return LoanAccountData.loanProductWithTemplateDefaults(loanProduct, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions);
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, loanPurposeOptions, loanCollateralOptions, loanCycleCounter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1150,4 +1158,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " and plc.charge_id not in (SELECT charge_id from m_loan_charge as mlc where mlc.loan_id = ml.id and mlc.charge_id = plc.charge_id and mlc.due_for_collection_as_of_date > fromdate AND mlc.due_for_collection_as_of_date <= duedate) ";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer retriveLoanCounter(Long groupId,Integer loanType){
|
||||
final String sql = "Select MAX(l.loan_counter) from m_loan l where l.group_id = ? "
|
||||
+ " and l.loan_type_enum = ?";
|
||||
return this.jdbcTemplate.queryForInt(sql,groupId,loanType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer retriveLoanCounter(Long clientId){
|
||||
final String sql = "Select MAX(l.loan_counter) from m_loan l where l.client_id = ?";
|
||||
return this.jdbcTemplate.queryForInt(sql,clientId);
|
||||
}
|
||||
}
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
package org.mifosplatform.portfolio.loanproduct;
|
||||
|
||||
|
||||
public interface LoanProductConstants {
|
||||
|
||||
public static final String useBorrowerCycleParameterName = "useBorrowerCycle";
|
||||
|
||||
public static final String principalVariationsForBorrowerCycleParameterName = "principalVariationsForBorrowerCycle";
|
||||
public static final String interestRateVariationsForBorrowerCycleParameterName = "interestRateVariationsForBorrowerCycle";
|
||||
public static final String numberOfRepaymentVariationsForBorrowerCycleParameterName = "numberOfRepaymentVariationsForBorrowerCycle";
|
||||
|
||||
public static final String defaultValueParameterName = "defaultValue";
|
||||
public static final String minValueParameterName = "minValue";
|
||||
public static final String maxValueParameterName = "maxValue";
|
||||
public static final String valueConditionTypeParamName = "valueConditionType";
|
||||
public static final String borrowerCycleNumberParamName = "borrowerCycleNumber";
|
||||
public static final String borrowerCycleIdParameterName = "id";
|
||||
|
||||
|
||||
public static final String principalPerCycleParameterName = "principalPerCycle";
|
||||
public static final String minPrincipalPerCycleParameterName = "minPrincipalPerCycle";
|
||||
public static final String maxPrincipalPerCycleParameterName = "maxPrincipalPerCycle";
|
||||
public static final String principalValueUsageConditionParamName = "principalValueUsageCondition";
|
||||
public static final String principalCycleNumbersParamName = "principalCycleNumbers";
|
||||
|
||||
public static final String numberOfRepaymentsPerCycleParameterName = "numberOfRepaymentsPerCycle";
|
||||
public static final String minNumberOfRepaymentsPerCycleParameterName = "minNumberOfRepaymentsPerCycle";
|
||||
public static final String maxNumberOfRepaymentsPerCycleParameterName = "maxNumberOfRepaymentsPerCycle";
|
||||
public static final String repaymentValueUsageConditionParamName = "repaymentValueUsageCondition";
|
||||
public static final String repaymentCycleNumberParamName = "repaymentCycleNumber";
|
||||
|
||||
public static final String interestRatePerPeriodPerCycleParameterName = "interestRatePerPeriodPerCycle";
|
||||
public static final String minInterestRatePerPeriodPerCycleParameterName = "minInterestRatePerPeriodPerCycle";
|
||||
public static final String maxInterestRatePerPeriodPerCycleParameterName = "maxInterestRatePerPeriodPerCycle";
|
||||
public static final String interestRateValueUsageConditionParamName = "interestRateValueUsageCondition";
|
||||
public static final String interestRateCycleNumberParamName = "interestRateCycleNumber";
|
||||
|
||||
public static final String principal = "principal";
|
||||
public static final String minPrincipal = "minPrincipal";
|
||||
public static final String maxPrincipal = "maxPrincipalValue";
|
||||
|
||||
public static final String interestRatePerPeriod = "interestRatePerPeriod";
|
||||
public static final String minInterestRatePerPeriod = "minInterestRatePerPeriod";
|
||||
public static final String maxInterestRatePerPeriod = "maxInterestRatePerPeriod";
|
||||
|
||||
public static final String numberOfRepayments = "numberOfRepayments";
|
||||
public static final String minNumberOfRepayments = "minNumberOfRepayments";
|
||||
public static final String maxNumberOfRepayments = "maxNumberOfRepayments";
|
||||
|
||||
public static final String VALUE_CONDITION_TYPE_ERROR = "condition.type.must.end.with.greterthan";
|
||||
}
|
||||
+3
-1
@@ -264,11 +264,13 @@ public class LoanProductsApiResource {
|
||||
|
||||
final List<EnumOptionData> accountingRuleTypeOptions = this.accountingDropdownReadPlatformService
|
||||
.retrieveAccountingRuleTypeOptions();
|
||||
|
||||
final List<EnumOptionData> loanCycleValueConditionTypeOptions = this.dropdownReadPlatformService.retrieveLoanCycleValueConditionTypeOptions();
|
||||
|
||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions,
|
||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions,
|
||||
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions,
|
||||
accountingRuleTypeOptions);
|
||||
accountingRuleTypeOptions,loanCycleValueConditionTypeOptions);
|
||||
}
|
||||
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package org.mifosplatform.portfolio.loanproduct.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
|
||||
|
||||
public class LoanProductBorrowerCycleVariationData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
private final Integer borrowerCycleNumber;
|
||||
private final EnumOptionData paramType;
|
||||
private final EnumOptionData valueConditionType;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal minValue;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal maxValue;
|
||||
private final BigDecimal defaultValue;
|
||||
|
||||
|
||||
public LoanProductBorrowerCycleVariationData(final Long id,final Integer borrowerCycleNumber,final EnumOptionData paramType,final EnumOptionData valueConditionType,final BigDecimal defaultValue,final BigDecimal minValue,
|
||||
final BigDecimal maxValue) {
|
||||
this.id = id;
|
||||
this.borrowerCycleNumber = borrowerCycleNumber;
|
||||
this.paramType = paramType;
|
||||
this.valueConditionType = valueConditionType;;
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public LoanProductParamType getParamType(){
|
||||
return LoanProductParamType.fromInt(this.paramType.getId().intValue());
|
||||
}
|
||||
|
||||
|
||||
public Integer getBorrowerCycleNumber() {
|
||||
return this.borrowerCycleNumber;
|
||||
}
|
||||
|
||||
|
||||
public LoanProductValueConditionType getValueConditionType() {
|
||||
return LoanProductValueConditionType.fromInt(this.valueConditionType.getId().intValue());
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
+62
-7
@@ -39,6 +39,7 @@ public class LoanProductData {
|
||||
private final Long fundId;
|
||||
private final String fundName;
|
||||
private final boolean includeInBorrowerCycle;
|
||||
private final boolean useBorrowerCycle;
|
||||
private final LocalDate startDate;
|
||||
private final LocalDate closeDate;
|
||||
private final String status;
|
||||
@@ -72,7 +73,10 @@ public class LoanProductData {
|
||||
|
||||
// charges
|
||||
private final Collection<ChargeData> charges;
|
||||
|
||||
|
||||
private final Collection<LoanProductBorrowerCycleVariationData> principalVariationsForBorrowerCycle;
|
||||
private final Collection<LoanProductBorrowerCycleVariationData> interestRateVariationsForBorrowerCycle;
|
||||
private final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle;
|
||||
// accounting
|
||||
private final EnumOptionData accountingRule;
|
||||
private Map<String, Object> accountingMappings;
|
||||
@@ -99,7 +103,8 @@ public class LoanProductData {
|
||||
private final List<EnumOptionData> accountingRuleOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Map<String, List<GLAccountData>> accountingMappingOptions;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final List<EnumOptionData> valueConditionTypeOptions;
|
||||
/**
|
||||
* Used when returning lookup information about loan product for dropdowns.
|
||||
*/
|
||||
@@ -131,8 +136,12 @@ public class LoanProductData {
|
||||
final Integer graceOnInterestPayment = null;
|
||||
final Integer graceOnInterestCharged = null;
|
||||
final Collection<ChargeData> charges = null;
|
||||
final Collection<LoanProductBorrowerCycleVariationData> principalVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> interestRateVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final EnumOptionData accountingType = null;
|
||||
final boolean includeInBorrowerCycle = false;
|
||||
final boolean useBorrowerCycle = false;
|
||||
final LocalDate startDate = null;
|
||||
final LocalDate closeDate = null;
|
||||
final String status = null;
|
||||
@@ -142,7 +151,8 @@ public class LoanProductData {
|
||||
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
|
||||
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
|
||||
transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
|
||||
accountingType, includeInBorrowerCycle, startDate, closeDate, status, externalId);
|
||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
||||
interestRateVariations, numberOfRepaymentVariations);
|
||||
}
|
||||
|
||||
public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) {
|
||||
@@ -175,17 +185,23 @@ public class LoanProductData {
|
||||
final Collection<ChargeData> charges = null;
|
||||
final EnumOptionData accountingType = null;
|
||||
final boolean includeInBorrowerCycle = false;
|
||||
final boolean useBorrowerCycle = false;
|
||||
final LocalDate startDate = null;
|
||||
final LocalDate closeDate = null;
|
||||
final String status = null;
|
||||
final String externalId = null;
|
||||
|
||||
final Collection<LoanProductBorrowerCycleVariationData> principalVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> interestRateVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
|
||||
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance, numberOfRepayments,
|
||||
minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod, minInterestRatePerPeriod,
|
||||
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
|
||||
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
|
||||
transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
|
||||
accountingType, includeInBorrowerCycle, startDate, closeDate, status, externalId);
|
||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariations,
|
||||
interestRateVariations, numberOfRepaymentVariations);
|
||||
}
|
||||
|
||||
public static LoanProductData sensibleDefaultsForNewLoanProductCreation() {
|
||||
@@ -222,8 +238,13 @@ public class LoanProductData {
|
||||
final Integer graceOnInterestCharged = null;
|
||||
|
||||
final Collection<ChargeData> charges = null;
|
||||
final Collection<LoanProductBorrowerCycleVariationData> principalVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> interestRateVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>(1);
|
||||
|
||||
final EnumOptionData accountingType = AccountingEnumerations.accountingRuleType(AccountingRuleType.NONE);
|
||||
final boolean includeInBorrowerCycle = false;
|
||||
final boolean useBorrowerCycle = false;
|
||||
final LocalDate startDate = null;
|
||||
final LocalDate closeDate = null;
|
||||
final String status = null;
|
||||
@@ -234,7 +255,8 @@ public class LoanProductData {
|
||||
maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType, interestRateFrequencyType, amortizationType,
|
||||
interestType, interestCalculationPeriodType, fundId, fundName, transactionProcessingStrategyId,
|
||||
transactionProcessingStrategyName, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges,
|
||||
accountingType, includeInBorrowerCycle, startDate, closeDate, status, externalId);
|
||||
accountingType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate, status, externalId, principalVariationsForBorrowerCycle,
|
||||
interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle);
|
||||
}
|
||||
|
||||
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
|
||||
@@ -258,7 +280,9 @@ public class LoanProductData {
|
||||
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
|
||||
final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
|
||||
final Collection<ChargeData> charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle,
|
||||
final LocalDate startDate, final LocalDate closeDate, final String status, final String externalId) {
|
||||
boolean useBorrowerCycle, final LocalDate startDate, final LocalDate closeDate, final String status, final String externalId,
|
||||
Collection<LoanProductBorrowerCycleVariationData> principalVariations, Collection<LoanProductBorrowerCycleVariationData> interestRateVariations,
|
||||
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariations) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
@@ -290,6 +314,7 @@ public class LoanProductData {
|
||||
this.charges = charges;
|
||||
this.accountingRule = accountingType;
|
||||
this.includeInBorrowerCycle = includeInBorrowerCycle;
|
||||
this.useBorrowerCycle = useBorrowerCycle;
|
||||
this.startDate = startDate;
|
||||
this.closeDate = closeDate;
|
||||
this.status = status;
|
||||
@@ -313,6 +338,10 @@ public class LoanProductData {
|
||||
this.paymentChannelToFundSourceMappings = null;
|
||||
this.feeToIncomeAccountMappings = null;
|
||||
this.penaltyToIncomeAccountMappings = null;
|
||||
this.valueConditionTypeOptions = null;
|
||||
this.principalVariationsForBorrowerCycle = principalVariations;
|
||||
this.interestRateVariationsForBorrowerCycle = interestRateVariations;
|
||||
this.numberOfRepaymentVariationsForBorrowerCycle = numberOfRepaymentVariations;
|
||||
}
|
||||
|
||||
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
|
||||
@@ -321,7 +350,8 @@ public class LoanProductData {
|
||||
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
|
||||
final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
|
||||
final Collection<FundData> fundOptions, final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions) {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
|
||||
final List<EnumOptionData> valueConditionTypeOptions) {
|
||||
this.id = productData.id;
|
||||
this.name = productData.name;
|
||||
this.description = productData.description;
|
||||
@@ -351,6 +381,9 @@ public class LoanProductData {
|
||||
this.externalId = productData.externalId;
|
||||
|
||||
this.charges = nullIfEmpty(productData.charges());
|
||||
this.principalVariationsForBorrowerCycle = productData.principalVariationsForBorrowerCycle;
|
||||
this.interestRateVariationsForBorrowerCycle = productData.interestRateVariationsForBorrowerCycle;
|
||||
this.numberOfRepaymentVariationsForBorrowerCycle = productData.numberOfRepaymentVariationsForBorrowerCycle;
|
||||
this.accountingRule = productData.accountingRule;
|
||||
this.accountingMappings = productData.accountingMappings;
|
||||
this.paymentChannelToFundSourceMappings = productData.paymentChannelToFundSourceMappings;
|
||||
@@ -379,6 +412,7 @@ public class LoanProductData {
|
||||
this.graceOnInterestPayment = productData.graceOnInterestPayment;
|
||||
this.graceOnInterestCharged = productData.graceOnInterestCharged;
|
||||
this.includeInBorrowerCycle = productData.includeInBorrowerCycle;
|
||||
this.useBorrowerCycle = productData.useBorrowerCycle;
|
||||
|
||||
this.amortizationTypeOptions = amortizationTypeOptions;
|
||||
this.interestTypeOptions = interestTypeOptions;
|
||||
@@ -388,6 +422,7 @@ public class LoanProductData {
|
||||
|
||||
this.accountingMappingOptions = accountingMappingOptions;
|
||||
this.accountingRuleOptions = accountingRuleOptions;
|
||||
this.valueConditionTypeOptions = valueConditionTypeOptions;
|
||||
|
||||
}
|
||||
|
||||
@@ -549,4 +584,24 @@ public class LoanProductData {
|
||||
public int hashCode() {
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
|
||||
public boolean useBorrowerCycle() {
|
||||
return this.useBorrowerCycle;
|
||||
}
|
||||
|
||||
|
||||
public Collection<LoanProductBorrowerCycleVariationData> getPrincipalVariationsForBorrowerCycle() {
|
||||
return this.principalVariationsForBorrowerCycle;
|
||||
}
|
||||
|
||||
|
||||
public Collection<LoanProductBorrowerCycleVariationData> getInterestRateVariationsForBorrowerCycle() {
|
||||
return this.interestRateVariationsForBorrowerCycle;
|
||||
}
|
||||
|
||||
|
||||
public Collection<LoanProductBorrowerCycleVariationData> getNumberOfRepaymentVariationsForBorrowerCycle() {
|
||||
return this.numberOfRepaymentVariationsForBorrowerCycle;
|
||||
}
|
||||
}
|
||||
+310
-6
@@ -6,12 +6,16 @@
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
@@ -19,12 +23,15 @@ import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.accounting.common.AccountingRuleType;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
@@ -33,17 +40,19 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Loan products allow for categorisation of an organisations loans into
|
||||
* something meaningful to them.
|
||||
*
|
||||
*
|
||||
* They provide a means of simplifying creation/maintenance of loans. They can
|
||||
* also allow for product comparison to take place when reporting.
|
||||
*
|
||||
*
|
||||
* They allow for constraints to be added at product level.
|
||||
*/
|
||||
@Entity
|
||||
@@ -81,6 +90,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "include_in_borrower_cycle")
|
||||
private boolean includeInBorrowerCycle;
|
||||
|
||||
@Column(name = "use_borrower_cycle")
|
||||
private boolean useBorrowerCycle;
|
||||
|
||||
@Column(name = "start_date", nullable = true)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date startDate;
|
||||
@@ -92,6 +104,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "external_id", length = 100, nullable = true, unique = true)
|
||||
private String externalId;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanProduct", orphanRemoval = true)
|
||||
private Set<LoanProductBorrowerCycleVariations> borrowerCycleVariations = new HashSet<LoanProductBorrowerCycleVariations>();
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
|
||||
@@ -137,12 +153,186 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate");
|
||||
final String externalId = command.stringValueOfParameterNamedAllowingNull("externalId");
|
||||
|
||||
final boolean useBorrowerCycle = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.useBorrowerCycleParameterName);
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations = new HashSet<LoanProductBorrowerCycleVariations>();
|
||||
|
||||
if (useBorrowerCycle) {
|
||||
populateBorrowerCyclevariations(command, loanProductBorrowerCycleVariations);
|
||||
}
|
||||
|
||||
return new LoanProduct(fund, loanTransactionProcessingStrategy, name, description, currency, principal, minPrincipal, maxPrincipal,
|
||||
interestRatePerPeriod, minInterestRatePerPeriod, maxInterestRatePerPeriod, interestFrequencyType, annualInterestRate,
|
||||
interestMethod, interestCalculationPeriodMethod, repaymentEvery, repaymentFrequencyType, numberOfRepayments,
|
||||
minNumberOfRepayments, maxNumberOfRepayments, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate,
|
||||
externalId);
|
||||
externalId, useBorrowerCycle, loanProductBorrowerCycleVariations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command
|
||||
* @param loanProductBorrowerCycleVariations
|
||||
*/
|
||||
private static void populateBorrowerCyclevariations(final JsonCommand command,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations) {
|
||||
assemblePrincipalVariations(command, loanProductBorrowerCycleVariations);
|
||||
|
||||
assembleRepaymentVariations(command, loanProductBorrowerCycleVariations);
|
||||
|
||||
assembleInterestRateVariations(command, loanProductBorrowerCycleVariations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command
|
||||
* @param loanProductBorrowerCycleVariations
|
||||
*/
|
||||
private static void assembleInterestRateVariations(final JsonCommand command,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations) {
|
||||
assembleVaritions(command, loanProductBorrowerCycleVariations, LoanProductParamType.INTERESTRATE.getValue(),
|
||||
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command
|
||||
* @param loanProductBorrowerCycleVariations
|
||||
*/
|
||||
private static void assembleRepaymentVariations(final JsonCommand command,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations) {
|
||||
assembleVaritions(command, loanProductBorrowerCycleVariations, LoanProductParamType.REPAYMENT.getValue(),
|
||||
LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command
|
||||
* @param loanProductBorrowerCycleVariations
|
||||
*/
|
||||
private static void assemblePrincipalVariations(final JsonCommand command,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations) {
|
||||
assembleVaritions(command, loanProductBorrowerCycleVariations, LoanProductParamType.PRINCIPAL.getValue(),
|
||||
LoanProductConstants.principalVariationsForBorrowerCycleParameterName);
|
||||
}
|
||||
|
||||
private static void assembleVaritions(final JsonCommand command,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations,Integer paramType,
|
||||
String variationParameterName){
|
||||
if (command.parameterExists(variationParameterName)) {
|
||||
final JsonArray variationArray= command.arrayOfParameterNamed(variationParameterName);
|
||||
if (variationArray != null && variationArray.size() > 0) {
|
||||
int i = 0;
|
||||
do {
|
||||
final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
|
||||
BigDecimal defaultValue = null;
|
||||
BigDecimal minValue = null;
|
||||
BigDecimal maxValue = null;
|
||||
Integer cycleNumber = null;
|
||||
Integer valueUsageCondition = null;
|
||||
if (jsonObject.has(LoanProductConstants.defaultValueParameterName) && jsonObject.get(LoanProductConstants.defaultValueParameterName).isJsonPrimitive()) {
|
||||
defaultValue = jsonObject.getAsJsonPrimitive(LoanProductConstants.defaultValueParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.minValueParameterName) && jsonObject.get(LoanProductConstants.minValueParameterName).isJsonPrimitive()) {
|
||||
minValue = jsonObject.getAsJsonPrimitive(LoanProductConstants.minValueParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.maxValueParameterName) && jsonObject.get(LoanProductConstants.maxValueParameterName).isJsonPrimitive()) {
|
||||
maxValue = jsonObject.getAsJsonPrimitive(LoanProductConstants.maxValueParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.borrowerCycleNumberParamName) && jsonObject.get(LoanProductConstants.borrowerCycleNumberParamName).isJsonPrimitive()) {
|
||||
cycleNumber = jsonObject.getAsJsonPrimitive(LoanProductConstants.borrowerCycleNumberParamName).getAsInt();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.valueConditionTypeParamName) && jsonObject.get(LoanProductConstants.valueConditionTypeParamName).isJsonPrimitive()) {
|
||||
valueUsageCondition = jsonObject.getAsJsonPrimitive(LoanProductConstants.valueConditionTypeParamName).getAsInt();
|
||||
}
|
||||
LoanProductBorrowerCycleVariations borrowerCycleVariations= new LoanProductBorrowerCycleVariations(cycleNumber, paramType, valueUsageCondition, minValue, maxValue, defaultValue);
|
||||
loanProductBorrowerCycleVariations.add(borrowerCycleVariations);
|
||||
i++;
|
||||
}while (i < variationArray.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> updateBorrowerCycleVariations(final JsonCommand command) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(20);
|
||||
List<Long> variationIds = fetchAllVariationIds();
|
||||
updateBorrowerCycleVaritions(command, LoanProductParamType.PRINCIPAL.getValue(), LoanProductConstants.principalVariationsForBorrowerCycleParameterName, actualChanges, variationIds);
|
||||
updateBorrowerCycleVaritions(command, LoanProductParamType.INTERESTRATE.getValue(), LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName, actualChanges, variationIds);
|
||||
updateBorrowerCycleVaritions(command, LoanProductParamType.REPAYMENT.getValue(), LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, actualChanges, variationIds);
|
||||
for(Long id:variationIds){
|
||||
this.borrowerCycleVariations.remove(fetchLoanProductBorrowerCycleVariationById(id));
|
||||
}
|
||||
return actualChanges;
|
||||
}
|
||||
private List<Long> fetchAllVariationIds(){
|
||||
List<Long> list = new ArrayList<Long>();
|
||||
for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) {
|
||||
list.add(cycleVariation.getId());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void updateBorrowerCycleVaritions(final JsonCommand command,Integer paramType,
|
||||
String variationParameterName,final Map<String, Object> actualChanges, List<Long> variationIds){
|
||||
if (command.parameterExists(variationParameterName)) {
|
||||
final JsonArray variationArray= command.arrayOfParameterNamed(variationParameterName);
|
||||
if (variationArray != null && variationArray.size() > 0) {
|
||||
int i = 0;
|
||||
do {
|
||||
final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
|
||||
BigDecimal defaultValue = null;
|
||||
BigDecimal minValue = null;
|
||||
BigDecimal maxValue = null;
|
||||
Integer cycleNumber = null;
|
||||
Integer valueUsageCondition = null;
|
||||
Long id = null;
|
||||
if (jsonObject.has(LoanProductConstants.defaultValueParameterName) && jsonObject.get(LoanProductConstants.defaultValueParameterName).isJsonPrimitive()) {
|
||||
defaultValue = jsonObject.getAsJsonPrimitive(LoanProductConstants.defaultValueParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.minValueParameterName) && jsonObject.get(LoanProductConstants.minValueParameterName).isJsonPrimitive()) {
|
||||
minValue = jsonObject.getAsJsonPrimitive(LoanProductConstants.minValueParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.maxValueParameterName) && jsonObject.get(LoanProductConstants.maxValueParameterName).isJsonPrimitive()) {
|
||||
maxValue = jsonObject.getAsJsonPrimitive(LoanProductConstants.maxValueParameterName).getAsBigDecimal();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.borrowerCycleNumberParamName) && jsonObject.get(LoanProductConstants.borrowerCycleNumberParamName).isJsonPrimitive()) {
|
||||
cycleNumber = jsonObject.getAsJsonPrimitive(LoanProductConstants.borrowerCycleNumberParamName).getAsInt();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.valueConditionTypeParamName) && jsonObject.get(LoanProductConstants.valueConditionTypeParamName).isJsonPrimitive()) {
|
||||
valueUsageCondition = jsonObject.getAsJsonPrimitive(LoanProductConstants.valueConditionTypeParamName).getAsInt();
|
||||
}
|
||||
if (jsonObject.has(LoanProductConstants.borrowerCycleIdParameterName) && jsonObject.get(LoanProductConstants.borrowerCycleIdParameterName).isJsonPrimitive()) {
|
||||
id = jsonObject.getAsJsonPrimitive(LoanProductConstants.borrowerCycleIdParameterName).getAsLong();
|
||||
}
|
||||
LoanProductBorrowerCycleVariations borrowerCycleVariations= new LoanProductBorrowerCycleVariations(cycleNumber, paramType, valueUsageCondition, minValue, maxValue, defaultValue);
|
||||
if(id == null){
|
||||
borrowerCycleVariations.updateLoanProduct(this);
|
||||
this.borrowerCycleVariations.add(borrowerCycleVariations);
|
||||
actualChanges.put("borrowerCycleParamType", paramType);
|
||||
}else{
|
||||
variationIds.remove(id);
|
||||
LoanProductBorrowerCycleVariations existingCycleVariation = fetchLoanProductBorrowerCycleVariationById(id);
|
||||
if(!existingCycleVariation.equals(borrowerCycleVariations)){
|
||||
existingCycleVariation.copy(borrowerCycleVariations);
|
||||
actualChanges.put("borrowerCycleId", id);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}while (i < variationArray.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void clearVariations(LoanProductParamType paramType, boolean clearAll) {
|
||||
if (clearAll) {
|
||||
this.borrowerCycleVariations.clear();
|
||||
} else {
|
||||
Set<LoanProductBorrowerCycleVariations> remove = new HashSet<LoanProductBorrowerCycleVariations>();
|
||||
for (LoanProductBorrowerCycleVariations borrowerCycleVariations : this.borrowerCycleVariations) {
|
||||
if (paramType.equals(borrowerCycleVariations.getParamType())) {
|
||||
remove.add(borrowerCycleVariations);
|
||||
}
|
||||
}
|
||||
this.borrowerCycleVariations.removeAll(remove);
|
||||
}
|
||||
}
|
||||
|
||||
protected LoanProduct() {
|
||||
@@ -162,7 +352,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
final Integer graceOnPrincipalPayment, 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 LocalDate closeDate, final String externalId, final boolean useBorrowerCycle,
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.name = name.trim();
|
||||
@@ -191,6 +382,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
this.accountingRule = accountingRuleType.getValue();
|
||||
}
|
||||
this.includeInBorrowerCycle = includeInBorrowerCycle;
|
||||
this.useBorrowerCycle = useBorrowerCycle;
|
||||
|
||||
if (startDate != null) {
|
||||
this.startDate = startDate.toDateTimeAtStartOfDay().toDate();
|
||||
@@ -201,6 +393,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
this.externalId = externalId;
|
||||
this.borrowerCycleVariations = loanProductBorrowerCycleVariations;
|
||||
for (LoanProductBorrowerCycleVariations borrowerCycleVariations : this.borrowerCycleVariations) {
|
||||
borrowerCycleVariations.updateLoanProduct(this);
|
||||
}
|
||||
}
|
||||
|
||||
public MonetaryCurrency getCurrency() {
|
||||
@@ -303,6 +499,18 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
this.includeInBorrowerCycle = newValue;
|
||||
}
|
||||
|
||||
final String useBorrowerCycleParamName = "useBorrowerCycle";
|
||||
if (command.isChangeInBooleanParameterNamed(useBorrowerCycleParamName, this.includeInBorrowerCycle)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(useBorrowerCycleParamName);
|
||||
actualChanges.put(useBorrowerCycleParamName, newValue);
|
||||
this.useBorrowerCycle = newValue;
|
||||
}
|
||||
|
||||
if (this.useBorrowerCycle) {
|
||||
actualChanges.putAll(updateBorrowerCycleVariations(command));
|
||||
}else{
|
||||
clearVariations(null, true);
|
||||
}
|
||||
final String dateFormatAsInput = command.dateFormat();
|
||||
final String localeAsInput = command.locale();
|
||||
|
||||
@@ -401,8 +609,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
// If all min and max fields are null then loanProductMinMaxConstraints
|
||||
// initialising to null
|
||||
// Reset LoanProductMinMaxConstraints with null values.
|
||||
this.loanProductMinMaxConstraints = this.loanProductMinMaxConstraints == null ? new LoanProductMinMaxConstraints(null, null,
|
||||
null, null, null, null) : this.loanProductMinMaxConstraints;
|
||||
this.loanProductMinMaxConstraints = this.loanProductMinMaxConstraints == null ? new LoanProductMinMaxConstraints(null, null, null,
|
||||
null, null, null) : this.loanProductMinMaxConstraints;
|
||||
return this.loanProductMinMaxConstraints;
|
||||
}
|
||||
|
||||
@@ -434,4 +642,100 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return this.externalId;
|
||||
}
|
||||
|
||||
public boolean useBorrowerCycle() {
|
||||
return this.useBorrowerCycle;
|
||||
}
|
||||
|
||||
public LoanProductBorrowerCycleVariations fetchLoanProductBorrowerCycleVariationById(Long id){
|
||||
LoanProductBorrowerCycleVariations borrowerCycleVariation = null;
|
||||
for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) {
|
||||
if(id.equals(cycleVariation.getId())){
|
||||
borrowerCycleVariation = cycleVariation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return borrowerCycleVariation;
|
||||
}
|
||||
|
||||
public Map<String, BigDecimal> fetchBorrowerCycleVariationsForCycleNumber(final Integer cycleNumber) {
|
||||
Map<String, BigDecimal> borrowerCycleVariations = new HashMap<String, BigDecimal>();
|
||||
borrowerCycleVariations.put(LoanProductConstants.principal, this.loanProductRelatedDetail.getPrincipal().getAmount());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minPrincipal, this.loanProductMinMaxConstraints.getMinPrincipal());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxPrincipal, this.loanProductMinMaxConstraints.getMaxPrincipal());
|
||||
borrowerCycleVariations.put(LoanProductConstants.interestRatePerPeriod,
|
||||
this.loanProductRelatedDetail.getNominalInterestRatePerPeriod());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minInterestRatePerPeriod,
|
||||
this.loanProductMinMaxConstraints.getMinNominalInterestRatePerPeriod());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxInterestRatePerPeriod,
|
||||
this.loanProductMinMaxConstraints.getMaxNominalInterestRatePerPeriod());
|
||||
borrowerCycleVariations.put(LoanProductConstants.numberOfRepayments,
|
||||
BigDecimal.valueOf(this.loanProductRelatedDetail.getNumberOfRepayments()));
|
||||
borrowerCycleVariations.put(LoanProductConstants.minNumberOfRepayments,
|
||||
BigDecimal.valueOf(this.loanProductMinMaxConstraints.getMinNumberOfRepayments()));
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxNumberOfRepayments,
|
||||
BigDecimal.valueOf(this.loanProductMinMaxConstraints.getMaxNumberOfRepayments()));
|
||||
if (cycleNumber > 0) {
|
||||
Integer principalCycleUsed = 0;
|
||||
Integer interestCycleUsed = 0;
|
||||
Integer repaymentCycleUsed = 0;
|
||||
for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) {
|
||||
if (cycleVariation.getBorrowerCycleNumber() == cycleNumber
|
||||
&& cycleVariation.getValueConditionType().equals(LoanProductValueConditionType.EQUAL)) {
|
||||
switch (cycleVariation.getParamType()) {
|
||||
case PRINCIPAL:
|
||||
borrowerCycleVariations.put(LoanProductConstants.principal, cycleVariation.getDefaultValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minPrincipal, cycleVariation.getMinValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxPrincipal, cycleVariation.getMaxValue());
|
||||
principalCycleUsed = cycleVariation.getBorrowerCycleNumber();
|
||||
break;
|
||||
case INTERESTRATE:
|
||||
borrowerCycleVariations.put(LoanProductConstants.interestRatePerPeriod, cycleVariation.getDefaultValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minInterestRatePerPeriod, cycleVariation.getMinValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxInterestRatePerPeriod, cycleVariation.getMaxValue());
|
||||
interestCycleUsed = cycleVariation.getBorrowerCycleNumber();
|
||||
break;
|
||||
case REPAYMENT:
|
||||
borrowerCycleVariations.put(LoanProductConstants.numberOfRepayments, cycleVariation.getDefaultValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minNumberOfRepayments, cycleVariation.getMinValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxNumberOfRepayments, cycleVariation.getMaxValue());
|
||||
repaymentCycleUsed = cycleVariation.getBorrowerCycleNumber();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (cycleVariation.getBorrowerCycleNumber() < cycleNumber
|
||||
&& cycleVariation.getValueConditionType().equals(LoanProductValueConditionType.GRETERTHAN)) {
|
||||
switch (cycleVariation.getParamType()) {
|
||||
case PRINCIPAL:
|
||||
if(principalCycleUsed < cycleVariation.getBorrowerCycleNumber()){
|
||||
borrowerCycleVariations.put(LoanProductConstants.principal, cycleVariation.getDefaultValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minPrincipal, cycleVariation.getMinValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxPrincipal, cycleVariation.getMaxValue());
|
||||
principalCycleUsed = cycleVariation.getBorrowerCycleNumber();
|
||||
}
|
||||
break;
|
||||
case INTERESTRATE:
|
||||
if(interestCycleUsed < cycleVariation.getBorrowerCycleNumber()){
|
||||
borrowerCycleVariations.put(LoanProductConstants.interestRatePerPeriod, cycleVariation.getDefaultValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minInterestRatePerPeriod, cycleVariation.getMinValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxInterestRatePerPeriod, cycleVariation.getMaxValue());
|
||||
interestCycleUsed = cycleVariation.getBorrowerCycleNumber();
|
||||
}
|
||||
break;
|
||||
case REPAYMENT:
|
||||
if(repaymentCycleUsed < cycleVariation.getBorrowerCycleNumber()){
|
||||
borrowerCycleVariations.put(LoanProductConstants.numberOfRepayments, cycleVariation.getDefaultValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.minNumberOfRepayments, cycleVariation.getMinValue());
|
||||
borrowerCycleVariations.put(LoanProductConstants.maxNumberOfRepayments, cycleVariation.getMaxValue());
|
||||
repaymentCycleUsed = cycleVariation.getBorrowerCycleNumber();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return borrowerCycleVariations;
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_product_loan_variations_borrower_cycle")
|
||||
public class LoanProductBorrowerCycleVariations extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "loan_product_id", nullable = false)
|
||||
private LoanProduct loanProduct;
|
||||
|
||||
@Column(name = "borrower_cycle_number", nullable = false)
|
||||
private Integer borrowerCycleNumber;
|
||||
|
||||
@Column(name = "param_type", nullable = false)
|
||||
private Integer paramType;
|
||||
|
||||
@Column(name = "value_condition", nullable = false)
|
||||
private Integer valueConditionType;
|
||||
|
||||
@Column(name = "min_value", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal minValue;
|
||||
|
||||
@Column(name = "max_value", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal maxValue;
|
||||
|
||||
@Column(name = "default_value", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal defaultValue;
|
||||
|
||||
protected LoanProductBorrowerCycleVariations() {
|
||||
|
||||
}
|
||||
|
||||
public LoanProductBorrowerCycleVariations(final Integer borrowerCycleNumber, final Integer paramType, final Integer valueConditionType,
|
||||
final BigDecimal minValue, final BigDecimal maxValue, final BigDecimal defaultValue) {
|
||||
this.borrowerCycleNumber = borrowerCycleNumber;
|
||||
this.paramType = paramType;
|
||||
this.valueConditionType = valueConditionType;
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public void updateLoanProduct(final LoanProduct loanProduct) {
|
||||
this.loanProduct = loanProduct;
|
||||
}
|
||||
|
||||
public LoanProductParamType getParamType() {
|
||||
return LoanProductParamType.fromInt(this.paramType);
|
||||
}
|
||||
|
||||
public LoanProductValueConditionType getValueConditionType() {
|
||||
return LoanProductValueConditionType.fromInt(this.valueConditionType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
LoanProductBorrowerCycleVariations borrowerCycleVariations = (LoanProductBorrowerCycleVariations) obj;
|
||||
boolean minValequal = false;
|
||||
if(borrowerCycleVariations.minValue == null && this.minValue == null){
|
||||
minValequal = true;
|
||||
}else if(borrowerCycleVariations.minValue != null && this.minValue != null){
|
||||
minValequal = borrowerCycleVariations.minValue.equals(this.minValue);
|
||||
}
|
||||
|
||||
boolean maxValequal = false;
|
||||
if(borrowerCycleVariations.maxValue == null && this.maxValue == null){
|
||||
maxValequal = true;
|
||||
}else if(borrowerCycleVariations.maxValue != null && this.maxValue != null){
|
||||
maxValequal = borrowerCycleVariations.maxValue.equals(this.maxValue);
|
||||
}
|
||||
if (borrowerCycleVariations.borrowerCycleNumber.equals(this.borrowerCycleNumber)
|
||||
&& borrowerCycleVariations.defaultValue.equals(this.defaultValue)
|
||||
&& minValequal && maxValequal
|
||||
&& borrowerCycleVariations.valueConditionType.equals(this.valueConditionType)
|
||||
&& borrowerCycleVariations.paramType.equals(this.paramType)) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
public void copy(LoanProductBorrowerCycleVariations borrowerCycleVariations){
|
||||
this.defaultValue = borrowerCycleVariations.defaultValue;
|
||||
this.minValue = borrowerCycleVariations.minValue;
|
||||
this.maxValue = borrowerCycleVariations.maxValue;
|
||||
this.valueConditionType = borrowerCycleVariations.valueConditionType;
|
||||
this.borrowerCycleNumber = borrowerCycleVariations.borrowerCycleNumber;
|
||||
}
|
||||
|
||||
|
||||
public Integer getBorrowerCycleNumber() {
|
||||
return this.borrowerCycleNumber;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getMinValue() {
|
||||
return this.minValue;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getMaxValue() {
|
||||
return this.maxValue;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
public enum LoanProductParamType {
|
||||
|
||||
INVALID(0, "LoanProductParamType.invalid"), //
|
||||
PRINCIPAL(1, "LoanProductParamType.principal"), //
|
||||
INTERESTRATE(2, "LoanProductParamType.interestrate"), //
|
||||
REPAYMENT(3, "LoanProductParamType.repayment"); //
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private LoanProductParamType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static LoanProductParamType fromInt(final Integer chargeTime) {
|
||||
LoanProductParamType loanProductParamType = LoanProductParamType.INVALID;
|
||||
if (chargeTime != null) {
|
||||
switch (chargeTime) {
|
||||
case 1:
|
||||
loanProductParamType = PRINCIPAL;
|
||||
break;
|
||||
case 2:
|
||||
loanProductParamType = INTERESTRATE;
|
||||
break;
|
||||
case 3:
|
||||
loanProductParamType = REPAYMENT;
|
||||
break;
|
||||
default:
|
||||
loanProductParamType = INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return loanProductParamType;
|
||||
}
|
||||
|
||||
public boolean isParamTypePrincipal(){
|
||||
return LoanProductParamType.PRINCIPAL.getValue().equals(this.value);
|
||||
}
|
||||
|
||||
public boolean isParamTypeInterestTate(){
|
||||
return LoanProductParamType.INTERESTRATE.getValue().equals(this.value);
|
||||
}
|
||||
|
||||
public boolean isParamTypeRepayment(){
|
||||
return LoanProductParamType.REPAYMENT.getValue().equals(this.value);
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
public enum LoanProductValueConditionType {
|
||||
|
||||
INVALID(0, "LoanProductParamType.invalid"), //
|
||||
// LESSTHAN(1,"LoanProductValueConditionType.lessthan"),//
|
||||
EQUAL(2, "LoanProductValueConditionType.equal"), //
|
||||
GRETERTHAN(3, "LoanProductValueConditionType.greterthan"); //
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
private LoanProductValueConditionType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static LoanProductValueConditionType fromInt(final Integer type) {
|
||||
LoanProductValueConditionType loanProductParamType = LoanProductValueConditionType.INVALID;
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case 2:
|
||||
loanProductParamType = EQUAL;
|
||||
break;
|
||||
case 3:
|
||||
loanProductParamType = GRETERTHAN;
|
||||
break;
|
||||
default:
|
||||
loanProductParamType = INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return loanProductParamType;
|
||||
}
|
||||
|
||||
public boolean isValueConditionTypeEqual(){
|
||||
return LoanProductValueConditionType.EQUAL.getValue().equals(this.value);
|
||||
}
|
||||
|
||||
public boolean isValueConditionTypeGreterThan(){
|
||||
return LoanProductValueConditionType.GRETERTHAN.getValue().equals(this.value);
|
||||
}
|
||||
|
||||
}
|
||||
+210
-1
@@ -23,7 +23,9 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -52,7 +54,9 @@ public final class LoanProductDataValidator {
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue()));
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),LoanProductConstants.useBorrowerCycleParameterName,
|
||||
LoanProductConstants.principalVariationsForBorrowerCycleParameterName,LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -309,6 +313,15 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue())
|
||||
.value(receivablePenaltyAccountId).notNull().integerGreaterThanZero();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.useBorrowerCycleParameterName, element)) {
|
||||
final Boolean useBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.useBorrowerCycleParameterName, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.useBorrowerCycleParameterName).value(useBorrowerCycle).ignoreIfNull()
|
||||
.validateForBooleanValue();
|
||||
}
|
||||
|
||||
|
||||
validateBorrowerCycleVariations(element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -550,6 +563,14 @@ public final class LoanProductDataValidator {
|
||||
|
||||
validateMinMaxConstraints(element, baseDataValidator, loanProduct);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.useBorrowerCycleParameterName, element)) {
|
||||
final Boolean useBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.useBorrowerCycleParameterName, element);
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.useBorrowerCycleParameterName).value(useBorrowerCycle).ignoreIfNull()
|
||||
.validateForBooleanValue();
|
||||
}
|
||||
|
||||
validateBorrowerCycleVariations(element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -633,6 +654,87 @@ public final class LoanProductDataValidator {
|
||||
|
||||
validateNominalInterestRatePerPeriodMinMaxConstraint(element, loanProduct, baseDataValidator);
|
||||
}
|
||||
|
||||
public void validateMinMaxConstraints(final JsonElement element, final DataValidatorBuilder baseDataValidator,
|
||||
final LoanProduct loanProduct,Integer cycleNumber) {
|
||||
|
||||
final Map<String, BigDecimal> minmaxValues = loanProduct.fetchBorrowerCycleVariationsForCycleNumber(cycleNumber);
|
||||
final String principalParameterName = "principal";
|
||||
BigDecimal principalAmount = null;
|
||||
BigDecimal minPrincipalAmount = null;
|
||||
BigDecimal maxPrincipalAmount = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(principalParameterName, element)) {
|
||||
principalAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(principalParameterName, element);
|
||||
minPrincipalAmount = minmaxValues.get(LoanProductConstants.minPrincipal);
|
||||
maxPrincipalAmount = minmaxValues.get(LoanProductConstants.maxPrincipal);
|
||||
}
|
||||
|
||||
if ((minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) == 1)
|
||||
&& (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) == 1)) {
|
||||
baseDataValidator.reset().parameter(principalParameterName).value(principalAmount)
|
||||
.inMinAndMaxAmountRange(minPrincipalAmount, maxPrincipalAmount);
|
||||
} else {
|
||||
if (minPrincipalAmount != null && minPrincipalAmount.compareTo(BigDecimal.ZERO) == 1) {
|
||||
baseDataValidator.reset().parameter(principalParameterName).value(principalAmount).notLessThanMin(minPrincipalAmount);
|
||||
} else if (maxPrincipalAmount != null && maxPrincipalAmount.compareTo(BigDecimal.ZERO) == 1) {
|
||||
baseDataValidator.reset().parameter(principalParameterName).value(principalAmount)
|
||||
.notGreaterThanMax(maxPrincipalAmount);
|
||||
}
|
||||
}
|
||||
|
||||
final String numberOfRepaymentsParameterName = "numberOfRepayments";
|
||||
Integer maxNumberOfRepayments = null;
|
||||
Integer minNumberOfRepayments = null;
|
||||
Integer numberOfRepayments = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(numberOfRepaymentsParameterName, element)) {
|
||||
numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(numberOfRepaymentsParameterName, element);
|
||||
if(minmaxValues.get(LoanProductConstants.minNumberOfRepayments)!=null){
|
||||
minNumberOfRepayments = minmaxValues.get(LoanProductConstants.minNumberOfRepayments).intValueExact();
|
||||
}
|
||||
if(minmaxValues.get(LoanProductConstants.maxNumberOfRepayments)!=null){
|
||||
maxNumberOfRepayments = minmaxValues.get(LoanProductConstants.maxNumberOfRepayments).intValueExact();
|
||||
}
|
||||
}
|
||||
|
||||
if (maxNumberOfRepayments != null && maxNumberOfRepayments.compareTo(0) == 1) {
|
||||
if (minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) == 1) {
|
||||
baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments)
|
||||
.inMinMaxRange(minNumberOfRepayments, maxNumberOfRepayments);
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments)
|
||||
.notGreaterThanMax(maxNumberOfRepayments);
|
||||
}
|
||||
} else if (minNumberOfRepayments != null && minNumberOfRepayments.compareTo(0) == 1) {
|
||||
baseDataValidator.reset().parameter(numberOfRepaymentsParameterName).value(numberOfRepayments)
|
||||
.notLessThanMin(minNumberOfRepayments);
|
||||
}
|
||||
|
||||
|
||||
|
||||
final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
|
||||
BigDecimal interestRatePerPeriod = null;
|
||||
BigDecimal minInterestRatePerPeriod = null;
|
||||
BigDecimal maxInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) {
|
||||
interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName, element);
|
||||
minInterestRatePerPeriod = minmaxValues.get(LoanProductConstants.minInterestRatePerPeriod);
|
||||
maxInterestRatePerPeriod = minmaxValues.get(LoanProductConstants.maxInterestRatePerPeriod);
|
||||
}
|
||||
if (maxInterestRatePerPeriod != null) {
|
||||
if (minInterestRatePerPeriod != null) {
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod)
|
||||
.inMinAndMaxAmountRange(minInterestRatePerPeriod, maxInterestRatePerPeriod);
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod)
|
||||
.notGreaterThanMax(maxInterestRatePerPeriod);
|
||||
}
|
||||
} else if (minInterestRatePerPeriod != null) {
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod)
|
||||
.notLessThanMin(minInterestRatePerPeriod);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void validatePrincipalMinMaxConstraint(final JsonElement element, final LoanProduct loanProduct,
|
||||
final DataValidatorBuilder baseDataValidator) {
|
||||
@@ -825,4 +927,111 @@ public final class LoanProductDataValidator {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors); }
|
||||
}
|
||||
|
||||
private void validateBorrowerCycleVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
|
||||
validateBorrowerCyclePrincipalVariations(element, baseDataValidator);
|
||||
validateBorrowerCycleRepaymentVariations(element, baseDataValidator);
|
||||
validateBorrowerCycleInterestVariations(element, baseDataValidator);
|
||||
}
|
||||
|
||||
private void validateBorrowerCyclePrincipalVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
|
||||
|
||||
validateBorrowerCycleVariations(element, baseDataValidator, LoanProductConstants.principalVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.principalPerCycleParameterName, LoanProductConstants.minPrincipalPerCycleParameterName,
|
||||
LoanProductConstants.maxPrincipalPerCycleParameterName, LoanProductConstants.principalValueUsageConditionParamName,
|
||||
LoanProductConstants.principalCycleNumbersParamName);
|
||||
}
|
||||
|
||||
private void validateBorrowerCycleRepaymentVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
|
||||
validateBorrowerCycleVariations(element, baseDataValidator, LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.numberOfRepaymentsPerCycleParameterName, LoanProductConstants.minNumberOfRepaymentsPerCycleParameterName,
|
||||
LoanProductConstants.maxNumberOfRepaymentsPerCycleParameterName, LoanProductConstants.repaymentValueUsageConditionParamName,
|
||||
LoanProductConstants.repaymentCycleNumberParamName);
|
||||
}
|
||||
|
||||
private void validateBorrowerCycleInterestVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator) {
|
||||
validateBorrowerCycleVariations(element, baseDataValidator, LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.interestRatePerPeriodPerCycleParameterName,LoanProductConstants.minInterestRatePerPeriodPerCycleParameterName,
|
||||
LoanProductConstants.maxInterestRatePerPeriodPerCycleParameterName,LoanProductConstants.interestRateValueUsageConditionParamName,
|
||||
LoanProductConstants.interestRateCycleNumberParamName);
|
||||
}
|
||||
|
||||
private void validateBorrowerCycleVariations(final JsonElement element, final DataValidatorBuilder baseDataValidator,
|
||||
final String variationParameterName,final String defaultParameterName,final String minParameterName,
|
||||
final String maxParameterName,final String valueUsageConditionParamName,final String cycleNumbersParamName) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
Integer lastCycleNumber = 0;
|
||||
LoanProductValueConditionType lastConditionType = LoanProductValueConditionType.EQUAL;
|
||||
if (this.fromApiJsonHelper.parameterExists(variationParameterName, element)) {
|
||||
final JsonArray variationArray= this.fromApiJsonHelper.extractJsonArrayNamed(variationParameterName, element);
|
||||
if (variationArray != null && variationArray.size() > 0) {
|
||||
int i = 0;
|
||||
do {
|
||||
final JsonObject jsonObject = variationArray.get(i).getAsJsonObject();
|
||||
|
||||
BigDecimal defaultValue = this.fromApiJsonHelper.extractBigDecimalNamed(
|
||||
LoanProductConstants.defaultValueParameterName, jsonObject,locale);
|
||||
BigDecimal minValue = this.fromApiJsonHelper.extractBigDecimalNamed(
|
||||
LoanProductConstants.minValueParameterName, jsonObject,locale);
|
||||
BigDecimal maxValue = this.fromApiJsonHelper.extractBigDecimalNamed(
|
||||
LoanProductConstants.maxValueParameterName, jsonObject,locale);
|
||||
Integer cycleNumber = this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.borrowerCycleNumberParamName, jsonObject, locale);
|
||||
Integer valueUsageCondition = this.fromApiJsonHelper.extractIntegerNamed(LoanProductConstants.valueConditionTypeParamName, jsonObject, locale);
|
||||
|
||||
baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).notBlank();
|
||||
if (minValue != null) {
|
||||
baseDataValidator.reset().parameter(minParameterName).value(minValue).notGreaterThanMax(maxValue);
|
||||
}
|
||||
|
||||
if (maxValue != null) {
|
||||
baseDataValidator.reset().parameter(maxParameterName).value(maxValue).notLessThanMin(minValue);
|
||||
}
|
||||
if ((minValue != null && minValue.compareTo(BigDecimal.ZERO) == 1)
|
||||
&& (maxValue != null && maxValue.compareTo(BigDecimal.ZERO) == 1)) {
|
||||
baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue)
|
||||
.inMinAndMaxAmountRange(minValue, maxValue);
|
||||
} else {
|
||||
if (minValue != null && minValue.compareTo(BigDecimal.ZERO) == 1) {
|
||||
baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).notLessThanMin(minValue);
|
||||
} else if (maxValue != null && maxValue.compareTo(BigDecimal.ZERO) == 1) {
|
||||
baseDataValidator.reset().parameter(defaultParameterName).value(defaultValue).notGreaterThanMax(maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
LoanProductValueConditionType conditionType = LoanProductValueConditionType.INVALID;
|
||||
if (valueUsageCondition != null) {
|
||||
conditionType = LoanProductValueConditionType.fromInt(valueUsageCondition);
|
||||
}
|
||||
baseDataValidator.reset().parameter(valueUsageConditionParamName).value(valueUsageCondition).notNull().inMinMaxRange(LoanProductValueConditionType.EQUAL.getValue(),
|
||||
LoanProductValueConditionType.GRETERTHAN.getValue());
|
||||
if (lastConditionType.equals(LoanProductValueConditionType.EQUAL)
|
||||
&& conditionType.equals(LoanProductValueConditionType.GRETERTHAN)) {
|
||||
baseDataValidator.reset().parameter(cycleNumbersParamName).value(cycleNumber).notNull()
|
||||
.integerSameAsNumber(lastCycleNumber);
|
||||
} else if (lastConditionType.equals(LoanProductValueConditionType.EQUAL)) {
|
||||
baseDataValidator.reset().parameter(cycleNumbersParamName).value(cycleNumber).notNull()
|
||||
.integerSameAsNumber(lastCycleNumber+1);
|
||||
} else if (lastConditionType.equals(LoanProductValueConditionType.GRETERTHAN)) {
|
||||
baseDataValidator.reset().parameter(cycleNumbersParamName).value(cycleNumber).notNull()
|
||||
.integerGreaterThanNumber(lastCycleNumber);
|
||||
}
|
||||
if(conditionType!=null){
|
||||
lastConditionType = conditionType;
|
||||
}
|
||||
if(cycleNumber!=null){
|
||||
lastCycleNumber = cycleNumber;
|
||||
}
|
||||
i++;
|
||||
} while (i < variationArray.size());
|
||||
if(!lastConditionType.equals(LoanProductValueConditionType.GRETERTHAN)){
|
||||
baseDataValidator.reset().parameter(cycleNumbersParamName).failWithCode(LoanProductConstants.VALUE_CONDITION_TYPE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
@@ -27,4 +27,6 @@ public interface LoanDropdownReadPlatformService {
|
||||
|
||||
Collection<TransactionProcessingStrategyData> retreiveTransactionProcessingStrategies();
|
||||
|
||||
List<EnumOptionData> retrieveLoanCycleValueConditionTypeOptions();
|
||||
|
||||
}
|
||||
+11
@@ -11,6 +11,7 @@ import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.i
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestType;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanTermFrequencyType;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanCycleValueConditionType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -23,6 +24,7 @@ import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrateg
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -100,5 +102,14 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
|
||||
|
||||
return strategyOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveLoanCycleValueConditionTypeOptions() {
|
||||
|
||||
final List<EnumOptionData> repaymentFrequencyOptions = Arrays.asList(loanCycleValueConditionType(LoanProductValueConditionType.EQUAL),
|
||||
loanCycleValueConditionType(LoanProductValueConditionType.GRETERTHAN));
|
||||
return repaymentFrequencyOptions;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+48
@@ -14,6 +14,8 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
|
||||
public class LoanEnumerations {
|
||||
@@ -369,4 +371,50 @@ public class LoanEnumerations {
|
||||
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData loanCycleValueConditionType(final int id) {
|
||||
return loanCycleValueConditionType(LoanProductValueConditionType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData loanCycleValueConditionType(final LoanProductValueConditionType type) {
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
case EQUAL:
|
||||
optionData = new EnumOptionData(LoanProductValueConditionType.EQUAL.getValue().longValue(), LoanProductValueConditionType.EQUAL.getCode(), "equals");
|
||||
break;
|
||||
case GRETERTHAN:
|
||||
optionData = new EnumOptionData(LoanProductValueConditionType.GRETERTHAN.getValue().longValue(), LoanProductValueConditionType.GRETERTHAN.getCode(), "greter than");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(LoanProductValueConditionType.INVALID.getValue().longValue(), LoanProductValueConditionType.INVALID.getCode(),
|
||||
"Invalid");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData loanCycleParamType(final int id) {
|
||||
return loanCycleParamType(LoanProductParamType.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData loanCycleParamType(final LoanProductParamType type) {
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
case PRINCIPAL:
|
||||
optionData = new EnumOptionData(LoanProductParamType.PRINCIPAL.getValue().longValue(), LoanProductParamType.PRINCIPAL.getCode(), "principal");
|
||||
break;
|
||||
case INTERESTRATE:
|
||||
optionData = new EnumOptionData(LoanProductParamType.INTERESTRATE.getValue().longValue(),LoanProductParamType.INTERESTRATE.getCode(), "Interest rate");
|
||||
break;
|
||||
case REPAYMENT:
|
||||
optionData = new EnumOptionData(LoanProductParamType.REPAYMENT.getValue().longValue(),LoanProductParamType.REPAYMENT.getCode(), "repayment");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(LoanProductParamType.INVALID.getValue().longValue(), LoanProductParamType.INVALID.getCode(),
|
||||
"Invalid");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanproduct.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
|
||||
public interface LoanProductReadPlatformService {
|
||||
@@ -24,4 +25,6 @@ public interface LoanProductReadPlatformService {
|
||||
Collection<LoanProductData> retrieveRestrictedProductsForMix(Long productId);
|
||||
|
||||
Collection<LoanProductData> retrieveAllowedProductsForMix(Long productId);
|
||||
|
||||
Collection<LoanProductBorrowerCycleVariationData> retrieveLoanProductBorrowerCycleVariations(Long loanProductId);
|
||||
}
|
||||
+62
-8
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.loanproduct.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
@@ -20,7 +21,9 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
@@ -48,8 +51,8 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
|
||||
try {
|
||||
final Collection<ChargeData> charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProductId);
|
||||
|
||||
final LoanProductMapper rm = new LoanProductMapper(charges);
|
||||
final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas = this.retrieveLoanProductBorrowerCycleVariations(loanProductId);
|
||||
final LoanProductMapper rm = new LoanProductMapper(charges,borrowerCycleVariationDatas);
|
||||
final String sql = "select " + rm.loanProductSchema() + " where lp.id = ?";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanProductId });
|
||||
@@ -58,13 +61,20 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
throw new LoanProductNotFoundException(loanProductId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanProductBorrowerCycleVariationData> retrieveLoanProductBorrowerCycleVariations(final Long loanProductId) {
|
||||
LoanProductBorrowerCycleMapper rm = new LoanProductBorrowerCycleMapper();
|
||||
String sql = "select " + rm.schema() +" where bc.loan_product_id=? order by bc.borrower_cycle_number,bc.value_condition";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanProductId });
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanProductData> retrieveAllLoanProducts() {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final LoanProductMapper rm = new LoanProductMapper(null);
|
||||
final LoanProductMapper rm = new LoanProductMapper(null,null);
|
||||
|
||||
final String sql = "select " + rm.loanProductSchema();
|
||||
|
||||
@@ -91,9 +101,12 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
private static final class LoanProductMapper implements RowMapper<LoanProductData> {
|
||||
|
||||
private final Collection<ChargeData> charges;
|
||||
|
||||
private final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas;
|
||||
|
||||
public LoanProductMapper(final Collection<ChargeData> charges) {
|
||||
public LoanProductMapper(final Collection<ChargeData> charges,final Collection<LoanProductBorrowerCycleVariationData> borrowerCycleVariationDatas) {
|
||||
this.charges = charges;
|
||||
this.borrowerCycleVariationDatas = borrowerCycleVariationDatas;
|
||||
}
|
||||
|
||||
public String loanProductSchema() {
|
||||
@@ -105,7 +118,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lp.repay_every as repaidEvery, lp.repayment_period_frequency_enum as repaymentPeriodFrequency, lp.number_of_repayments as numberOfRepayments, lp.min_number_of_repayments as minNumberOfRepayments, lp.max_number_of_repayments as maxNumberOfRepayments, "
|
||||
+ "lp.grace_on_principal_periods as graceOnPrincipalPayment, lp.grace_on_interest_periods as graceOnInterestPayment, lp.grace_interest_free_periods as graceOnInterestCharged,"
|
||||
+ "lp.amortization_method_enum as amortizationMethod, lp.arrearstolerance_amount as tolerance, "
|
||||
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
|
||||
+ "lp.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle,lp.use_borrower_cycle as useBorrowerCycle, lp.start_date as startDate, lp.close_date as closeDate, "
|
||||
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId "
|
||||
+ " from m_product_loan lp "
|
||||
+ " left join m_fund f on f.id = lp.fund_id"
|
||||
@@ -173,6 +186,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
.interestCalculationPeriodType(interestCalculationPeriodTypeId);
|
||||
|
||||
final boolean includeInBorrowerCycle = rs.getBoolean("includeInBorrowerCycle");
|
||||
final boolean useBorrowerCycle = rs.getBoolean("useBorrowerCycle");
|
||||
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
|
||||
final LocalDate closeDate = JdbcSupport.getLocalDate(rs, "closeDate");
|
||||
String status = "";
|
||||
@@ -182,14 +196,29 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
status = "loanProduct.active";
|
||||
}
|
||||
final String externalId = rs.getString("externalId");
|
||||
|
||||
Collection<LoanProductBorrowerCycleVariationData> principalVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>();;
|
||||
Collection<LoanProductBorrowerCycleVariationData> interestRateVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>();;
|
||||
Collection<LoanProductBorrowerCycleVariationData> numberOfRepaymentVariationsForBorrowerCycle = new ArrayList<LoanProductBorrowerCycleVariationData>();;
|
||||
if(this.borrowerCycleVariationDatas!=null){
|
||||
for(LoanProductBorrowerCycleVariationData borrowerCycleVariationData:this.borrowerCycleVariationDatas){
|
||||
LoanProductParamType loanProductParamType =borrowerCycleVariationData.getParamType();
|
||||
if(loanProductParamType.isParamTypePrincipal()){
|
||||
principalVariationsForBorrowerCycle.add(borrowerCycleVariationData);
|
||||
}else if(loanProductParamType.isParamTypeInterestTate()){
|
||||
interestRateVariationsForBorrowerCycle.add(borrowerCycleVariationData);
|
||||
}else if(loanProductParamType.isParamTypeRepayment()){
|
||||
numberOfRepaymentVariationsForBorrowerCycle.add(borrowerCycleVariationData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new LoanProductData(id, name, description, currency, principal, minPrincipal, maxPrincipal, tolerance,
|
||||
numberOfRepayments, minNumberOfRepayments, maxNumberOfRepayments, repaymentEvery, interestRatePerPeriod,
|
||||
minInterestRatePerPeriod, maxInterestRatePerPeriod, annualInterestRate, repaymentFrequencyType,
|
||||
interestRateFrequencyType, amortizationType, interestType, interestCalculationPeriodType, fundId, fundName,
|
||||
transactionStrategyId, transactionStrategyName, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, status,
|
||||
externalId);
|
||||
graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, useBorrowerCycle, startDate, closeDate,
|
||||
status, externalId, principalVariationsForBorrowerCycle, interestRateVariationsForBorrowerCycle, numberOfRepaymentVariationsForBorrowerCycle);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -222,6 +251,31 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
return LoanProductData.lookup(id, name);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LoanProductBorrowerCycleMapper implements RowMapper<LoanProductBorrowerCycleVariationData> {
|
||||
public String schema(){
|
||||
return "bc.id as id,bc.borrower_cycle_number as cycleNumber,bc.value_condition as conditionType,bc.param_type as paramType," +
|
||||
"bc.default_value as defaultValue,bc.max_value as maxVal,bc.min_value as minVal "+
|
||||
"from m_product_loan_variations_borrower_cycle bc";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanProductBorrowerCycleVariationData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final Integer cycleNumber = JdbcSupport.getInteger(rs, "cycleNumber");
|
||||
final Integer conditionType = JdbcSupport.getInteger(rs, "conditionType");
|
||||
final EnumOptionData conditionTypeData = LoanEnumerations.loanCycleValueConditionType(conditionType);
|
||||
final Integer paramType = JdbcSupport.getInteger(rs, "paramType");
|
||||
final EnumOptionData paramTypeData = LoanEnumerations.loanCycleParamType(paramType);
|
||||
final BigDecimal defaultValue = rs.getBigDecimal("defaultValue");
|
||||
final BigDecimal maxValue = rs.getBigDecimal("maxVal");
|
||||
final BigDecimal minValue = rs.getBigDecimal("minVal");
|
||||
|
||||
LoanProductBorrowerCycleVariationData borrowerCycleVariationData = new LoanProductBorrowerCycleVariationData(id,cycleNumber, paramTypeData, conditionTypeData, defaultValue, minValue, maxValue);
|
||||
return borrowerCycleVariationData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoanProductData> retrieveAvailableLoanProductsForMix() {
|
||||
|
||||
Reference in New Issue
Block a user