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:
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+14
-7
@@ -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() {
|
||||||
|
|||||||
+6
-10
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-14
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+27
-6
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-1
@@ -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;
|
||||||
|
|||||||
+2
-4
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -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) {
|
||||||
|
|||||||
+1
-1
@@ -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);
|
||||||
|
|||||||
+1
-1
@@ -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);
|
||||||
|
|||||||
+1
-1
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user