ensure payment of charges due at disbursement are captured on a transaction that is automaticatly entered when disbursement of loan occurs.

This commit is contained in:
Keith Woodlock
2012-09-25 16:09:05 +01:00
parent 7656bccb21
commit 4830b85493
16 changed files with 143 additions and 113 deletions
@@ -0,0 +1,2 @@
ALTER TABLE `m_loan_transaction`
ADD COLUMN `charges_portion_derived` decimal(19,6) DEFAULT NULL AFTER `interest_waived_derived`;
@@ -30,4 +30,8 @@ public class DisbursementData {
public BigDecimal amount() { public BigDecimal amount() {
return this.principalDisbursed; return this.principalDisbursed;
} }
public boolean isDisbursed() {
return this.actualDisbursementDate != null;
}
} }
@@ -8,24 +8,36 @@ import java.util.Collection;
*/ */
public class LoanChargeData { public class LoanChargeData {
@SuppressWarnings("unused")
private final Long id; private final Long id;
@SuppressWarnings("unused")
private final Long chargeId; private final Long chargeId;
@SuppressWarnings("unused")
private final String name; private final String name;
@SuppressWarnings("unused")
private final CurrencyData currency; private final CurrencyData currency;
@SuppressWarnings("unused")
private final BigDecimal amount; private final BigDecimal amount;
@SuppressWarnings("unused")
private final EnumOptionData chargeTimeType; private final EnumOptionData chargeTimeType;
@SuppressWarnings("unused")
private final EnumOptionData chargeCalculationType; private final EnumOptionData chargeCalculationType;
private final Collection<ChargeData> chargeOptions; @SuppressWarnings("unused")
private final Collection<ChargeData> chargeOptions;
public static LoanChargeData template(final Collection<ChargeData> chargeOptions){ public static LoanChargeData template(final Collection<ChargeData> chargeOptions){
return new LoanChargeData(null,null,null,null,null,null,null, chargeOptions); return new LoanChargeData(null,null,null,null,null,null,null, chargeOptions);
} }
public LoanChargeData(Long id, Long chargeId, String name, CurrencyData currency, BigDecimal amount, public LoanChargeData(
EnumOptionData chargeTimeType, EnumOptionData chargeCalculationType) { final Long id,
final Long chargeId,
final String name,
final CurrencyData currency,
final BigDecimal amount,
final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType) {
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
@@ -37,9 +49,15 @@ public class LoanChargeData {
this.chargeOptions = null; this.chargeOptions = null;
} }
public LoanChargeData(Long id, Long chargeId, String name, CurrencyData currency, BigDecimal amount, public LoanChargeData(
EnumOptionData chargeTimeType, EnumOptionData chargeCalculationType, final Long id,
Collection<ChargeData> chargeOptions) { final Long chargeId,
final String name,
final CurrencyData currency,
final BigDecimal amount,
final EnumOptionData chargeTimeType,
final EnumOptionData chargeCalculationType,
final Collection<ChargeData> chargeOptions) {
this.id = id; this.id = id;
this.chargeId = chargeId; this.chargeId = chargeId;
this.name = name; this.name = name;
@@ -49,32 +67,4 @@ public class LoanChargeData {
this.chargeCalculationType = chargeCalculationType; this.chargeCalculationType = chargeCalculationType;
this.chargeOptions = chargeOptions; this.chargeOptions = chargeOptions;
} }
public Long getId() {
return id;
}
public Long getChargeId() {
return chargeId;
}
public String getName() {
return name;
}
public CurrencyData getCurrency() {
return currency;
}
public BigDecimal getAmount() {
return amount;
}
public EnumOptionData getChargeTimeType() {
return chargeTimeType;
}
public EnumOptionData getChargeCalculationType() {
return chargeCalculationType;
}
} }
@@ -43,10 +43,11 @@ public class LoanSchedulePeriodData {
public static LoanSchedulePeriodData disbursementOnlyPeriod( public static LoanSchedulePeriodData disbursementOnlyPeriod(
final LocalDate disbursementDate, final LocalDate disbursementDate,
final BigDecimal principalDisbursed, final BigDecimal principalDisbursed,
final BigDecimal chargesDueAtTimeOfDisbursement) { final BigDecimal chargesDueAtTimeOfDisbursement,
final boolean isDisbursed) {
Integer periodNumber = Integer.valueOf(0); Integer periodNumber = Integer.valueOf(0);
LocalDate from = null; LocalDate from = null;
return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, chargesDueAtTimeOfDisbursement); return new LoanSchedulePeriodData(periodNumber, from, disbursementDate, principalDisbursed, chargesDueAtTimeOfDisbursement, isDisbursed);
} }
public static LoanSchedulePeriodData repaymentOnlyPeriod(final Integer periodNumber, public static LoanSchedulePeriodData repaymentOnlyPeriod(final Integer periodNumber,
@@ -89,7 +90,8 @@ public class LoanSchedulePeriodData {
final LocalDate fromDate, final LocalDate fromDate,
final LocalDate dueDate, final LocalDate dueDate,
final BigDecimal principalDisbursed, final BigDecimal principalDisbursed,
final BigDecimal chargesDueAtTimeOfDisbursement) { final BigDecimal chargesDueAtTimeOfDisbursement,
final boolean isDisbursed) {
this.period = periodNumber; this.period = periodNumber;
this.fromDate = fromDate; this.fromDate = fromDate;
this.dueDate = dueDate; this.dueDate = dueDate;
@@ -112,14 +114,19 @@ public class LoanSchedulePeriodData {
this.interestOutstanding = null; this.interestOutstanding = null;
this.chargesDue = chargesDueAtTimeOfDisbursement; this.chargesDue = chargesDueAtTimeOfDisbursement;
this.chargesPaid = null; if (isDisbursed) {
this.chargesOutstanding = chargesDueAtTimeOfDisbursement; this.chargesPaid = chargesDueAtTimeOfDisbursement;
this.chargesOutstanding = BigDecimal.ZERO;
} else {
this.chargesPaid = BigDecimal.ZERO;
this.chargesOutstanding = chargesDueAtTimeOfDisbursement;
}
this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement; this.totalOriginalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalDueForPeriod = chargesDueAtTimeOfDisbursement; this.totalDueForPeriod = chargesDueAtTimeOfDisbursement;
this.totalPaidForPeriod = BigDecimal.ZERO; this.totalPaidForPeriod = this.chargesPaid;
this.totalWaivedForPeriod = null; this.totalWaivedForPeriod = null;
this.totalOutstandingForPeriod = chargesDueAtTimeOfDisbursement; this.totalOutstandingForPeriod = this.chargesOutstanding;
if (dueDate.isBefore(new LocalDate())) { if (dueDate.isBefore(new LocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod; this.totalOverdue = this.totalOutstandingForPeriod;
} else { } else {
@@ -39,7 +39,7 @@ public class Money implements Comparable<Money>, Serializable {
return total; return total;
} }
public static Money of(MonetaryCurrency currency, BigDecimal newAmount) { public static Money of(final MonetaryCurrency currency, final BigDecimal newAmount) {
return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), newAmount); return new Money(currency.getCode(), currency.getDigitsAfterDecimal(), newAmount);
} }
@@ -56,8 +56,12 @@ public class Money implements Comparable<Money>, Serializable {
private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount) { private Money(final String currencyCode, final int digitsAfterDecimal, final BigDecimal amount) {
this.currencyCode = currencyCode; this.currencyCode = currencyCode;
this.currencyDigitsAfterDecimal = digitsAfterDecimal; this.currencyDigitsAfterDecimal = digitsAfterDecimal;
BigDecimal amountStripped = amount.stripTrailingZeros(); if (amount != null) {
this.amount = amountStripped.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN); BigDecimal amountStripped = amount.stripTrailingZeros();
this.amount = amountStripped.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN);
} else {
this.amount = BigDecimal.ZERO;
}
} }
public Money copy() { public Money copy() {
@@ -79,7 +79,7 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
Money principalPortion = Money.zero(currency); Money principalPortion = Money.zero(currency);
Money interestWaivedPortion = Money.zero(currency); Money interestWaivedPortion = Money.zero(currency);
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion); loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
} }
} }
@@ -91,7 +91,7 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
Money interestPortion = Money.zero(currency); Money interestPortion = Money.zero(currency);
Money interestWaivedPortion = Money.zero(currency); Money interestWaivedPortion = Money.zero(currency);
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion); loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
} }
} }
@@ -125,15 +125,11 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
Money interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining); Money interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion); transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
Money principalPortion = currentInstallment Money principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
.payPrincipalComponent(transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
transactionAmountRemaining = transactionAmountRemaining
.minus(principalPortion);
Money interestWaivedPortion = Money.zero(transactionAmountRemaining Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
.getCurrency()); loanTransaction.updateComponents(principalPortion, interestPortion,interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
loanTransaction.updateComponents(principalPortion, interestPortion,
interestWaivedPortion);
return transactionAmountRemaining; return transactionAmountRemaining;
} }
@@ -90,10 +90,8 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
transactionAmountRemaining = transactionAmountRemaining transactionAmountRemaining = transactionAmountRemaining
.minus(principalPortion); .minus(principalPortion);
Money interestWaivedPortion = Money.zero(transactionAmountRemaining Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
.getCurrency()); loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
loanTransaction.updateComponents(principalPortion, interestPortion,
interestWaivedPortion);
return transactionAmountRemaining; return transactionAmountRemaining;
} }
@@ -84,8 +84,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
.getInterest(currency)); .getInterest(currency));
} }
loanTransaction.updateComponents(principalPortion, interestPortion, loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
interestWaivedPortion);
transactionAmountRemaining = transactionAmountRemaining transactionAmountRemaining = transactionAmountRemaining
.minus(principalPortion); .minus(principalPortion);
@@ -105,20 +104,14 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
Money transactionAmountRemaining = transactionAmountUnprocessed; Money transactionAmountRemaining = transactionAmountUnprocessed;
Money interestPortion = currentInstallment Money interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
.payInterestComponent(transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
transactionAmountRemaining = transactionAmountRemaining
.minus(interestPortion);
Money principalPortion = currentInstallment Money principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
.payPrincipalComponent(transactionAmountRemaining); transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
transactionAmountRemaining = transactionAmountRemaining
.minus(principalPortion);
Money interestWaivedPortion = Money.zero(transactionAmountRemaining Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
.getCurrency()); loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
loanTransaction.updateComponents(principalPortion, interestPortion,
interestWaivedPortion);
return transactionAmountRemaining; return transactionAmountRemaining;
} }
@@ -155,7 +155,6 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "interest_rebate_amount", scale = 6, precision = 19) @Column(name = "interest_rebate_amount", scale = 6, precision = 19)
private BigDecimal interestRebateOwed; private BigDecimal interestRebateOwed;
@SuppressWarnings("unused") // derived field used in GETs only at present
@Column(name = "total_charges_due_at_disbursement_derived", scale = 6, precision = 19) @Column(name = "total_charges_due_at_disbursement_derived", scale = 6, precision = 19)
private BigDecimal totalChargesDueAtDisbursement; private BigDecimal totalChargesDueAtDisbursement;
@@ -322,14 +321,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
} }
// if the loan application/contract is modified when repayments are already against it - then need to re-process it // if the loan application/contract is modified when repayments are already against it - then need to re-process it
List<LoanTransaction> repaymentsOrWaivers = new ArrayList<LoanTransaction>(); final List<LoanTransaction> repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement();
for (LoanTransaction transaction : this.loanTransactions) {
if (!transaction.isDisbursement() && transaction.isNotContra()) {
repaymentsOrWaivers.add(transaction);
}
}
LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
Collections.sort(repaymentsOrWaivers, transactionComparator);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy);
loanRepaymentScheduleTransactionProcessor.handleTransaction(repaymentsOrWaivers, getCurrency(), this.repaymentScheduleInstallments); loanRepaymentScheduleTransactionProcessor.handleTransaction(repaymentsOrWaivers, getCurrency(), this.repaymentScheduleInstallments);
@@ -481,8 +473,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
disburse(disbursedOn, loanLifecycleStateMachine); disburse(disbursedOn, loanLifecycleStateMachine);
} }
public void disburse(final LocalDate disbursedOn, public void disburse(final LocalDate disbursedOn, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
LoanLifecycleStateMachine loanLifecycleStateMachine) {
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus)); LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue(); this.loanStatus = statusEnum.getValue();
@@ -492,11 +483,24 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
.get(this.repaymentScheduleInstallments.size() - 1) .get(this.repaymentScheduleInstallments.size() - 1)
.getDueDate().toDateMidnight().toDate(); .getDueDate().toDateMidnight().toDate();
LoanTransaction loanTransaction = LoanTransaction.disbursement( // add disbursement transaction to track outgoing money from mfi to client
this.loanRepaymentScheduleDetail.getPrincipal(), disbursedOn); LoanTransaction loanTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(), disbursedOn);
loanTransaction.updateLoan(this); loanTransaction.updateLoan(this);
this.loanTransactions.add(loanTransaction); this.loanTransactions.add(loanTransaction);
// add repayment transaction to track incoming money from client to mfi for (charges due at time of disbursement)
if (getTotalChargesDueAtDisbursement().isGreaterThanZero()) {
// FIXME - add charges component to 'transactions'
LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getTotalChargesDueAtDisbursement(), disbursedOn);
Money zero = Money.zero(getCurrency());
chargesPayment.updateComponents(zero, zero, zero, getTotalChargesDueAtDisbursement());
chargesPayment.updateLoan(this);
this.loanTransactions.add(chargesPayment);
// should also pay-off the time-of-disbursement charges
}
if (disbursedOn.isBefore(getApprovedOnDate())) { if (disbursedOn.isBefore(getApprovedOnDate())) {
final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: " final String errorMessage = "The date on which a loan is disbursed cannot be before its approval date: "
+ getApprovedOnDate().toString(); + getApprovedOnDate().toString();
@@ -619,15 +623,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
"cannot.be.a.future.date", errorMessage, loanTransactionDate); "cannot.be.a.future.date", errorMessage, loanTransactionDate);
} }
List<LoanTransaction> repaymentsOrWaivers = new ArrayList<LoanTransaction>(); List<LoanTransaction> repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement();
for (LoanTransaction transaction : this.loanTransactions) {
if (!transaction.isDisbursement() && transaction.isNotContra()) {
repaymentsOrWaivers.add(transaction);
}
}
LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
Collections.sort(repaymentsOrWaivers, transactionComparator);
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy); final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy);
if (isTransactionChronologicallyLatest && !adjusted) { if (isTransactionChronologicallyLatest && !adjusted) {
@@ -639,6 +635,18 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
doPostLoanTransactionChecks(loanTransaction, loanLifecycleStateMachine); doPostLoanTransactionChecks(loanTransaction, loanLifecycleStateMachine);
} }
private List<LoanTransaction> retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement() {
List<LoanTransaction> repaymentsOrWaivers = new ArrayList<LoanTransaction>();
for (LoanTransaction transaction : this.loanTransactions) {
if (!transaction.isDisbursement() && transaction.isNotContra()) {
repaymentsOrWaivers.add(transaction);
}
}
LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
Collections.sort(repaymentsOrWaivers, transactionComparator);
return repaymentsOrWaivers;
}
private void doPostLoanTransactionChecks(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) { private void doPostLoanTransactionChecks(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
if (this.isOverPaid()) { if (this.isOverPaid()) {
@@ -1260,4 +1268,8 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
public Money getPrincpal() { public Money getPrincpal() {
return this.loanRepaymentScheduleDetail.getPrincipal(); return this.loanRepaymentScheduleDetail.getPrincipal();
} }
public Money getTotalChargesDueAtDisbursement() {
return Money.of(getCurrency(), this.totalChargesDueAtDisbursement);
}
} }
@@ -46,6 +46,9 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "interest_waived_derived", scale = 6, precision = 19) @Column(name = "interest_waived_derived", scale = 6, precision = 19)
private BigDecimal interestWaivedPortion = BigDecimal.ZERO; private BigDecimal interestWaivedPortion = BigDecimal.ZERO;
@Column(name = "charges_portion_derived", scale = 6, precision = 19, nullable = false)
private BigDecimal chargesPortion = BigDecimal.ZERO;
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
@Column(name = "transaction_date", nullable=false) @Column(name = "transaction_date", nullable=false)
private final Date dateOf; private final Date dateOf;
@@ -73,6 +76,10 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
return new LoanTransaction(LoanTransactionType.REPAYMENT, amount.getAmount(), paymentDate); return new LoanTransaction(LoanTransactionType.REPAYMENT, amount.getAmount(), paymentDate);
} }
public static LoanTransaction repaymentAtDisbursement(final Money amount, final LocalDate paymentDate) {
return new LoanTransaction(LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, amount.getAmount(), paymentDate);
}
public static LoanTransaction waiver(final Money waived, final LocalDate waiveDate) { public static LoanTransaction waiver(final Money waived, final LocalDate waiveDate) {
return new LoanTransaction(LoanTransactionType.WAIVED, waived.getAmount(), waiveDate); return new LoanTransaction(LoanTransactionType.WAIVED, waived.getAmount(), waiveDate);
} }
@@ -130,6 +137,10 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
return LoanTransactionType.DISBURSEMENT.equals(getTypeOf()); return LoanTransactionType.DISBURSEMENT.equals(getTypeOf());
} }
public boolean isRepaymentAtDisbursement() {
return LoanTransactionType.REPAYMENT_AT_DISBURSEMENT.equals(getTypeOf());
}
public boolean isWaiver() { public boolean isWaiver() {
return LoanTransactionType.WAIVED.equals(getTypeOf()) && isNotContra(); return LoanTransactionType.WAIVED.equals(getTypeOf()) && isNotContra();
} }
@@ -168,30 +179,40 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
* *
* This accumulates the values passed to the already existent values for each of the portions. * This accumulates the values passed to the already existent values for each of the portions.
*/ */
public void updateComponents(final Money principal, final Money interest, final Money interestWaivedPortion) { public void updateComponents(final Money principal, final Money interest, final Money interestWaivedPortion, final Money charges) {
MonetaryCurrency currency = principal.getCurrency(); MonetaryCurrency currency = principal.getCurrency();
this.principalPortion = getPrincipalPortion(currency).plus(principal).getAmount(); this.principalPortion = getPrincipalPortion(currency).plus(principal).getAmount();
this.interestPortion = getInterestPortion(currency).plus(interest).getAmount(); this.interestPortion = getInterestPortion(currency).plus(interest).getAmount();
this.interestWaivedPortion = getInterestWaivedPortion(currency).plus(interestWaivedPortion).getAmount(); this.interestWaivedPortion = getInterestWaivedPortion(currency).plus(interestWaivedPortion).getAmount();
this.chargesPortion = getChargesPortion(currency).plus(charges).getAmount();
} }
public Money getPrincipalPortion(MonetaryCurrency currency) { public Money getPrincipalPortion(final MonetaryCurrency currency) {
return Money.of(currency, principalPortion); return Money.of(currency, principalPortion);
} }
public Money getInterestPortion(MonetaryCurrency currency) { public Money getInterestPortion(final MonetaryCurrency currency) {
return Money.of(currency, interestPortion); return Money.of(currency, interestPortion);
} }
public Money getInterestWaivedPortion(MonetaryCurrency currency) { public Money getInterestWaivedPortion(final MonetaryCurrency currency) {
if (this.interestWaivedPortion == null) { if (this.interestWaivedPortion == null) {
this.interestWaivedPortion = BigDecimal.ZERO; this.interestWaivedPortion = BigDecimal.ZERO;
} }
return Money.of(currency, interestWaivedPortion); return Money.of(currency, interestWaivedPortion);
} }
public Money getChargesPortion(final MonetaryCurrency currency) {
if (this.chargesPortion == null) {
this.chargesPortion = BigDecimal.ZERO;
}
return Money.of(currency, chargesPortion);
}
public void resetDerivedComponents() { public void resetDerivedComponents() {
this.principalPortion = BigDecimal.ZERO; this.principalPortion = null;
this.interestPortion = BigDecimal.ZERO; this.interestPortion = null;
this.interestWaivedPortion = null;
this.chargesPortion = null;
} }
} }
@@ -6,7 +6,8 @@ public enum LoanTransactionType {
DISBURSEMENT(1, "loanTransactionType.disbursement"), // DISBURSEMENT(1, "loanTransactionType.disbursement"), //
REPAYMENT(2, "loanTransactionType.repayment"), // REPAYMENT(2, "loanTransactionType.repayment"), //
REVERSAL(3, "loanTransactionType.reversal"), // REVERSAL(3, "loanTransactionType.reversal"), //
WAIVED(4, "loanTransactionType.waiver"); WAIVED(4, "loanTransactionType.waiver"), //
REPAYMENT_AT_DISBURSEMENT(5, "loanTransactionType.repaymentAtDisbursement");
private final Integer value; private final Integer value;
private final String code; private final String code;
@@ -44,6 +45,9 @@ public enum LoanTransactionType {
case 4: case 4:
loanTransactionType = LoanTransactionType.WAIVED; loanTransactionType = LoanTransactionType.WAIVED;
break; break;
case 5:
loanTransactionType = LoanTransactionType.REPAYMENT_AT_DISBURSEMENT;
break;
default: default:
loanTransactionType = LoanTransactionType.INVALID; loanTransactionType = LoanTransactionType.INVALID;
break; break;
@@ -81,10 +81,8 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends
transactionAmountRemaining = transactionAmountRemaining transactionAmountRemaining = transactionAmountRemaining
.minus(principalPortion); .minus(principalPortion);
Money interestWaivedPortion = Money.zero(transactionAmountRemaining Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
.getCurrency()); loanTransaction.updateComponents(principalPortion, interestPortion,interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
loanTransaction.updateComponents(principalPortion, interestPortion,
interestWaivedPortion);
return transactionAmountRemaining; return transactionAmountRemaining;
} }
@@ -103,7 +103,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement); final LoanScheduleMapper rm = new LoanScheduleMapper(disbursement);
final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment"; final String sql = "select " + rm.loanScheduleSchema() + " where l.id = ? order by ls.loan_id, ls.installment";
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursement.disbursementDate(), disbursement.amount(), totalChargesAtDisbursement); final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursement.disbursementDate(), disbursement.amount(), totalChargesAtDisbursement, disbursement.isDisbursed());
final Collection<LoanSchedulePeriodData> repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); final Collection<LoanSchedulePeriodData> repaymentSchedulePeriods = this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
@@ -162,9 +162,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
// retrieve all loan transactions that are not invalid (0) and not // retrieve all loan transactions that are not invalid (0) and not
// disbursements (1) and have not been 'contra'ed by another transaction // disbursements (1) and have not been 'contra'ed by another transaction
// repayments at time of disbursement (e.g. charges)
String sql = "select " String sql = "select "
+ rm.LoanPaymentsSchema() + rm.LoanPaymentsSchema()
+ " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 1) and tr.contra_id is null order by tr.transaction_date ASC"; + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 1, 5) and tr.contra_id is null order by tr.transaction_date ASC";
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId }); return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
} catch (EmptyResultDataAccessException e) { } catch (EmptyResultDataAccessException e) {
@@ -69,7 +69,7 @@ public class EqualInstallmentsAmortizationLoanScheduleGenerator implements Amort
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement; BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
// create entries of disbursement period on loan schedule // create entries of disbursement period on loan schedule
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement); final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
periods.add(disbursementPeriod); periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0); int loanTermInDays = Integer.valueOf(0);
@@ -59,7 +59,7 @@ public class EqualPrincipalAmortizationLoanScheduleGenerator implements Amortiza
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement; BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
// create entries of disbursement period on loan schedule // create entries of disbursement period on loan schedule
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement); final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
periods.add(disbursementPeriod); periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0); int loanTermInDays = Integer.valueOf(0);
@@ -74,7 +74,7 @@ public class FlatMethodLoanScheduleGenerator implements LoanScheduleGenerator {
BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement; BigDecimal cumulativeChargesToDate = chargesDueAtTimeOfDisbursement;
// create entries of disbursement period on loan schedule // create entries of disbursement period on loan schedule
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement); final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(disbursementDate, principalDisbursed.getAmount(), chargesDueAtTimeOfDisbursement, false);
periods.add(disbursementPeriod); periods.add(disbursementPeriod);
int loanTermInDays = Integer.valueOf(0); int loanTermInDays = Integer.valueOf(0);