diff --git a/mifosng-db/migrations/core_db/V132__borrower_cycle_changes.sql b/mifosng-db/migrations/core_db/V132__borrower_cycle_changes.sql new file mode 100755 index 000000000..e65437a07 --- /dev/null +++ b/mifosng-db/migrations/core_db/V132__borrower_cycle_changes.sql @@ -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`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java index d8bf38f0e..009445a54 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/data/DataValidatorBuilder.java @@ -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 diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index a13933a32..ad9f4025a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -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) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java index 493fe6dcb..0ac414e20 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java @@ -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 interestRateFrequencyTypeOptions, final Collection amortizationTypeOptions, final Collection interestTypeOptions, final Collection interestCalculationPeriodTypeOptions, final Collection fundOptions, final Collection chargeOptions, - final Collection loanPurposeOptions, final Collection loanCollateralOptions) { + final Collection loanPurposeOptions, final Collection 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 calendarOptions = null; final Collection loanOfficerOptions = null; - final Integer termFrequency = product.getNumberOfRepayments() * product.getRepaymentEvery(); final EnumOptionData termPeriodFrequencyType = product.getRepaymentFrequencyType(); final Collection charges = new ArrayList(); @@ -512,21 +514,50 @@ public class LoanAccountData { final Integer loanProductCounter = null; final PortfolioAccountData linkedAccount = null; final Collection accountLinkingOptions = null; + BigDecimal principal = null; + BigDecimal interestRatePerPeriod = null; + Integer numberOfRepayments = null; + if (product.useBorrowerCycle() && loanCycleNumber > 0) { + Collection principalVariationsForBorrowerCycle = product + .getPrincipalVariationsForBorrowerCycle(); + Collection interestForVariationsForBorrowerCycle = product + .getInterestRateVariationsForBorrowerCycle(); + Collection 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 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); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 4c8cb650f..6356d6155 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -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 dataValidationErrors = new ArrayList(); + 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 chargePerInstallments = existingLoanApplication.generateInstallmentLoanCharges(loanCharge); + final Set chargePerInstallments = existingLoanApplication + .generateInstallmentLoanCharges(loanCharge); loanCharge.addLoanInstallmentCharges(chargePerInstallments); this.loanChargeRepository.save(loanCharge); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java index 1d8d51661..8398697ed 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformService.java @@ -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 retrieveAllLoansWithOverdueInstallmentsNotAlreadyPenalized(); + + Integer retriveLoanCounter(Long groupId, Integer loanType); + + Integer retriveLoanCounter(Long clientId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 9177fb09d..1171d1f20 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -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 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); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java new file mode 100755 index 000000000..3111fc9f2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/LoanProductConstants.java @@ -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"; +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java index 375742baa..c14e0d423 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/api/LoanProductsApiResource.java @@ -264,11 +264,13 @@ public class LoanProductsApiResource { final List accountingRuleTypeOptions = this.accountingDropdownReadPlatformService .retrieveAccountingRuleTypeOptions(); + + final List loanCycleValueConditionTypeOptions = this.dropdownReadPlatformService.retrieveLoanCycleValueConditionTypeOptions(); return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions, - accountingRuleTypeOptions); + accountingRuleTypeOptions,loanCycleValueConditionTypeOptions); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductBorrowerCycleVariationData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductBorrowerCycleVariationData.java new file mode 100755 index 000000000..5d416e870 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductBorrowerCycleVariationData.java @@ -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; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java index 6fcc6be2a..18b4c36e8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/data/LoanProductData.java @@ -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 charges; - + + private final Collection principalVariationsForBorrowerCycle; + private final Collection interestRateVariationsForBorrowerCycle; + private final Collection numberOfRepaymentVariationsForBorrowerCycle; // accounting private final EnumOptionData accountingRule; private Map accountingMappings; @@ -99,7 +103,8 @@ public class LoanProductData { private final List accountingRuleOptions; @SuppressWarnings("unused") private final Map> accountingMappingOptions; - + @SuppressWarnings("unused") + private final List 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 charges = null; + final Collection principalVariations = new ArrayList(1); + final Collection interestRateVariations = new ArrayList(1); + final Collection numberOfRepaymentVariations = new ArrayList(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 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 principalVariations = new ArrayList(1); + final Collection interestRateVariations = new ArrayList(1); + final Collection numberOfRepaymentVariations = new ArrayList(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 charges = null; + final Collection principalVariationsForBorrowerCycle = new ArrayList(1); + final Collection interestRateVariationsForBorrowerCycle = new ArrayList(1); + final Collection numberOfRepaymentVariationsForBorrowerCycle = new ArrayList(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 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 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 principalVariations, Collection interestRateVariations, + Collection 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 chargeOptions, @@ -321,7 +350,8 @@ public class LoanProductData { final List interestTypeOptions, final List interestCalculationPeriodTypeOptions, final List repaymentFrequencyTypeOptions, final List interestRateFrequencyTypeOptions, final Collection fundOptions, final Collection transactionStrategyOptions, - final Map> accountingMappingOptions, final List accountingRuleOptions) { + final Map> accountingMappingOptions, final List accountingRuleOptions, + final List 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 getPrincipalVariationsForBorrowerCycle() { + return this.principalVariationsForBorrowerCycle; + } + + + public Collection getInterestRateVariationsForBorrowerCycle() { + return this.interestRateVariationsForBorrowerCycle; + } + + + public Collection getNumberOfRepaymentVariationsForBorrowerCycle() { + return this.numberOfRepaymentVariationsForBorrowerCycle; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 6178a657e..28490a86d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -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 { @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 { @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 borrowerCycleVariations = new HashSet(); + public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, final List productCharges, final JsonCommand command, final AprCalculator aprCalculator) { @@ -137,12 +153,186 @@ public class LoanProduct extends AbstractPersistable { final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate"); final String externalId = command.stringValueOfParameterNamedAllowingNull("externalId"); + final boolean useBorrowerCycle = command.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.useBorrowerCycleParameterName); + final Set loanProductBorrowerCycleVariations = new HashSet(); + + 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) { + assemblePrincipalVariations(command, loanProductBorrowerCycleVariations); + + assembleRepaymentVariations(command, loanProductBorrowerCycleVariations); + + assembleInterestRateVariations(command, loanProductBorrowerCycleVariations); + } + + /** + * @param command + * @param loanProductBorrowerCycleVariations + */ + private static void assembleInterestRateVariations(final JsonCommand command, + final Set loanProductBorrowerCycleVariations) { + assembleVaritions(command, loanProductBorrowerCycleVariations, LoanProductParamType.INTERESTRATE.getValue(), + LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName); + + } + + /** + * @param command + * @param loanProductBorrowerCycleVariations + */ + private static void assembleRepaymentVariations(final JsonCommand command, + final Set loanProductBorrowerCycleVariations) { + assembleVaritions(command, loanProductBorrowerCycleVariations, LoanProductParamType.REPAYMENT.getValue(), + LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName); + + } + + /** + * @param command + * @param loanProductBorrowerCycleVariations + */ + private static void assemblePrincipalVariations(final JsonCommand command, + final Set loanProductBorrowerCycleVariations) { + assembleVaritions(command, loanProductBorrowerCycleVariations, LoanProductParamType.PRINCIPAL.getValue(), + LoanProductConstants.principalVariationsForBorrowerCycleParameterName); + } + + private static void assembleVaritions(final JsonCommand command, + final Set 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 updateBorrowerCycleVariations(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(20); + List 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 fetchAllVariationIds(){ + List list = new ArrayList(); + for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) { + list.add(cycleVariation.getId()); + } + return list; + } + + private void updateBorrowerCycleVaritions(final JsonCommand command,Integer paramType, + String variationParameterName,final Map actualChanges, List 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 remove = new HashSet(); + 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 { final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List 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) { this.fund = fund; this.transactionProcessingStrategy = transactionProcessingStrategy; this.name = name.trim(); @@ -191,6 +382,7 @@ public class LoanProduct extends AbstractPersistable { 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 { } 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 { 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 { // 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 { 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 fetchBorrowerCycleVariationsForCycleNumber(final Integer cycleNumber) { + Map borrowerCycleVariations = new HashMap(); + 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; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductBorrowerCycleVariations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductBorrowerCycleVariations.java new file mode 100755 index 000000000..f78c6b268 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductBorrowerCycleVariations.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductParamType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductParamType.java new file mode 100755 index 000000000..3d2acf685 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductParamType.java @@ -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); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductValueConditionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductValueConditionType.java new file mode 100755 index 000000000..531821660 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductValueConditionType.java @@ -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); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index 1aa3d6ab1..4daf2129e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -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 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); + } + } + + } + + + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java index 2ba79ab97..94e5d0d02 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformService.java @@ -27,4 +27,6 @@ public interface LoanDropdownReadPlatformService { Collection retreiveTransactionProcessingStrategies(); + List retrieveLoanCycleValueConditionTypeOptions(); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java index 333485583..bb7d2927c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanDropdownReadPlatformServiceImpl.java @@ -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 retrieveLoanCycleValueConditionTypeOptions() { + + final List repaymentFrequencyOptions = Arrays.asList(loanCycleValueConditionType(LoanProductValueConditionType.EQUAL), + loanCycleValueConditionType(LoanProductValueConditionType.GRETERTHAN)); + return repaymentFrequencyOptions; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java index 904834899..e40908ad4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java @@ -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; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java index 43cf5b293..955e35031 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformService.java @@ -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 retrieveRestrictedProductsForMix(Long productId); Collection retrieveAllowedProductsForMix(Long productId); + + Collection retrieveLoanProductBorrowerCycleVariations(Long loanProductId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java index d603dec03..f6e437b8e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductReadPlatformServiceImpl.java @@ -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 charges = this.chargeReadPlatformService.retrieveLoanProductCharges(loanProductId); - - final LoanProductMapper rm = new LoanProductMapper(charges); + final Collection 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 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 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 { private final Collection charges; + + private final Collection borrowerCycleVariationDatas; - public LoanProductMapper(final Collection charges) { + public LoanProductMapper(final Collection charges,final Collection 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 principalVariationsForBorrowerCycle = new ArrayList();; + Collection interestRateVariationsForBorrowerCycle = new ArrayList();; + Collection numberOfRepaymentVariationsForBorrowerCycle = new ArrayList();; + 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 { + 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 retrieveAvailableLoanProductsForMix() {