allow adjusted transactions to be zeroed, ensure inArrearsTolerance is nullable and does not need to be provided.

This commit is contained in:
Keith Woodlock
2012-10-12 00:41:05 +01:00
parent 3ce1ee7ded
commit b56ecc8387
9 changed files with 76 additions and 91 deletions
@@ -0,0 +1,8 @@
alter table m_loan_transaction modify column principal_portion_derived decimal(19,6);
alter table m_loan_transaction modify column interest_portion_derived decimal(19,6);
alter table m_loan_transaction modify column charges_portion_derived decimal(19,6);
alter table m_product_loan modify column arrearstolerance_amount decimal(19,6);
@@ -35,7 +35,9 @@ public class LoanProductCommand {
private final Set<String> modifiedParameters;
public LoanProductCommand(final Set<String> modifiedParameters, final Long id, final String name, final String description,
public LoanProductCommand(
final Set<String> modifiedParameters,
final Long id, final String name, final String description,
final Long fundId, final Long transactionProcessingStrategyId,
final String currencyCode, final Integer digitsAfterDecimal,
final BigDecimal principal, final BigDecimal inArrearsTolerance, final Integer numberOfRepayments,
@@ -51,7 +53,11 @@ public class LoanProductCommand {
this.currencyCode = currencyCode;
this.digitsAfterDecimal = digitsAfterDecimal;
this.principal = principal;
this.inArrearsTolerance = inArrearsTolerance;
if (inArrearsTolerance != null && BigDecimal.ZERO.compareTo(inArrearsTolerance) == 0) {
this.inArrearsTolerance = null;
} else {
this.inArrearsTolerance = inArrearsTolerance;
}
this.numberOfRepayments = numberOfRepayments;
this.repaymentEvery = repaymentEvery;
this.interestRatePerPeriod = interestRatePerPeriod;
@@ -616,7 +616,9 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
boolean isTransactionChronologicallyLatest = isChronologicallyLatestRepaymentOrWaiver(loanTransaction, this.loanTransactions);
this.loanTransactions.add(loanTransaction);
if (loanTransaction.isNotZero(loanCurrency())) {
this.loanTransactions.add(loanTransaction);
}
if (loanTransaction.isNotRepayment() && loanTransaction.isNotInterestWaiver()) {
final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
@@ -16,7 +16,6 @@ import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.LoanProductCommand;
import org.mifosng.platform.charge.domain.Charge;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.fund.domain.Fund;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.user.domain.AppUser;
@@ -33,17 +32,21 @@ import org.mifosng.platform.user.domain.AppUser;
@Table(name = "m_product_loan")
public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
@SuppressWarnings("unused")
@ManyToOne
@JoinColumn(name = "fund_id", nullable = true)
private Fund fund;
@SuppressWarnings("unused")
@ManyToOne
@JoinColumn(name = "loan_transaction_strategy_id", nullable = true)
private LoanTransactionProcessingStrategy transactionProcessingStrategy;
@SuppressWarnings("unused")
@Column(name = "name", nullable = false)
private String name;
@SuppressWarnings("unused")
@Column(name = "description")
private String description;
@@ -56,19 +59,28 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
@Embedded
private final LoanProductRelatedDetail loanProductRelatedDetail;
public LoanProduct() {
this.fund = null;
this.name = null;
this.description = null;
this.loanProductRelatedDetail = null;
protected LoanProduct() {
this.loanProductRelatedDetail = null;
}
public LoanProduct(final Fund fund, final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final String name, final String description, final MonetaryCurrency currency, final BigDecimal defaultPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod, final PeriodFrequencyType interestPeriodFrequencyType, final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod, final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery, final PeriodFrequencyType repaymentFrequencyType, final Integer defaultNumberOfInstallments, final AmortizationMethod amortizationMethod,
final BigDecimal inArrearsTolerance, final Set<Charge> charges) {
public LoanProduct(
final Fund fund,
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
final String name,
final String description,
final MonetaryCurrency currency,
final BigDecimal defaultPrincipal,
final BigDecimal defaultNominalInterestRatePerPeriod,
final PeriodFrequencyType interestPeriodFrequencyType,
final BigDecimal defaultAnnualNominalInterestRate,
final InterestMethod interestMethod,
final InterestCalculationPeriodMethod interestCalculationPeriodMethod,
final Integer repayEvery,
final PeriodFrequencyType repaymentFrequencyType,
final Integer defaultNumberOfInstallments,
final AmortizationMethod amortizationMethod,
final BigDecimal inArrearsTolerance,
final Set<Charge> charges) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
this.name = name.trim();
@@ -92,64 +104,15 @@ public class LoanProduct extends AbstractAuditableCustom<AppUser, Long> {
return this.loanProductRelatedDetail.getCurrency();
}
public Money getInArrearsTolerance() {
return this.loanProductRelatedDetail.getInArrearsTolerance();
}
public String getName() {
return this.name;
}
public String getDescription() {
return this.description;
}
public Integer getRepayEvery() {
return this.loanProductRelatedDetail.getRepayEvery();
}
public BigDecimal getDefaultNominalInterestRatePerPeriod() {
return this.loanProductRelatedDetail.getNominalInterestRatePerPeriod();
}
public PeriodFrequencyType getInterestPeriodFrequencyType() {
return this.loanProductRelatedDetail.getInterestPeriodFrequencyType();
}
public BigDecimal getDefaultAnnualNominalInterestRate() {
return this.loanProductRelatedDetail.getAnnualNominalInterestRate();
}
public InterestMethod getInterestMethod() {
return this.loanProductRelatedDetail.getInterestMethod();
}
public PeriodFrequencyType getRepaymentPeriodFrequencyType() {
return this.loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
}
public Integer getDefaultNumberOfRepayments() {
return this.loanProductRelatedDetail.getNumberOfRepayments();
}
public AmortizationMethod getAmortizationMethod() {
return this.loanProductRelatedDetail.getAmortizationMethod();
}
public Fund getFund() {
return fund;
}
public LoanTransactionProcessingStrategy getTransactionProcessingStrategy() {
return transactionProcessingStrategy;
}
public Set<Charge> getCharges() {
return charges;
}
public void update(final LoanProductCommand command, final Fund fund, final LoanTransactionProcessingStrategy strategy,
final Set<Charge> charges) {
public void update(
final LoanProductCommand command,
final Fund fund,
final LoanTransactionProcessingStrategy strategy,
final Set<Charge> charges) {
if (command.isNameChanged()) {
this.name = command.getName();
@@ -57,21 +57,11 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
@Column(name = "amortization_method_enum", nullable = false)
private AmortizationMethod amortizationMethod;
@Column(name = "arrearstolerance_amount", scale = 6, precision = 19, nullable = false)
@Column(name = "arrearstolerance_amount", scale = 6, precision = 19, nullable = true)
private BigDecimal inArrearsTolerance;
protected LoanProductRelatedDetail() {
this.principal = null;
this.nominalInterestRatePerPeriod = null;
this.interestPeriodFrequencyType = null;
this.annualNominalInterestRate = null;
this.interestMethod = null;
this.repayEvery = null;
this.repaymentPeriodFrequencyType = null;
this.numberOfRepayments = null;
this.amortizationMethod = null;
this.inArrearsTolerance = null;
//
}
public LoanProductRelatedDetail(final MonetaryCurrency currency,
@@ -97,7 +87,11 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
this.repaymentPeriodFrequencyType = repaymentFrequencyType;
this.numberOfRepayments = defaultNumberOfRepayments;
this.amortizationMethod = amortizationMethod;
this.inArrearsTolerance = inArrearsTolerance;
if (inArrearsTolerance != null && BigDecimal.ZERO.compareTo(inArrearsTolerance) == 0) {
this.inArrearsTolerance = null;
} else {
this.inArrearsTolerance = inArrearsTolerance;
}
}
public MonetaryCurrency getCurrency() {
@@ -37,13 +37,13 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "principal_portion_derived", scale = 6, precision = 19, nullable = false)
@Column(name = "principal_portion_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal principalPortion = BigDecimal.ZERO;
@Column(name = "interest_portion_derived", scale = 6, precision = 19, nullable = false)
@Column(name = "interest_portion_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal interestPortion = BigDecimal.ZERO;
@Column(name = "charges_portion_derived", scale = 6, precision = 19, nullable = false)
@Column(name = "charges_portion_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal chargesPortion = BigDecimal.ZERO;
@Temporal(TemporalType.DATE)
@@ -218,4 +218,12 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
this.interestPortion = null;
this.chargesPortion = null;
}
public boolean isGreaterThanZero(final MonetaryCurrency currency) {
return getAmount(currency).isGreaterThanZero();
}
public boolean isNotZero(final MonetaryCurrency currency) {
return !getAmount(currency).isZero();
}
}
@@ -25,7 +25,7 @@ public class AdjustLoanTransactionCommandValidator {
baseDataValidator.reset().parameter("loanId").value(command.getLoanId()).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("transactionId").value(command.getTransactionId()).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("transactionDate").value(command.getTransactionDate()).notNull();
baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmount()).notNull().positiveAmount();
baseDataValidator.reset().parameter("transactionAmount").value(command.getTransactionAmount()).notNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter("note").value(command.getNote()).notExceedingLengthOf(1000);
if (!dataValidationErrors.isEmpty()) {
@@ -24,6 +24,7 @@ import org.mifosng.platform.client.domain.Client;
import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.client.domain.Note;
import org.mifosng.platform.client.domain.NoteRepository;
import org.mifosng.platform.currency.domain.MonetaryCurrency;
import org.mifosng.platform.currency.domain.Money;
import org.mifosng.platform.exceptions.ChargeIsNotActiveException;
import org.mifosng.platform.exceptions.ChargeNotFoundException;
@@ -487,9 +488,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
throw new LoanTransactionNotFoundException(command.getTransactionId());
}
Money transactionAmount = Money.of(loan
.repaymentScheduleDetail().getPrincipal()
.getCurrency(), command.getTransactionAmount());
final MonetaryCurrency currency = loan.repaymentScheduleDetail().getPrincipal().getCurrency();
final Money transactionAmount = Money.of(currency, command.getTransactionAmount());
// adjustment is only supported for repayments and waivers at present
LocalDate transactionDate = command.getTransactionDate();
@@ -500,7 +500,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());
this.loanTransactionRepository.save(newTransactionDetail);
if (newTransactionDetail.isGreaterThanZero(currency)) {
this.loanTransactionRepository.save(newTransactionDetail);
}
this.loanRepository.save(loan);
@@ -64,12 +64,14 @@ public class LoanProductCommandValidator {
baseDataValidator.reset().parameter("digitsAfterDecimal").value(command.getDigitsAfterDecimal()).notNull().inMinMaxRange(0, 6);
baseDataValidator.reset().parameter("principal").value(command.getPrincipal()).notNull().positiveAmount();
baseDataValidator.reset().parameter("inArrearsTolerance").value(command.getInArrearsTolerance()).notNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter("inArrearsTolerance").value(command.getInArrearsTolerance()).ignoreIfNull().zeroOrPositiveAmount();
baseDataValidator.reset().parameter("repaymentFrequencyType").value(command.getRepaymentFrequencyType()).notNull().inMinMaxRange(0, 3);
baseDataValidator.reset().parameter("repaymentEvery").value(command.getRepaymentEvery()).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("numberOfRepayments").value(command.getNumberOfRepayments()).notNull().integerGreaterThanZero();
// transactionProcessingStrategyId defaults to mifos strategy if left null
baseDataValidator.reset().parameter("interestRatePerPeriod").value(command.getInterestRatePerPeriod()).notNull();
baseDataValidator.reset().parameter("interestRateFrequencyType").value(command.getInterestRateFrequencyType()).notNull().inMinMaxRange(0, 3);