diff --git a/mifosng-db/migrations/core_db/V77__alter_m_product_loan_changes.sql b/mifosng-db/migrations/core_db/V77__alter_m_product_loan_changes.sql new file mode 100644 index 000000000..c8a586942 --- /dev/null +++ b/mifosng-db/migrations/core_db/V77__alter_m_product_loan_changes.sql @@ -0,0 +1,2 @@ +alter table `m_product_loan` add column `start_date` DATE NULL DEFAULT NULL , +add column `close_date` DATE NULL DEFAULT NULL ; \ 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 d07f0fa34..b702d7fd2 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 @@ -12,6 +12,7 @@ import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonQuery; @@ -45,6 +46,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTrans import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper; +import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException; import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted; import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified; import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException; @@ -148,7 +150,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final Long productId = fromJsonHelper.extractLongNamed("productId", command.parsedJson()); final LoanProduct loanProduct = this.loanProductRepository.findOne(productId); if (loanProduct == null) { throw new LoanProductNotFoundException(productId); } - + + validateSubmittedOnDate(command, loanProduct); + this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator, loanProduct); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } @@ -538,5 +542,24 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory); return loan; } + + private void validateSubmittedOnDate(final JsonCommand command, final LoanProduct loanProduct) { + final LocalDate startDate = loanProduct.getStartDate(); + final LocalDate closeDate = loanProduct.getCloseDate(); + final LocalDate submittedOnDate = command.localDateValueOfParameterNamed("submittedOnDate"); + + String defaultUserMessage = ""; + if (startDate != null && submittedOnDate.isBefore(startDate)) { + defaultUserMessage = "submittedOnDate cannot be before the loan product startDate."; + throw new LoanApplicationDateException("submitted.on.date.cannot.be.before.the.loan.product.start.date", defaultUserMessage, + submittedOnDate.toString(), startDate.toString()); + } + + if (closeDate != null && submittedOnDate.isAfter(closeDate)) { + defaultUserMessage = "submittedOnDate cannot be after the loan product closeDate."; + throw new LoanApplicationDateException("submitted.on.date.cannot.be.after.the.loan.product.close.date", defaultUserMessage, + submittedOnDate.toString(), closeDate.toString()); + } + } } \ No newline at end of file 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 a97b468a8..c1d28c0a5 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 @@ -11,6 +11,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import org.joda.time.LocalDate; import org.mifosplatform.accounting.common.AccountingEnumerations; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.accounting.glaccount.data.GLAccountData; @@ -38,7 +39,9 @@ public class LoanProductData { private final Long fundId; private final String fundName; private final boolean includeInBorrowerCycle; - + private final LocalDate startDate; + private final LocalDate closeDate; + private final String status; // terms private final CurrencyData currency; private final BigDecimal principal; @@ -129,13 +132,15 @@ public class LoanProductData { final Collection charges = null; final EnumOptionData accountingType = null; final boolean includeInBorrowerCycle = false; - + final LocalDate startDate = null; + final LocalDate closeDate = null; + final String status = null; 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); + accountingType, includeInBorrowerCycle, startDate, closeDate, status); } public static LoanProductData lookupWithCurrency(final Long id, final String name, final CurrencyData currency) { @@ -168,13 +173,16 @@ public class LoanProductData { final Collection charges = null; final EnumOptionData accountingType = null; final boolean includeInBorrowerCycle = false; + final LocalDate startDate = null; + final LocalDate closeDate = null; + final String status = null; 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); + accountingType, includeInBorrowerCycle, startDate, closeDate, status); } public static LoanProductData sensibleDefaultsForNewLoanProductCreation() { @@ -213,13 +221,16 @@ public class LoanProductData { final Collection charges = null; final EnumOptionData accountingType = AccountingEnumerations.accountingRuleType(AccountingRuleType.NONE); final boolean includeInBorrowerCycle = false; + final LocalDate startDate = null; + final LocalDate closeDate = null; + final String status = null; 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); + accountingType, includeInBorrowerCycle, startDate, closeDate, status); } public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map accountingMappings, @@ -242,7 +253,8 @@ public class LoanProductData { final EnumOptionData interestCalculationPeriodType, final Long fundId, final String fundName, 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 Collection charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle, + final LocalDate startDate, final LocalDate closeDate, final String status) { this.id = id; this.name = name; this.description = description; @@ -274,6 +286,9 @@ public class LoanProductData { this.charges = charges; this.accountingRule = accountingType; this.includeInBorrowerCycle = includeInBorrowerCycle; + this.startDate = startDate; + this.closeDate = closeDate; + this.status = status; this.chargeOptions = null; this.penaltyOptions = null; @@ -325,6 +340,9 @@ public class LoanProductData { this.amortizationType = productData.amortizationType; this.interestType = productData.interestType; this.interestCalculationPeriodType = productData.interestCalculationPeriodType; + this.startDate = productData.startDate; + this.closeDate = productData.closeDate; + this.status = productData.status; this.charges = nullIfEmpty(productData.charges()); this.accountingRule = productData.accountingRule; 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 53622d085..9b007ebfd 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,6 +6,7 @@ package org.mifosplatform.portfolio.loanproduct.domain; import java.math.BigDecimal; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -19,9 +20,12 @@ import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; import org.mifosplatform.accounting.common.AccountingRuleType; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; @@ -76,6 +80,14 @@ public class LoanProduct extends AbstractPersistable { @Column(name = "include_in_borrower_cycle") private boolean includeInBorrowerCycle; + @Column(name = "start_date", nullable = true) + @Temporal(TemporalType.DATE) + private Date startDate; + + @Column(name = "close_date", nullable = true) + @Temporal(TemporalType.DATE) + private Date closeDate; + public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy, final List productCharges, final JsonCommand command, final AprCalculator aprCalculator) { @@ -115,12 +127,15 @@ public class LoanProduct extends AbstractPersistable { final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule")); final boolean includeInBorrowerCycle = command.booleanPrimitiveValueOfParameterNamed("includeInBorrowerCycle"); - + + final LocalDate startDate = command.localDateValueOfParameterNamed("startDate"); + final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate"); + 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); + amortizationMethod, inArrearsTolerance, productCharges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate); } protected LoanProduct() { @@ -139,7 +154,8 @@ public class LoanProduct extends AbstractPersistable { final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments, 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 AccountingRuleType accountingRuleType, final boolean includeInBorrowerCycle, final LocalDate startDate, + final LocalDate closeDate) { this.fund = fund; this.transactionProcessingStrategy = transactionProcessingStrategy; this.name = name.trim(); @@ -168,6 +184,14 @@ public class LoanProduct extends AbstractPersistable { this.accountingRule = accountingRuleType.getValue(); } this.includeInBorrowerCycle = includeInBorrowerCycle; + + if (startDate != null) { + this.startDate = startDate.toDateMidnight().toDate(); + } + + if (closeDate != null) { + this.closeDate = closeDate.toDateMidnight().toDate(); + } } public MonetaryCurrency getCurrency() { @@ -244,7 +268,7 @@ public class LoanProduct extends AbstractPersistable { final Long newValue = command.longValueOfParameterNamed(fundIdParamName); actualChanges.put(fundIdParamName, newValue); } - + Long existingStrategyId = null; if (this.transactionProcessingStrategy != null) { existingStrategyId = this.transactionProcessingStrategy.getId(); @@ -269,7 +293,42 @@ public class LoanProduct extends AbstractPersistable { actualChanges.put(includeInBorrowerCycleParamName, newValue); this.includeInBorrowerCycle = newValue; } + + final String dateFormatAsInput = command.dateFormat(); + final String localeAsInput = command.locale(); + + final String localeParamName = "locale"; + final String dateFormatParamName = "dateFormat"; + final String startDateParamName = "startDate"; + if (command.isChangeInLocalDateParameterNamed(startDateParamName, getStartDate())) { + final String valueAsInput = command.stringValueOfParameterNamed(startDateParamName); + actualChanges.put(startDateParamName, valueAsInput); + actualChanges.put(dateFormatParamName, dateFormatAsInput); + actualChanges.put(localeParamName, localeAsInput); + + final LocalDate newValue = command.localDateValueOfParameterNamed(startDateParamName); + if (newValue != null) { + this.startDate = newValue.toDate(); + } else { + this.startDate = null; + } + } + + final String closeDateParamName = "closeDate"; + if (command.isChangeInLocalDateParameterNamed(closeDateParamName, getCloseDate())) { + final String valueAsInput = command.stringValueOfParameterNamed(closeDateParamName); + actualChanges.put(closeDateParamName, valueAsInput); + actualChanges.put(dateFormatParamName, dateFormatAsInput); + actualChanges.put(localeParamName, localeAsInput); + + final LocalDate newValue = command.localDateValueOfParameterNamed(closeDateParamName); + if (newValue != null) { + this.closeDate = newValue.toDate(); + } else { + this.closeDate = null; + } + } return actualChanges; } @@ -333,4 +392,21 @@ public class LoanProduct extends AbstractPersistable { public boolean isIncludeInBorrowerCycle() { return this.includeInBorrowerCycle; } + + public LocalDate getStartDate() { + LocalDate startLocalDate = null; + if (this.startDate != null) { + startLocalDate = LocalDate.fromDateFields(this.startDate); + } + return startLocalDate; + } + + public LocalDate getCloseDate() { + LocalDate closeLocalDate = null; + if (this.closeDate != null) { + closeLocalDate = LocalDate.fromDateFields(this.closeDate); + } + return closeLocalDate; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java index 552fbaad2..2bfce6916 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductRelatedDetail.java @@ -223,7 +223,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche currencyCode = newValue; this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal); } - + final Map loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator); actualChanges.putAll(loanApplicationAttributeChanges); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/LoanProductDateException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/LoanProductDateException.java new file mode 100644 index 000000000..e55970bbc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/exception/LoanProductDateException.java @@ -0,0 +1,13 @@ +package org.mifosplatform.portfolio.loanproduct.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + + +public class LoanProductDateException extends AbstractPlatformDomainRuleException { + + public LoanProductDateException(Object... defaultUserMessageArgs) { + super("error.msg.loan.product.close.date.cannot.be.before.start.date.close.date", + "Loan product close date cannot be before the start date", defaultUserMessageArgs); + } + +} 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 7597aa94a..d57fd8b16 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 @@ -38,12 +38,12 @@ public final class LoanProductDataValidator { /** * The parameters supported for this command. */ - private final Set supportedParameters = new HashSet(Arrays.asList("locale", "name", "description", "fundId", + private final Set supportedParameters = new HashSet(Arrays.asList("locale", "dateFormat", "name", "description", "fundId", "currencyCode", "digitsAfterDecimal", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment", - "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule","includeInBorrowerCycle", + "graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule","includeInBorrowerCycle", "startDate", "closeDate", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), 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 451e9b774..27c35db5d 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 @@ -10,9 +10,11 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; +import org.joda.time.LocalDate; import org.mifosplatform.accounting.common.AccountingEnumerations; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.monetary.data.CurrencyData; @@ -103,7 +105,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.accounting_type as accountingType, lp.include_in_borrower_cycle as includeInBorrowerCycle, 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 " + " from m_product_loan lp " + " left join m_fund f on f.id = lp.fund_id" @@ -170,13 +172,21 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo .interestCalculationPeriodType(interestCalculationPeriodTypeId); final boolean includeInBorrowerCycle = rs.getBoolean("includeInBorrowerCycle"); - + final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate"); + final LocalDate closeDate = JdbcSupport.getLocalDate(rs, "closeDate"); + String status = ""; + if (closeDate != null && closeDate.isBefore(DateUtils.getLocalDateOfTenant())) { + status = "loanProduct.inActive"; + } else { + status = "loanProduct.active"; + } + 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); + graceOnInterestCharged, this.charges, accountingRuleType, includeInBorrowerCycle, startDate, closeDate, status); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java index 75de974fe..a0709ed0e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanProductWritePlatformServiceJpaRepositoryImpl.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.joda.time.LocalDate; import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; @@ -27,6 +28,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository; import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; +import org.mifosplatform.portfolio.loanproduct.exception.LoanProductDateException; import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException; import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductDataValidator; import org.slf4j.Logger; @@ -78,7 +80,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro this.context.authenticatedUser(); this.fromApiJsonDeserializer.validateForCreate(command.json()); - + validateInputDates(command); + final Fund fund = findFundByIdIfProvided(command.longValueOfParameterNamed("fundId")); final Long transactionProcessingStrategyId = command.longValueOfParameterNamed("transactionProcessingStrategyId"); @@ -136,6 +139,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro if (product == null) { throw new LoanProductNotFoundException(loanProductId); } this.fromApiJsonDeserializer.validateForUpdate(command.json(), product); + validateInputDates(command); final Map changes = product.update(command, this.aprCalculator); @@ -237,6 +241,17 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro throw new PlatformDataIntegrityException("error.msg.product.loan.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); } + + private void validateInputDates(final JsonCommand command) { + final LocalDate startDate = command.localDateValueOfParameterNamed("startDate"); + final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate"); + + if (startDate != null && closeDate != null) { + if (closeDate.isBefore(startDate)) { + throw new LoanProductDateException(startDate.toString(), closeDate.toString()); + } + } + } private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { logger.error(dve.getMessage(), dve);