diff --git a/mifosng-db/patches/patch-0008-alter-loan-transaction-derived-columns-to-be-nullable.sql b/mifosng-db/patches/patch-0008-alter-loan-transaction-derived-columns-to-be-nullable.sql new file mode 100644 index 000000000..fd3fe333c --- /dev/null +++ b/mifosng-db/patches/patch-0008-alter-loan-transaction-derived-columns-to-be-nullable.sql @@ -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); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java index 8743e435c..2eb7ec65b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/LoanProductCommand.java @@ -35,7 +35,9 @@ public class LoanProductCommand { private final Set modifiedParameters; - public LoanProductCommand(final Set modifiedParameters, final Long id, final String name, final String description, + public LoanProductCommand( + final Set 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; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index 9d669cbc5..d82751f25 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -616,7 +616,9 @@ public class Loan extends AbstractAuditableCustom { 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."; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java index 9a21d2622..c5736208f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProduct.java @@ -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 { + @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 { @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 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 charges) { this.fund = fund; this.transactionProcessingStrategy = transactionProcessingStrategy; this.name = name.trim(); @@ -92,64 +104,15 @@ public class LoanProduct extends AbstractAuditableCustom { 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 getCharges() { return charges; } - public void update(final LoanProductCommand command, final Fund fund, final LoanTransactionProcessingStrategy strategy, - final Set charges) { + public void update( + final LoanProductCommand command, + final Fund fund, + final LoanTransactionProcessingStrategy strategy, + final Set charges) { if (command.isNameChanged()) { this.name = command.getName(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java index 6c82f5474..7c7ac647e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanProductRelatedDetail.java @@ -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() { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java index 8d0d7c17f..cafbf6c2a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransaction.java @@ -37,13 +37,13 @@ public class LoanTransaction extends AbstractAuditableCustom { @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 { 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(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/AdjustLoanTransactionCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/AdjustLoanTransactionCommandValidator.java index fb95085ff..0ceebad97 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/AdjustLoanTransactionCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/AdjustLoanTransactionCommandValidator.java @@ -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()) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java index 78bd318fb..005bd462c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -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); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java index 18b0d92b5..1454eb61b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loanproduct/service/LoanProductCommandValidator.java @@ -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);