allow adjusted transactions to be zeroed, ensure inArrearsTolerance is nullable and does not need to be provided.
This commit is contained in:
+8
@@ -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);
|
||||
+8
-2
@@ -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();
|
||||
|
||||
+7
-13
@@ -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() {
|
||||
|
||||
+11
-3
@@ -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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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()) {
|
||||
|
||||
+6
-4
@@ -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);
|
||||
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user