diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 43bdc901a..0aeb7e2c3 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanaccount.domain; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; @@ -119,6 +120,11 @@ public final class LoanTransaction extends AbstractPersistable { @Column(name = "manually_adjusted_or_reversed", nullable = false) private boolean manuallyAdjustedOrReversed; + @LazyCollection(LazyCollectionOption.FALSE) + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "loan_transaction_id", referencedColumnName= "id" , nullable = false) + private Set loanTransactionToRepaymentScheduleMappings = new HashSet<>(); + protected LoanTransaction() { this.loan = null; this.dateOf = null; @@ -640,9 +646,9 @@ public final class LoanTransaction extends AbstractPersistable { private LocalDate getCreatedDate() { return new LocalDate(this.createdDate); } - + public LocalDateTime getCreatedDateTime() { - return new LocalDateTime(this.createdDate); + return new LocalDateTime(this.createdDate); } public boolean isLastTransaction(final LoanTransaction loanTransaction) { @@ -662,4 +668,40 @@ public final class LoanTransaction extends AbstractPersistable { } return isLatest; } + + public void updateLoanTransactionToRepaymentScheduleMappings(final Collection mappings) { + Collection retainMappings = new ArrayList<>(); + for (LoanTransactionToRepaymentScheduleMapping updatedrepaymentScheduleMapping : mappings) { + updateMapingDetail(retainMappings, updatedrepaymentScheduleMapping); + } + this.loanTransactionToRepaymentScheduleMappings.retainAll(retainMappings); + } + + private boolean updateMapingDetail(final Collection retainMappings, + final LoanTransactionToRepaymentScheduleMapping updatedrepaymentScheduleMapping) { + boolean isMappingUpdated = false; + for (LoanTransactionToRepaymentScheduleMapping repaymentScheduleMapping : this.loanTransactionToRepaymentScheduleMappings) { + if (updatedrepaymentScheduleMapping.getLoanRepaymentScheduleInstallment().getId() != null + && repaymentScheduleMapping.getLoanRepaymentScheduleInstallment().getDueDate() + .equals(updatedrepaymentScheduleMapping.getLoanRepaymentScheduleInstallment().getDueDate())) { + repaymentScheduleMapping.setComponents(updatedrepaymentScheduleMapping.getPrincipalPortion(), + updatedrepaymentScheduleMapping.getInterestPortion(), updatedrepaymentScheduleMapping.getFeeChargesPortion(), + updatedrepaymentScheduleMapping.getPenaltyChargesPortion()); + isMappingUpdated = true; + retainMappings.add(repaymentScheduleMapping); + break; + } + } + if (!isMappingUpdated) { + this.loanTransactionToRepaymentScheduleMappings.add(updatedrepaymentScheduleMapping); + retainMappings.add(updatedrepaymentScheduleMapping); + } + return isMappingUpdated; + } + + + public Set getLoanTransactionToRepaymentScheduleMappings() { + return this.loanTransactionToRepaymentScheduleMappings; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java new file mode 100644 index 000000000..3739a4e27 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java @@ -0,0 +1,145 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.loanaccount.domain; + +import java.math.BigDecimal; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.Money; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_loan_transaction_repayment_schedule_mapping") +public class LoanTransactionToRepaymentScheduleMapping extends AbstractPersistable { + + @ManyToOne(optional = false, cascade = CascadeType.PERSIST) + @JoinColumn(name = "loan_repayment_schedule_id", nullable = false) + private LoanRepaymentScheduleInstallment installment; + + @Column(name = "principal_portion_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal principalPortion; + + @Column(name = "interest_portion_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal interestPortion; + + @Column(name = "fee_charges_portion_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal feeChargesPortion; + + @Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal penaltyChargesPortion; + + @Column(name = "amount", scale = 6, precision = 19) + private BigDecimal amount; + + protected LoanTransactionToRepaymentScheduleMapping() { + + } + + private LoanTransactionToRepaymentScheduleMapping(final LoanRepaymentScheduleInstallment installment, + final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, + final BigDecimal penaltyChargesPortion, final BigDecimal amount) { + this.installment = installment; + this.principalPortion = principalPortion; + this.interestPortion = interestPortion; + this.feeChargesPortion = feeChargesPortion; + this.penaltyChargesPortion = penaltyChargesPortion; + this.amount = amount; + } + + public static LoanTransactionToRepaymentScheduleMapping createFrom(final LoanRepaymentScheduleInstallment installment, + final Money principalPortion, final Money interestPortion, final Money feeChargesPortion, final Money penaltyChargesPortion) { + return new LoanTransactionToRepaymentScheduleMapping(installment, defaultToNullIfZero(principalPortion), + defaultToNullIfZero(interestPortion), defaultToNullIfZero(feeChargesPortion), defaultToNullIfZero(penaltyChargesPortion), + defaultToNullIfZero(principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion))); + } + + private static BigDecimal defaultToNullIfZero(final Money value) { + BigDecimal result = value.getAmount(); + if (value.isZero()) { + result = null; + } + return result; + } + + private BigDecimal defaultToZeroIfNull(final BigDecimal value) { + BigDecimal result = value; + if (value == null) { + result = BigDecimal.ZERO; + } + return result; + } + + public LoanRepaymentScheduleInstallment getLoanRepaymentScheduleInstallment() { + return this.installment; + } + + public void updateComponents(final Money principal, final Money interest, final Money feeCharges, final Money penaltyCharges) { + final MonetaryCurrency currency = principal.getCurrency(); + this.principalPortion = defaultToNullIfZero(getPrincipalPortion(currency).plus(principal)); + this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).plus(interest)); + updateChargesComponents(feeCharges, penaltyCharges); + updateAmount(); + } + + private void updateAmount() { + this.amount = defaultToZeroIfNull(getPrincipalPortion()).add(defaultToZeroIfNull(getInterestPortion())) + .add(defaultToZeroIfNull(getFeeChargesPortion())).add(defaultToZeroIfNull(getPenaltyChargesPortion())); + } + + public void setComponents(final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges, + final BigDecimal penaltyCharges) { + this.principalPortion = principal; + this.interestPortion = interest; + this.feeChargesPortion = feeCharges; + this.penaltyChargesPortion = penaltyCharges; + updateAmount(); + } + + private void updateChargesComponents(final Money feeCharges, final Money penaltyCharges) { + final MonetaryCurrency currency = feeCharges.getCurrency(); + this.feeChargesPortion = defaultToNullIfZero(getFeeChargesPortion(currency).plus(feeCharges)); + this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges)); + } + + public Money getPrincipalPortion(final MonetaryCurrency currency) { + return Money.of(currency, this.principalPortion); + } + + public Money getInterestPortion(final MonetaryCurrency currency) { + return Money.of(currency, this.interestPortion); + } + + public Money getFeeChargesPortion(final MonetaryCurrency currency) { + return Money.of(currency, this.feeChargesPortion); + } + + public Money getPenaltyChargesPortion(final MonetaryCurrency currency) { + return Money.of(currency, this.penaltyChargesPortion); + } + + public BigDecimal getPrincipalPortion() { + return this.principalPortion; + } + + public BigDecimal getInterestPortion() { + return this.interestPortion; + } + + public BigDecimal getFeeChargesPortion() { + return this.feeChargesPortion; + } + + public BigDecimal getPenaltyChargesPortion() { + return this.penaltyChargesPortion; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 3d50d72fb..e8bdddb4f 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -24,6 +24,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor; @@ -170,7 +171,9 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen * reverse the original transaction and update * changedTransactionDetail accordingly **/ - if (!LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) { + if (LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) { + loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings()); + } else{ loanTransaction.reverse(); loanTransaction.updateExternalId(null); changedTransactionDetail.getNewTransactionMappings().put(loanTransaction.getId(), newLoanTransaction); @@ -267,6 +270,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen if (amountToProcess != null) { transactionAmountUnprocessed = amountToProcess; } + List transactionMappings = new ArrayList<>(); for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { if (transactionAmountUnprocessed.isGreaterThanZero()) { @@ -277,23 +281,24 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen // current installment? if (isTransactionInAdvanceOfInstallment(installmentIndex, installments, transactionDate, transactionAmountUnprocessed)) { transactionAmountUnprocessed = handleTransactionThatIsPaymentInAdvanceOfInstallment(currentInstallment, - installments, loanTransaction, transactionDate, transactionAmountUnprocessed); + installments, loanTransaction, transactionDate, transactionAmountUnprocessed, transactionMappings); } else if (isTransactionALateRepaymentOnInstallment(installmentIndex, installments, loanTransaction.getTransactionDate())) { // does this result in a late payment of existing // installment? transactionAmountUnprocessed = handleTransactionThatIsALateRepaymentOfInstallment(currentInstallment, installments, - loanTransaction, transactionAmountUnprocessed); + loanTransaction, transactionAmountUnprocessed, transactionMappings); } else { // standard transaction transactionAmountUnprocessed = handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, - loanTransaction, transactionAmountUnprocessed); + loanTransaction, transactionAmountUnprocessed, transactionMappings); } } } installmentIndex++; } + loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings); return transactionAmountUnprocessed; } @@ -327,7 +332,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen if (loanTransaction.isChargePayment()) { feeAmount = feeCharges; } - if(unpaidCharge == null) break ; //All are trache charges + if (unpaidCharge == null) break; // All are trache charges final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining, installmentNumber, feeAmount); if (!amountPaidTowardsCharge.isZero()) { Set chargesPaidBies = loanTransaction.getLoanChargesPaid(); @@ -421,10 +426,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen /** * For late repayments, how should components of installment be paid off + * + * @param transactionMappings + * TODO */ protected abstract Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed); + final Money transactionAmountUnprocessed, final List transactionMappings); /** * This method is responsible for checking if the current transaction is 'an @@ -444,16 +452,24 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen /** * For early/'in advance' repayments. + * + * @param transactionMappings + * TODO */ protected abstract Money handleTransactionThatIsPaymentInAdvanceOfInstallment( final LoanRepaymentScheduleInstallment currentInstallment, final List installments, - final LoanTransaction loanTransaction, final LocalDate transactionDate, final Money paymentInAdvance); + final LoanTransaction loanTransaction, final LocalDate transactionDate, final Money paymentInAdvance, + final List transactionMappings); /** * For normal on-time repayments. + * + * @param transactionMappings + * TODO */ protected abstract Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed); + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + final List transactionMappings); /** * Invoked when a transaction results in an over-payment of the full loan. @@ -489,7 +505,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen public void handleRefund(LoanTransaction loanTransaction, MonetaryCurrency currency, List installments, final Set charges) { // TODO Auto-generated method stub - + List transactionMappings = new ArrayList<>(); final Comparator byDate = new Comparator() { @Override @@ -506,7 +522,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen if (outstanding.isLessThan(due)) { transactionAmountUnprocessed = handleRefundTransactionPaymentOfInstallment(currentInstallment, loanTransaction, - transactionAmountUnprocessed); + transactionAmountUnprocessed, transactionMappings); } @@ -527,7 +543,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen if (penaltyCharges.isGreaterThanZero()) { undoChargesPaidAmountBy(loanTransaction, penaltyCharges, loanPenalties, installmentNumber); } - + loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings); } /** @@ -537,9 +553,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen * Undoes principal, interest, fees and charges of this transaction based on * the repayment strategy * + * @param transactionMappings + * TODO + * */ protected abstract Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed); + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + final List transactionMappings); private void undoChargesPaidAmountBy(final LoanTransaction loanTransaction, final Money feeCharges, final Set charges, final Integer installmentNumber) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java index 592218e1f..4efe7126c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/CreocoreLoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -52,9 +53,10 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + final List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings); } /** @@ -65,9 +67,10 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, final List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, + transactionMappings); } /** @@ -75,7 +78,8 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + final List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -119,7 +123,10 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL principalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion); } - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); return transactionAmountRemaining; } @@ -129,10 +136,11 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { // dont do anything for with loan over-payment } - + @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); Money transactionAmountRemaining = transactionAmountUnprocessed; @@ -140,26 +148,29 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends AbstractL Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency()); Money feeChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); Money penaltyChargesPortion = Money.zero(transactionAmountRemaining.getCurrency()); - + principalPortion = currentInstallment.unpayPrincipalComponent(transactionDate, transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion); - - if(transactionAmountRemaining.isGreaterThanZero()) { - interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining); + + if (transactionAmountRemaining.isGreaterThanZero()) { + interestPortion = currentInstallment.unpayInterestComponent(transactionDate, transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); } - - if(transactionAmountRemaining.isGreaterThanZero()) { - feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining); - transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); + + if (transactionAmountRemaining.isGreaterThanZero()) { + feeChargesPortion = currentInstallment.unpayFeeChargesComponent(transactionDate, transactionAmountRemaining); + transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); } - - if(transactionAmountRemaining.isGreaterThanZero()) { - penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining); - transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); + + if (transactionAmountRemaining.isGreaterThanZero()) { + penaltyChargesPortion = currentInstallment.unpayPenaltyChargesComponent(transactionDate, transactionAmountRemaining); + transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java index cfbd10fb6..3670507a2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/EarlyPaymentLoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -29,7 +30,8 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + final List transactionMappings) { final MonetaryCurrency currency = paymentInAdvance.getCurrency(); Money transactionAmountRemaining = paymentInAdvance; @@ -69,7 +71,10 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); } - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } @@ -82,9 +87,10 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, + transactionMappings); } /** @@ -92,7 +98,8 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + final List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -134,13 +141,17 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); } - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); // final MonetaryCurrency currency = @@ -172,7 +183,10 @@ public class EarlyPaymentLoanRepaymentScheduleTransactionProcessor extends Abstr } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java index c769378d1..76c576045 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -38,9 +39,10 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, + transactionMappings); } @Override @@ -69,7 +71,8 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + final List transactionMappings) { final MonetaryCurrency currency = paymentInAdvance.getCurrency(); Money transactionAmountRemaining = paymentInAdvance; @@ -120,6 +123,10 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } @@ -128,7 +135,8 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -176,15 +184,20 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } @Override protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {} - + @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -215,7 +228,10 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java index 6f02d69fc..bce95e67c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -30,9 +31,10 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings); } /** @@ -43,9 +45,10 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, + transactionMappings); } /** @@ -53,7 +56,8 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -101,13 +105,17 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); } - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } - + @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); Money transactionAmountRemaining = transactionAmountUnprocessed; @@ -137,7 +145,10 @@ public class InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionPr } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/MifosStyleLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/MifosStyleLoanRepaymentScheduleTransactionProcessor.java index ddb9c7347..c0cb8cf01 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/MifosStyleLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/MifosStyleLoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -45,9 +46,10 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings); } /** @@ -57,9 +59,10 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, + transactionMappings); } /** @@ -67,7 +70,8 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -115,7 +119,10 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); } - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } @@ -123,10 +130,11 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) { // TODO - KW - dont do anything with loan over-payment for now } - + @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -154,7 +162,10 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends Abstrac transactionAmountRemaining = transactionAmountRemaining.minus(penaltyChargesPortion); } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java index d22a0c461..f0d0376aa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -30,9 +31,10 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings); } /** @@ -43,9 +45,10 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, transactionAmountUnprocessed, + transactionMappings); } /** @@ -53,7 +56,8 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -101,16 +105,22 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); } + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } - + @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); - //final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); + // final MonetaryCurrency currency = + // transactionAmountUnprocessed.getCurrency(); Money transactionAmountRemaining = transactionAmountUnprocessed; Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency()); Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency()); @@ -138,7 +148,10 @@ public class PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionPr } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java index 40a0f0f74..c76cebe04 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/impl/RBILoanRepaymentScheduleTransactionProcessor.java @@ -12,6 +12,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; +import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.AbstractLoanRepaymentScheduleTransactionProcessor; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; @@ -52,9 +53,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe @Override protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final LocalDate transactionDate, final Money paymentInAdvance) { + final LocalDate transactionDate, final Money paymentInAdvance, + List transactionMappings) { - return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance); + return handleTransactionThatIsOnTimePaymentOfInstallment(currentInstallment, loanTransaction, paymentInAdvance, transactionMappings); } /** @@ -64,7 +66,7 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe @Override protected Money handleTransactionThatIsALateRepaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, final List installments, final LoanTransaction loanTransaction, - final Money transactionAmountUnprocessed) { + final Money transactionAmountUnprocessed, List transactionMappings) { // pay of overdue and current interest due given transaction date final LocalDate transactionDate = loanTransaction.getTransactionDate(); @@ -80,6 +82,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe final Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency()); loanTransaction.updateComponents(principalPortion, interestWaivedPortion, feeChargesPortion, penaltyChargesPortion); + if (interestWaivedPortion.isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestWaivedPortion, feeChargesPortion, penaltyChargesPortion)); + } } else if (loanTransaction.isChargePayment()) { final Money principalPortion = Money.zero(currency); final Money interestPortion = Money.zero(currency); @@ -91,6 +97,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe transactionAmountRemaining = transactionAmountRemaining.minus(feeChargesPortion); } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } } else { final LoanRepaymentScheduleInstallment currentInstallmentBasedOnTransactionDate = nearestInstallment( @@ -112,6 +122,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe final Money principalPortion = Money.zero(currency); loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(installment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } } } @@ -124,6 +138,21 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe final Money interestPortion = Money.zero(currency); loanTransaction.updateComponents(principalPortion, interestPortion, Money.zero(currency), Money.zero(currency)); + boolean isMappingUpdated = false; + for (LoanTransactionToRepaymentScheduleMapping repaymentScheduleMapping : transactionMappings) { + if (repaymentScheduleMapping.getLoanRepaymentScheduleInstallment().getDueDate().equals(installment.getDueDate())) { + repaymentScheduleMapping.updateComponents(principalPortion, principalPortion.zero(), principalPortion.zero(), + principalPortion.zero()); + isMappingUpdated = true; + break; + } + } + if (!isMappingUpdated + && principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion) + .isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(installment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } } } } @@ -150,7 +179,8 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe */ @Override protected Money handleTransactionThatIsOnTimePaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + final List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency(); @@ -197,6 +227,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } @@ -213,7 +247,8 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe @Override protected Money handleRefundTransactionPaymentOfInstallment(final LoanRepaymentScheduleInstallment currentInstallment, - final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed) { + final LoanTransaction loanTransaction, final Money transactionAmountUnprocessed, + final List transactionMappings) { final LocalDate transactionDate = loanTransaction.getTransactionDate(); // final MonetaryCurrency currency = @@ -245,7 +280,10 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe } loanTransaction.updateComponents(principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion); - + if (principalPortion.plus(interestPortion).plus(feeChargesPortion).plus(penaltyChargesPortion).isGreaterThanZero()) { + transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(currentInstallment, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion)); + } return transactionAmountRemaining; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index b52ec983d..52a224a1f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -54,23 +54,23 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) { final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency(); - // 1. generate list of proposed schedule due dates + // generate list of proposed schedule due dates final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO); loanApplicationTerms.updateLoanEndDate(loanEndDate); - // 2. determine the total charges due at time of disbursement + // determine the total charges due at time of disbursement final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges); - // 3. setup variables for tracking important facts required for loan + // setup variables for tracking important facts required for loan // schedule generation. - Money principalDisbursed ; - - if(loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) { - principalDisbursed = loanApplicationTerms.getApprovedPrincipal(); - }else { - principalDisbursed = loanApplicationTerms.getPrincipal(); + Money principalToBeScheduled; + + if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) { + principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal(); + } else { + principalToBeScheduled = loanApplicationTerms.getPrincipal(); } - final MonetaryCurrency currency = principalDisbursed.getCurrency(); + final MonetaryCurrency currency = principalToBeScheduled.getCurrency(); final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments(); // variables for cumulative totals @@ -80,14 +80,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener BigDecimal totalPenaltyChargesCharged = BigDecimal.ZERO; BigDecimal totalRepaymentExpected = chargesDueAtTimeOfDisbursement; final BigDecimal totalOutstanding = BigDecimal.ZERO; - Money totalCumulativePrincipal = principalDisbursed.zero(); - Money totalCumulativeInterest = principalDisbursed.zero(); - Money totalOutstandingInterestPaymentDueToGrace = principalDisbursed.zero(); + Money totalCumulativePrincipal = principalToBeScheduled.zero(); + Money totalCumulativeInterest = principalToBeScheduled.zero(); + Money totalOutstandingInterestPaymentDueToGrace = principalToBeScheduled.zero(); final Collection periods = createNewLoanScheduleListWithDisbursementDetails(numberOfRepayments, loanApplicationTerms, chargesDueAtTimeOfDisbursement); - // 4. Determine the total interest owed over the full loan for FLAT + // Determine the total interest owed over the full loan for FLAT // interest method . Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged( this.paymentPeriodsInOneYearCalculator, mc); @@ -104,10 +104,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // Actual period Number as per the schedule int periodNumber = 1; - // Actual period Number and interest only repayments + // Actual period Number plus interest only repayments int instalmentNumber = 1; - Money outstandingBalance = principalDisbursed; + Money outstandingBalance = principalToBeScheduled; // Set fixed EMI Amount updateFixedInstallmentAmount(mc, loanApplicationTerms, actualRepaymentDate, periodNumber, outstandingBalance, holidayDetailDTO); @@ -120,7 +120,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // details to map BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms, periodStartDate, periods, chargesDueAtTimeOfDisbursement, disburseDetailMap, isInterestRecalculationRequired(loanApplicationTerms, transactions)); - principalDisbursed = principalDisbursed.zero().plus(disburseAmt); + principalToBeScheduled = principalToBeScheduled.zero().plus(disburseAmt); loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().zero().plus(disburseAmt)); outstandingBalance = outstandingBalance.zero().plus(disburseAmt); } @@ -228,16 +228,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // updates actual outstanding balance with new // disbursement detail outstandingBalance = outstandingBalance.plus(disburseDetail.getValue()); - principalDisbursed = principalDisbursed.plus(disburseDetail.getValue()); + principalToBeScheduled = principalToBeScheduled.plus(disburseDetail.getValue()); loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseDetail.getValue())); } } } - // Adds new interest repayment to the schedule as per the repayment + // process repayments to the schedule as per the repayment // transaction processor configuration - // will be added only if there is a loan repayment between the - // period for interest first repayment strategies + // will add a new schedule with interest till the transaction date + // for a loan repayment which falls between the + // two periods for interest first repayment strategies Money earlyPaidAmount = Money.zero(currency); LoanScheduleModelPeriod lastInstallment = null; if (isInterestRecalculationRequired(loanApplicationTerms, transactions)) { @@ -282,7 +283,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace(); } - Money principalForThisPeriod = principalDisbursed.zero(); + Money principalForThisPeriod = principalToBeScheduled.zero(); // applies all the applicable charges to the // newly @@ -290,17 +291,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, interestForThisinstallment, null); Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, - detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, + detail.getTransactionDate(), loanCharges, currency, principalInterest, principalToBeScheduled, totalCumulativeInterest, numberOfRepayments, true); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, - detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed, + detail.getTransactionDate(), loanCharges, currency, principalInterest, principalToBeScheduled, totalCumulativeInterest, numberOfRepayments, true); - // 8. sum up real totalInstallmentDue from + // sum up real totalInstallmentDue from // components final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment) .plus(feeChargesForInstallment).plus(penaltyChargesForInstallment); - // 9. create repayment period from parts + // create repayment period from parts installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment, @@ -488,7 +489,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap, periodStartDateApplicableForInterest, scheduledDueDate, daysInPeriodApplicableForInterest); - if (loanApplicationTerms.getFixedEmiAmount() != null + if (loanApplicationTerms.getFixedEmiAmount() != null && loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) != 1) { String errorMsg = "EMI amount must be greater than : " + principalInterestForThisPeriod.interest().getAmount(); throw new MultiDisbursementEmiAmountException(errorMsg, principalInterestForThisPeriod.interest().getAmount(), @@ -520,13 +521,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener BigDecimal fixedEmiAmount = loanApplicationTerms.getFixedEmiAmount(); reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal, totalCumulativePrincipal, periodNumber + 1, mc, holidayDetailDTO); - if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment() && fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) { + if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment() + && fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) { isEmiAmountChanged = true; } principalForThisPeriod = principalForThisPeriod.plus(earlyPaidAmount); } - // 6. update outstandingLoanBlance using current period + // update outstandingLoanBlance using current period // 'principalDue' outstandingBalance = outstandingBalance.minus(principalForThisPeriod.minus(reducedBalance)); @@ -534,21 +536,21 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener principalForThisPeriod = principalForThisPeriod.plus(outstandingBalance); outstandingBalance = outstandingBalance.zero(); } - - if(!isNextRepaymentAvailable){ - disburseDetailMap.clear(); + + if (!isNextRepaymentAvailable) { + disburseDetailMap.clear(); } // applies charges for the period PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, interestForThisinstallment, null); Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency, - principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, true); + principalInterest, principalToBeScheduled, totalCumulativeInterest, numberOfRepayments, true); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, - currency, principalInterest, principalDisbursed, totalCumulativeInterest, numberOfRepayments, true); + currency, principalInterest, principalToBeScheduled, totalCumulativeInterest, numberOfRepayments, true); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); - // 8. sum up real totalInstallmentDue from components + // sum up real totalInstallmentDue from components final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment).plus(feeChargesForInstallment) .plus(penaltyChargesForInstallment); @@ -559,7 +561,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener continue; } - // 9. create repayment period from parts + // create repayment period from parts LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, scheduledDueDate, principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue, false); @@ -614,7 +616,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final Money totalDue = principalForThisPeriod// .plus(interestForThisinstallment); - // 9. create and replaces repayment period + // create and replaces repayment period // from parts installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate, detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, @@ -643,7 +645,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal, totalCumulativePrincipal.plus(principalForThisPeriod.minus(principalProcessed)), periodNumber + 1, mc, holidayDetailDTO); - if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment() && fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) { + if (loanApplicationTerms.getAmortizationMethod().isEqualInstallment() + && fixedEmiAmount.compareTo(loanApplicationTerms.getFixedEmiAmount()) != 0) { isEmiAmountChanged = true; } @@ -694,18 +697,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace.zero(); } - // 7. determine fees and penalties for charges which depends on total + // determine fees and penalties for charges which depends on total // loan interest for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) { if (loanScheduleModelPeriod.isRepaymentPeriod()) { PrincipalInterest principalInterest = new PrincipalInterest(Money.of(currency, loanScheduleModelPeriod.principalDue()), Money.of(currency, loanScheduleModelPeriod.interestDue()), null); Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(), - loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest, principalDisbursed, - totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent()); + loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest, + principalToBeScheduled, totalCumulativeInterest, numberOfRepayments, + !loanScheduleModelPeriod.isRecalculatedInterestComponent()); Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(), - loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest, principalDisbursed, - totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent()); + loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest, + principalToBeScheduled, totalCumulativeInterest, numberOfRepayments, + !loanScheduleModelPeriod.isRecalculatedInterestComponent()); totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount()); totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount()); totalRepaymentExpected = totalRepaymentExpected.add(feeChargesForInstallment.getAmount()).add( @@ -730,7 +735,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener loanApplicationTerms.resetFixedEmiAmount(); - return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalDisbursed, + return LoanScheduleModel.from(periods, applicationCurrency, loanTermInDays, principalToBeScheduled, totalCumulativePrincipal.getAmount(), totalPrincipalPaid, totalCumulativeInterest.getAmount(), totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, totalOutstanding); } @@ -943,8 +948,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener updateFixedInstallmentAmount(mc, loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(), periodNumber, loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal), holidayDetailDTO); } - if(loanApplicationTerms.getAmortizationMethod().isEqualPrincipal()){ - loanApplicationTerms.updateFixedPrincipalAmount(mc, periodNumber, loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal)); + if (loanApplicationTerms.getAmortizationMethod().isEqualPrincipal()) { + loanApplicationTerms.updateFixedPrincipalAmount(mc, periodNumber, + loanApplicationTerms.getPrincipal().minus(totalCumulativePrincipal)); } reducePrincipal = reducePrincipal.zero(); break; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java index a4e69ee6f..e45e06356 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java @@ -6,7 +6,6 @@ package org.mifosplatform.portfolio.loanaccount.service; import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V275__loan_transaction_to_repayment_schedule_mapping.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V275__loan_transaction_to_repayment_schedule_mapping.sql new file mode 100644 index 000000000..13d7342bb --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V275__loan_transaction_to_repayment_schedule_mapping.sql @@ -0,0 +1,15 @@ +CREATE TABLE `m_loan_transaction_repayment_schedule_mapping` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `loan_transaction_id` BIGINT(20) NOT NULL, + `loan_repayment_schedule_id` BIGINT(20) NOT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `principal_portion_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `interest_portion_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `fee_charges_portion_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `penalty_charges_portion_derived` DECIMAL(19,6) NULL DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `FK_mappings_m_loan_transaction` (`loan_transaction_id`), + INDEX `FK_mappings_m_loan_repayment_schedule` (`loan_repayment_schedule_id`), + CONSTRAINT `FK_mappings_m_loan_repayment_schedule` FOREIGN KEY (`loan_repayment_schedule_id`) REFERENCES `m_loan_repayment_schedule` (`id`), + CONSTRAINT `FK_mappings_m_loan_transaction` FOREIGN KEY (`loan_transaction_id`) REFERENCES `m_loan_transaction` (`id`) +); \ No newline at end of file