Merge pull request #446 from goutham-M/mifosx-499

reactivate feature is added
This commit is contained in:
Vishwas Babu A J
2013-07-11 07:33:08 -07:00
9 changed files with 175 additions and 18 deletions
@@ -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 ;
@@ -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());
}
}
}
@@ -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<ChargeData> 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<ChargeData> 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<ChargeData> 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<String, Object> 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<ChargeData> charges, final EnumOptionData accountingType, final boolean includeInBorrowerCycle) {
final Collection<ChargeData> 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;
@@ -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<Long> {
@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<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
@@ -115,12 +127,15 @@ public class LoanProduct extends AbstractPersistable<Long> {
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<Long> {
final Integer defaultMinNumberOfInstallments, final Integer defaultMaxNumberOfInstallments,
final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged,
final AmortizationMethod amortizationMethod, final BigDecimal inArrearsTolerance, final List<Charge> charges,
final AccountingRuleType accountingRuleType, final boolean includeInBorrowerCycle) {
final 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<Long> {
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<Long> {
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<Long> {
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<Long> {
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;
}
}
@@ -223,7 +223,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
currencyCode = newValue;
this.currency = new MonetaryCurrency(currencyCode, digitsAfterDecimal);
}
final Map<String, Object> loanApplicationAttributeChanges = updateLoanApplicationAttributes(command, aprCalculator);
actualChanges.putAll(loanApplicationAttributeChanges);
@@ -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);
}
}
@@ -38,12 +38,12 @@ public final class LoanProductDataValidator {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = new HashSet<String>(Arrays.asList("locale", "name", "description", "fundId",
private final Set<String> supportedParameters = new HashSet<String>(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(),
@@ -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);
}
}
@@ -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<String, Object> 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);