From d15b40bf331f58ea9d14710020446fcae819fec8 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Wed, 13 Nov 2013 11:30:20 +0530 Subject: [PATCH] waive and payment of installment charge corrections --- .../V128__added_loan_installment_charge.sql | 19 +++++ .../loanaccount/data/LoanChargeData.java | 21 +----- .../portfolio/loanaccount/domain/Loan.java | 23 ++++-- .../loanaccount/domain/LoanCharge.java | 72 +++++++++++++------ .../domain/LoanInstallmentCharge.java | 46 ++++++++++-- .../LoanRepaymentScheduleInstallment.java | 6 +- ...RepaymentScheduleTransactionProcessor.java | 22 +++++- ...WritePlatformServiceJpaRepositoryImpl.java | 19 ++++- .../LoanChargeReadPlatformServiceImpl.java | 14 ++-- ...WritePlatformServiceJpaRepositoryImpl.java | 40 +++++++++-- 10 files changed, 209 insertions(+), 73 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V128__added_loan_installment_charge.sql diff --git a/mifosng-db/migrations/core_db/V128__added_loan_installment_charge.sql b/mifosng-db/migrations/core_db/V128__added_loan_installment_charge.sql new file mode 100755 index 000000000..ed95e725e --- /dev/null +++ b/mifosng-db/migrations/core_db/V128__added_loan_installment_charge.sql @@ -0,0 +1,19 @@ +CREATE TABLE `m_loan_installment_charge` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `loan_charge_id` BIGINT(20) NOT NULL, + `loan_schedule_id` BIGINT(20) NOT NULL, + `due_date` DATE NULL DEFAULT NULL, + `amount` DECIMAL(19,6) NOT NULL, + `amount_paid_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `amount_waived_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `amount_writtenoff_derived` DECIMAL(19,6) NULL DEFAULT NULL, + `amount_outstanding_derived` DECIMAL(19,6) NOT NULL DEFAULT '0.000000', + `is_paid_derived` TINYINT(1) NOT NULL DEFAULT '0', + `waived` TINYINT(1) NOT NULL DEFAULT '0', + `amount_through_charge_payment` DECIMAL(19,6) NULL DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_loan_charge_id_charge_schedule` FOREIGN KEY (`loan_charge_id`) REFERENCES `m_loan_charge` (`id`), + CONSTRAINT `FK_loan_schedule_id_charge_schedule` FOREIGN KEY (`loan_schedule_id`) REFERENCES `m_loan_repayment_schedule` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java index 18ffe63f6..bbecea402 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanChargeData.java @@ -21,45 +21,32 @@ import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; public class LoanChargeData { private final Long id; - @SuppressWarnings("unused") private final Long chargeId; - @SuppressWarnings("unused") private final String name; - @SuppressWarnings("unused") private final EnumOptionData chargeTimeType; private final LocalDate dueDate; - @SuppressWarnings("unused") private final EnumOptionData chargeCalculationType; private final BigDecimal percentage; - @SuppressWarnings("unused") private final BigDecimal amountPercentageAppliedTo; - @SuppressWarnings("unused") private final CurrencyData currency; - @SuppressWarnings("unused") private final BigDecimal amount; - @SuppressWarnings("unused") private final BigDecimal amountPaid; - @SuppressWarnings("unused") private final BigDecimal amountWaived; - @SuppressWarnings("unused") private final BigDecimal amountWrittenOff; private final BigDecimal amountOutstanding; - @SuppressWarnings("unused") private final BigDecimal amountOrPercentage; - @SuppressWarnings("unused") private final Collection chargeOptions; - @SuppressWarnings("unused") private final boolean penalty; private final EnumOptionData chargePaymentMode; @@ -68,17 +55,15 @@ public class LoanChargeData { private final boolean waived; - @SuppressWarnings("unused") private final boolean chargePayable; private final Long loanId; - @SuppressWarnings("unused") private final BigDecimal minCap; - @SuppressWarnings("unused") private final BigDecimal maxCap; + @SuppressWarnings("unused") private final Collection installmentChargeData; public static LoanChargeData template(final Collection chargeOptions) { @@ -181,7 +166,7 @@ public class LoanChargeData { this.installmentChargeData = installmentChargeData; } - public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, final Long loanId, Collection installmentChargeData) { + public LoanChargeData(final Long id, final LocalDate dueAsOfDate, final BigDecimal amountOutstanding, EnumOptionData chargeTimeType, final Long loanId, Collection installmentChargeData) { this.id = id; this.chargeId = null; this.name = null; @@ -191,7 +176,7 @@ public class LoanChargeData { this.amountWaived = null; this.amountWrittenOff = null; this.amountOutstanding = amountOutstanding; - this.chargeTimeType = null; + this.chargeTimeType = chargeTimeType; this.dueDate = dueAsOfDate; this.chargeCalculationType = null; this.percentage = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 23b7e7122..d731ee217 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -885,8 +885,9 @@ public class Loan extends AbstractPersistable { } public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) { + clearInstallmentLoanCharge(); this.repaymentScheduleInstallments.clear(); - + for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) { if (scheduledLoanInstallment.isRepaymentPeriod()) { @@ -901,6 +902,15 @@ public class Loan extends AbstractPersistable { updateLoanScheduleDependentDerivedFields(); updateLoanSummaryDerivedFields(); + + } + + private void clearInstallmentLoanCharge(){ + for(LoanCharge loanCharge:this.charges){ + if(loanCharge.isInstalmentFee()){ + loanCharge.clearLoanInstallmentCharges(); + } + } } private void updateLoanScheduleDependentDerivedFields() { @@ -2979,20 +2989,21 @@ public class Loan extends AbstractPersistable { return this.charges; } - public Set createInstallmentLoanCharges(final LoanCharge loanCharge){ + public Set generateInstallmentLoanCharges(final LoanCharge loanCharge){ Set loanChargePerInstallments = new HashSet(); if(loanCharge.isInstalmentFee()){ for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) { - BigDecimal amount = BigDecimal.ZERO;; + BigDecimal amount = BigDecimal.ZERO; if(loanCharge.getChargeCalculation().isFlat()){ - amount = loanCharge.amount(); + amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments())); }else{ amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment).getAmount(); } - LoanInstallmentCharge loanChargePerInstallment = new LoanInstallmentCharge(amount, loanCharge, installment); - loanChargePerInstallments.add(loanChargePerInstallment); + LoanInstallmentCharge loanInstallmentCharge = new LoanInstallmentCharge(amount, loanCharge, installment); + loanChargePerInstallments.add(loanInstallmentCharge); } } return loanChargePerInstallments; } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java index c94e0d854..9d99eccf3 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java @@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.loanaccount.domain; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; +import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; @@ -107,7 +108,7 @@ public class LoanCharge extends AbstractPersistable { @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loancharge", orphanRemoval = true) - private Set loanChargePerInstallments = new HashSet(); + private Set loanInstallmentCharge = new HashSet(); public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) { @@ -248,14 +249,14 @@ public class LoanCharge extends AbstractPersistable { } this.amountOrPercentage = chargeAmount; if(this.loan!=null && this.isInstalmentFee()){ - Set chargePerInstallments = this.loan.createInstallmentLoanCharges(this); - if(this.loanChargePerInstallments.isEmpty()){ - this.loanChargePerInstallments.addAll(chargePerInstallments); + Set chargePerInstallments = this.loan.generateInstallmentLoanCharges(this); + if(this.loanInstallmentCharge.isEmpty()){ + this.loanInstallmentCharge.addAll(chargePerInstallments); }else{ int index = 0; LoanInstallmentCharge[] loanChargePerInstallments = new LoanInstallmentCharge[chargePerInstallments.size()]; LoanInstallmentCharge[] loanChargePerInstallmentArray = chargePerInstallments.toArray(loanChargePerInstallments); - for(LoanInstallmentCharge chargePerInstallment:this.loanChargePerInstallments){ + for(LoanInstallmentCharge chargePerInstallment:this.loanInstallmentCharge){ chargePerInstallment.copyFrom(loanChargePerInstallmentArray[index++]); } } @@ -275,12 +276,18 @@ public class LoanCharge extends AbstractPersistable { this.amountOutstanding = calculateAmountOutstanding(currency); this.paid = false; this.waived = false; + for(LoanInstallmentCharge installmentCharge:loanInstallmentCharge){ + installmentCharge.resetPaidAmount(currency); + } } public void resetPaidAmount(final MonetaryCurrency currency) { this.amountPaid = BigDecimal.ZERO; this.amountOutstanding = calculateAmountOutstanding(currency); this.paid = false; + for(LoanInstallmentCharge installmentCharge:loanInstallmentCharge){ + installmentCharge.resetPaidAmount(currency); + } } public Money waive(final MonetaryCurrency currency, Integer loanInstallmentNumber) { @@ -347,14 +354,14 @@ public class LoanCharge extends AbstractPersistable { } this.amountOrPercentage = amount; if(this.loan !=null && this.isInstalmentFee()){ - Set chargePerInstallments = this.loan.createInstallmentLoanCharges(this); - if(this.loanChargePerInstallments.isEmpty()){ - this.loanChargePerInstallments.addAll(chargePerInstallments); + Set chargePerInstallments = this.loan.generateInstallmentLoanCharges(this); + if(this.loanInstallmentCharge.isEmpty()){ + this.loanInstallmentCharge.addAll(chargePerInstallments); }else{ int index = 0; LoanInstallmentCharge[] loanChargePerInstallments = new LoanInstallmentCharge[chargePerInstallments.size()]; LoanInstallmentCharge[] loanChargePerInstallmentArray = chargePerInstallments.toArray(loanChargePerInstallments); - for(LoanInstallmentCharge chargePerInstallment:this.loanChargePerInstallments){ + for(LoanInstallmentCharge chargePerInstallment:this.loanInstallmentCharge){ chargePerInstallment.copyFrom(loanChargePerInstallmentArray[index++]); } } @@ -410,14 +417,14 @@ public class LoanCharge extends AbstractPersistable { } this.amountOrPercentage = newValue; if(this.isInstalmentFee()){ - Set chargePerInstallments = this.loan.createInstallmentLoanCharges(this); - if(this.loanChargePerInstallments.isEmpty()){ - this.loanChargePerInstallments.addAll(chargePerInstallments); + Set chargePerInstallments = this.loan.generateInstallmentLoanCharges(this); + if(this.loanInstallmentCharge.isEmpty()){ + this.loanInstallmentCharge.addAll(chargePerInstallments); }else{ int index = 0; LoanInstallmentCharge[] loanChargePerInstallments = new LoanInstallmentCharge[chargePerInstallments.size()]; LoanInstallmentCharge[] loanChargePerInstallmentArray = chargePerInstallments.toArray(loanChargePerInstallments); - for(LoanInstallmentCharge chargePerInstallment:this.loanChargePerInstallments){ + for(LoanInstallmentCharge chargePerInstallment:this.loanInstallmentCharge){ chargePerInstallment.copyFrom(loanChargePerInstallmentArray[index++]); } } @@ -603,17 +610,18 @@ public class LoanCharge extends AbstractPersistable { } /** + * @param feeAmount TODO * @param processAmount * Amount used to pay off this charge * @return Actual amount paid on this charge */ - public Money updatePaidAmountBy(final Money incrementBy,final Integer installmentNumber) { + public Money updatePaidAmountBy(final Money incrementBy,final Integer installmentNumber, Money feeAmount) { Money processAmount = Money.zero(incrementBy.getCurrency()); if(this.isInstalmentFee()){ if(installmentNumber == null){ - processAmount = getUnpaidInstallmentLoanCharge().updatePaidAmountBy(incrementBy); + processAmount = getUnpaidInstallmentLoanCharge().updatePaidAmountBy(incrementBy, feeAmount); }else{ - processAmount = getInstallmentLoanCharge(installmentNumber).updatePaidAmountBy(incrementBy); + processAmount = getInstallmentLoanCharge(installmentNumber).updatePaidAmountBy(incrementBy, feeAmount); } }else{ processAmount = incrementBy; @@ -631,9 +639,7 @@ public class LoanCharge extends AbstractPersistable { amountPaidOnThisCharge = processAmount; amountPaidToDate = amountPaidToDate.plus(processAmount); this.amountPaid = amountPaidToDate.getAmount(); - - final Money amountExpected = Money.of(processAmount.getCurrency(), this.amount); - this.amountOutstanding = amountExpected.minus(amountPaidToDate).getAmount(); + this.amountOutstanding = calculateAmountOutstanding(incrementBy.getCurrency()); } this.paid = determineIfFullyPaid(); @@ -695,7 +701,7 @@ public class LoanCharge extends AbstractPersistable { public LoanInstallmentCharge getUnpaidInstallmentLoanCharge() { LoanInstallmentCharge unpaidChargePerInstallment = null; - for (LoanInstallmentCharge loanChargePerInstallment : this.loanChargePerInstallments) { + for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { if (loanChargePerInstallment.isPending() && (unpaidChargePerInstallment == null || unpaidChargePerInstallment.getRepaymentInstallment().getDueDate() .isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { @@ -706,7 +712,7 @@ public class LoanCharge extends AbstractPersistable { } public LoanInstallmentCharge getInstallmentLoanCharge(LocalDate periodDueDate) { - for (LoanInstallmentCharge loanChargePerInstallment : this.loanChargePerInstallments) { + for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { if (periodDueDate.isEqual(loanChargePerInstallment.getRepaymentInstallment().getDueDate())) { return loanChargePerInstallment; } @@ -715,11 +721,33 @@ public class LoanCharge extends AbstractPersistable { } public LoanInstallmentCharge getInstallmentLoanCharge(Integer installmentNumber) { - for (LoanInstallmentCharge loanChargePerInstallment : this.loanChargePerInstallments) { + for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber().intValue()) ){ return loanChargePerInstallment; } } return null; } + + public void clearLoanInstallmentCharges(){ + this.loanInstallmentCharge.clear(); + } + + public void addLoanInstallmentCharges(Collection installmentCharges){ + this.loanInstallmentCharge.addAll(installmentCharges); + } + + public boolean hasNoLoanInstallmentCharges(){ + return this.loanInstallmentCharge.isEmpty(); + } + + public LoanRepaymentScheduleInstallment fetchRepaymentInstallment(Money trasferedAmount){ + for (LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { + if (loanChargePerInstallment.isPending() && + trasferedAmount.getAmount().equals(loanChargePerInstallment.getAmountThroughChargePayment(trasferedAmount.getCurrency()).getAmount()) ){ + return loanChargePerInstallment.getRepaymentInstallment(); + } + } + return null; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanInstallmentCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanInstallmentCharge.java index 3d111ff6f..6adf43d43 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanInstallmentCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanInstallmentCharge.java @@ -44,6 +44,9 @@ public class LoanInstallmentCharge extends AbstractPersistable { @Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false) private BigDecimal amountOutstanding; + + @Column(name = "amount_through_charge_payment", scale = 6, precision = 19, nullable = true) + private BigDecimal amountThroughChargePayment; @Column(name = "is_paid_derived", nullable = false) private boolean paid = false; @@ -122,12 +125,24 @@ public class LoanInstallmentCharge extends AbstractPersistable { public BigDecimal getAmount() { return this.amount; } - + public Money getAmount(final MonetaryCurrency currency) { + return Money.of(currency, this.amount); + } + + private Money getAmountPaid(final MonetaryCurrency currency) { + return Money.of(currency, this.amountPaid); + } + public BigDecimal getAmountOutstanding() { return this.amountOutstanding; } + private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) { + return getAmount(currency).minus(getAmountWaived(currency)).minus(getAmountPaid(currency)).getAmount(); + } + + public boolean isPaid() { return this.paid; } @@ -145,7 +160,7 @@ public class LoanInstallmentCharge extends AbstractPersistable { return this.installment; } - public Money updatePaidAmountBy(final Money incrementBy) { + public Money updatePaidAmountBy(final Money incrementBy, Money feeAmount) { Money amountPaidToDate = Money.of(incrementBy.getCurrency(), this.amountPaid); final Money amountOutstanding = Money.of(incrementBy.getCurrency(), this.amountOutstanding); @@ -160,11 +175,9 @@ public class LoanInstallmentCharge extends AbstractPersistable { amountPaidOnThisCharge = incrementBy; amountPaidToDate = amountPaidToDate.plus(incrementBy); this.amountPaid = amountPaidToDate.getAmount(); - - final Money amountExpected = Money.of(incrementBy.getCurrency(), this.amount); - this.amountOutstanding = amountExpected.minus(amountPaidToDate).getAmount(); + this.amountOutstanding = calculateAmountOutstanding(incrementBy.getCurrency()); } - + this.amountThroughChargePayment = feeAmount.getAmount(); this.paid = determineIfFullyPaid(); return amountPaidOnThisCharge; @@ -174,4 +187,25 @@ public class LoanInstallmentCharge extends AbstractPersistable { public Money getAmountWrittenOff(final MonetaryCurrency currency) { return Money.of(currency, this.amountWrittenOff); } + + public void resetPaidAmount(final MonetaryCurrency currency) { + this.amountPaid = BigDecimal.ZERO; + this.amountOutstanding = calculateAmountOutstanding(currency); + this.paid = false; + } + + public void resetToOriginal(final MonetaryCurrency currency) { + this.amountPaid = BigDecimal.ZERO; + this.amountWaived = BigDecimal.ZERO; + this.amountWrittenOff = BigDecimal.ZERO; + this.amountOutstanding = calculateAmountOutstanding(currency); + this.paid = false; + this.waived = false; + } + + + public Money getAmountThroughChargePayment(final MonetaryCurrency currency) { + return Money.of(currency, this.amountThroughChargePayment); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index 2db1cdf49..5d1464966 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -13,6 +13,7 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @@ -105,11 +106,6 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus @Column(name = "obligations_met_on_date") private Date obligationsMetOnDate; - @LazyCollection(LazyCollectionOption.FALSE) - @OneToMany(cascade = CascadeType.ALL) - private Set loanChargePerInstallments = new HashSet(); - - protected LoanRepaymentScheduleInstallment() { this.installmentNumber = null; this.fromDate = null; 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 27f554dd2..0e7c1bd80 100644 --- 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 @@ -69,12 +69,24 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen final List transactionstoBeProcessed = new ArrayList(); for (final LoanTransaction loanTransaction : transactionsPostDisbursement) { if (loanTransaction.isChargePayment()) { + final List chargePaymentInstallments = new ArrayList(); final Set chargePaidBies = loanTransaction.getLoanChargesPaid(); final Set transferCharges = new HashSet(); for (final LoanChargePaidBy chargePaidBy : chargePaidBies) { - transferCharges.add(chargePaidBy.getLoanCharge()); + LoanCharge loanCharge = chargePaidBy.getLoanCharge(); + transferCharges.add(loanCharge); + if(loanCharge.isInstalmentFee()){ + LoanRepaymentScheduleInstallment installment = null; + if(loanCharge.isFeeCharge()){ + installment = loanCharge.fetchRepaymentInstallment(loanTransaction.getFeeChargesPortion(currency)); + }else{ + installment = loanCharge.fetchRepaymentInstallment(loanTransaction.getPenaltyChargesPortion(currency)); + } + if(installment !=null){ + chargePaymentInstallments.add(installment); + } + } } - final List chargePaymentInstallments = new ArrayList(); LocalDate startDate = disbursementDate; for (final LoanRepaymentScheduleInstallment installment : installments) { for (final LoanCharge loanCharge : transferCharges) { @@ -221,7 +233,11 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen Money amountRemaining = feeCharges; while(amountRemaining.isGreaterThanZero()) { final LoanCharge unpaidCharge = findEarliestUnpaidChargeFromUnOrderedSet(charges); - final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining,installmentNumber); + Money feeAmount = feeCharges.zero(); + if(loanTransaction.isChargePayment()){ + feeAmount = feeCharges; + } + final Money amountPaidTowardsCharge = unpaidCharge.updatePaidAmountBy(amountRemaining,installmentNumber, feeAmount); if (!amountPaidTowardsCharge.isZero()) { if (!loanTransaction.isChargePayment()) { final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(loanTransaction, unpaidCharge, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 51f64bcec..dac0bc64a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -5,8 +5,10 @@ */ package org.mifosplatform.portfolio.loanaccount.service; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -47,6 +49,8 @@ import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; +import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository; +import org.mifosplatform.portfolio.loanaccount.domain.LoanInstallmentCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; @@ -109,6 +113,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa private final CalendarInstanceRepository calendarInstanceRepository; private final SavingsAccountAssembler savingsAccountAssembler; private final AccountAssociationsRepository accountAssociationsRepository; + private final LoanChargeRepository loanChargeRepository; @Autowired public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper, @@ -122,7 +127,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final LoanSummaryWrapper loanSummaryWrapper, final GroupRepositoryWrapper groupRepository, final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, final CalendarRepository calendarRepository, final CalendarInstanceRepository calendarInstanceRepository, - final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository) { + final SavingsAccountAssembler savingsAccountAssembler, final AccountAssociationsRepository accountAssociationsRepository, + final LoanChargeRepository loanChargeRepository) { this.context = context; this.fromJsonHelper = fromJsonHelper; this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator; @@ -145,6 +151,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa this.calendarInstanceRepository = calendarInstanceRepository; this.savingsAccountAssembler = savingsAccountAssembler; this.accountAssociationsRepository = accountAssociationsRepository; + this.loanChargeRepository = loanChargeRepository; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -333,7 +340,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa if (changes.containsKey("recalculateLoanSchedule")) { changes.remove("recalculateLoanSchedule"); - + final JsonElement parsedQuery = this.fromJsonHelper.parse(command.json()); final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper); @@ -354,6 +361,14 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final Note note = Note.loanNote(existingLoanApplication, submittedOnNote); this.noteRepository.save(note); } + + for (final LoanCharge loanCharge : existingLoanApplication.charges()) { + if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ + Set chargePerInstallments = existingLoanApplication.generateInstallmentLoanCharges(loanCharge); + loanCharge.addLoanInstallmentCharges(chargePerInstallments); + this.loanChargeRepository.save(loanCharge); + } + } final Long calendarId = command.longValueOfParameterNamed("calendarId"); Calendar calendar = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java index 42894725b..38d29d941 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanChargeReadPlatformServiceImpl.java @@ -169,7 +169,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform public String schema() { return "lc.id as id, lc.due_for_collection_as_of_date as dueAsOfDate, " - + "lc.amount_outstanding_derived as amountOutstanding, " + "loan.id as loanId " + "from m_loan_charge lc " + + "lc.amount_outstanding_derived as amountOutstanding, " + + "lc.charge_time_enum as chargeTime, " + + "loan.id as loanId " + + "from m_loan_charge lc " + "join m_loan loan on loan.id = lc.loan_id "; } @@ -180,7 +183,10 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform final LocalDate dueAsOfDate = JdbcSupport.getLocalDate(rs, "dueAsOfDate"); final Long loanId = rs.getLong("loanId"); final BigDecimal amountOutstanding = rs.getBigDecimal("amountOutstanding"); - return new LoanChargeData(id, dueAsOfDate, amountOutstanding, loanId, null); + final int chargeTime = rs.getInt("chargeTime"); + final EnumOptionData chargeTimeType = ChargeEnumerations.chargeTimeType(chargeTime); + + return new LoanChargeData(id, dueAsOfDate, amountOutstanding, chargeTimeType, loanId, null); } } @@ -190,7 +196,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform String sql = "select " + rm.schema() + "where lic.loan_charge_id= ? "; if(onlyPaymentPendingCharges){ - sql = sql +"and lc.waived =0 and lc.is_paid_derived=0"; + sql = sql +"and lic.waived =0 and lic.is_paid_derived=0"; } return this.jdbcTemplate.query(sql, rm, new Object[] { loanChargeId }); } @@ -200,7 +206,7 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform public String schema() { return " lsi.installment as installmentNumber, lsi.duedate as dueAsOfDate, " + "lic.amount_outstanding_derived as amountOutstanding," - + "lic.amount as amount" + + "lic.amount as amount, " + "lic.is_paid_derived as paid, " + "lic.waived as waied " + "from m_loan_installment_charge lic " diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index e1ed9d496..d4a5f88e3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -266,6 +266,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf for (final LoanCharge loanCharge : loanCharges) { if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount()); + }else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ + Set chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); + loanCharge.addLoanInstallmentCharges(chargePerInstallments); + this.loanChargeRepository.save(loanCharge); } } final Locale locale = command.extractLocale(); @@ -384,6 +388,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } + final Set loanCharges = loan.charges(); + final Map disBuLoanCharges = new HashMap(); + for (final LoanCharge loanCharge : loanCharges) { + if (loanCharge.isDueAtDisbursement() && loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { + disBuLoanCharges.put(loanCharge.getId(), loanCharge.amount()); + }else if(loanCharge.isInstalmentFee() && loanCharge.hasNoLoanInstallmentCharges()){ + Set chargePerInstallments = loan.generateInstallmentLoanCharges(loanCharge); + loanCharge.addLoanInstallmentCharges(chargePerInstallments); + this.loanChargeRepository.save(loanCharge); + } + } + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); + for (final Map.Entry entrySet : disBuLoanCharges.entrySet()) { + final PortfolioAccountData savingAccountData = this.accountAssociationsReadPlatformService.retriveLoanAssociation(loan.getId()); + final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(actualDisbursementDate, entrySet.getValue(), + PortfolioAccountType.SAVINGS, PortfolioAccountType.LOAN, savingAccountData.accountId(), loan.getId(), "Loan Charge Payment", + locale, fmt, null, null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.getValue(), entrySet.getKey(), null); + this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); + } } return changes; } @@ -933,12 +957,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf Integer loanInstallmentNumber = null; if (loanCharge.isInstalmentFee()){ LoanInstallmentCharge chargePerInstallment = null; - final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); - final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber"); - if(dueDate !=null){ - chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate); - }else if(installmentNumber!=null){ - chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber); + if(!StringUtils.isBlank(command.json())){ + final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate"); + final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber"); + if(dueDate !=null){ + chargePerInstallment = loanCharge.getInstallmentLoanCharge(dueDate); + }else if(installmentNumber!=null){ + chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber); + } } if(chargePerInstallment == null){ chargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge(); @@ -1090,7 +1116,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf Collection chargePerInstallments = this.LoanChargeReadPlatformService.retrieveInstallmentLoanCharges(chargeData.getId(), true); PortfolioAccountData portfolioAccountData = null; for(LoanInstallmentChargeData installmentChargeData : chargePerInstallments){ - if(installmentChargeData.getDueDate().isAfter(new LocalDate())){ + if(!installmentChargeData.getDueDate().isAfter(new LocalDate())){ if(portfolioAccountData == null){ portfolioAccountData = this.accountAssociationsReadPlatformService .retriveLoanAssociation(chargeData.getLoanId());