creocore: change waive functionality to support waive of interest only.
This commit is contained in:
@@ -40,6 +40,7 @@ import org.mifosng.platform.api.data.LoanPermissionData;
|
||||
import org.mifosng.platform.api.data.LoanProductLookup;
|
||||
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionNewData;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.api.data.StaffData;
|
||||
import org.mifosng.platform.api.data.TransactionProcessingStrategyData;
|
||||
@@ -414,9 +415,9 @@ public class LoansApiResource {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService
|
||||
.makeLoanRepayment(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
} else if (is(commandParam, "waiver")) {
|
||||
} else if (is(commandParam, "waiveinterest")) {
|
||||
EntityIdentifier identifier = this.loanWritePlatformService
|
||||
.waiveLoanAmount(command);
|
||||
.waiveInterestOnLoan(command);
|
||||
response = Response.ok().entity(identifier).build();
|
||||
}
|
||||
|
||||
@@ -436,26 +437,34 @@ public class LoansApiResource {
|
||||
@QueryParam("command") final String commandParam,
|
||||
@Context final UriInfo uriInfo) {
|
||||
|
||||
Set<String> typicalResponseParameters = new HashSet<String>(
|
||||
Arrays.asList("id", "transactionType", "date", "principal", "interest", "total", "totalWaived", "overpaid")
|
||||
);
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalResponseParameters);
|
||||
}
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
|
||||
LoanTransactionData transactionData = null;
|
||||
if (is(commandParam, "repayment")) {
|
||||
Set<String> typicalResponseParameters = new HashSet<String>(
|
||||
Arrays.asList("id", "transactionType", "date", "principal", "interest", "total", "totalWaived", "overpaid")
|
||||
);
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalResponseParameters);
|
||||
}
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
transactionData = this.loanReadPlatformService.retrieveNewLoanRepaymentDetails(loanId);
|
||||
} else if (is(commandParam, "waiver")) {
|
||||
transactionData = this.loanReadPlatformService.retrieveNewLoanWaiverDetails(loanId);
|
||||
return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, transactionData);
|
||||
} else if (is(commandParam, "waiveinterest")) {
|
||||
|
||||
final Set<String> typicalResponseParameters = new HashSet<String>(Arrays.asList("id", "type", "date", "currency", "amount"));
|
||||
|
||||
Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
|
||||
if (responseParameters.isEmpty()) {
|
||||
responseParameters.addAll(typicalResponseParameters);
|
||||
}
|
||||
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
|
||||
|
||||
LoanTransactionNewData newTransactionData = this.loanReadPlatformService.retrieveNewLoanWaiveInterestDetails(loanId);
|
||||
return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, newTransactionData);
|
||||
} else {
|
||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||
}
|
||||
|
||||
return this.apiJsonSerializerService.serializeLoanTransactionDataToJson(prettyPrint, responseParameters, transactionData);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package org.mifosng.platform.api.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a loan transaction.
|
||||
*/
|
||||
public class LoanTransactionNewData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Long id;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private EnumOptionData type;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private LocalDate date;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private CurrencyData currency;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BigDecimal amount;
|
||||
|
||||
public LoanTransactionNewData(
|
||||
final EnumOptionData transactionsType,
|
||||
final CurrencyData currency,
|
||||
final LocalDate date,
|
||||
final BigDecimal amount) {
|
||||
this.type = transactionsType;
|
||||
this.currency = currency;
|
||||
this.date = date;
|
||||
this.amount = amount;
|
||||
}
|
||||
}
|
||||
+3
@@ -23,6 +23,7 @@ import org.mifosng.platform.api.data.LoanAccountData;
|
||||
import org.mifosng.platform.api.data.LoanChargeData;
|
||||
import org.mifosng.platform.api.data.LoanProductData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionNewData;
|
||||
import org.mifosng.platform.api.data.NoteData;
|
||||
import org.mifosng.platform.api.data.OfficeData;
|
||||
import org.mifosng.platform.api.data.OfficeTransactionData;
|
||||
@@ -102,6 +103,8 @@ public interface ApiJsonSerializerService {
|
||||
String serializeLoanAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanAccountData loanAccount);
|
||||
|
||||
String serializeLoanTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanTransactionData transaction);
|
||||
|
||||
String serializeLoanTransactionDataToJson(boolean prettyPrint, Set<String> responseParameters, LoanTransactionNewData newTransactionData);
|
||||
|
||||
String serializeChargeDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ChargeData> charges);
|
||||
|
||||
|
||||
+15
@@ -25,6 +25,7 @@ import org.mifosng.platform.api.data.LoanAccountData;
|
||||
import org.mifosng.platform.api.data.LoanChargeData;
|
||||
import org.mifosng.platform.api.data.LoanProductData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionNewData;
|
||||
import org.mifosng.platform.api.data.NoteData;
|
||||
import org.mifosng.platform.api.data.OfficeData;
|
||||
import org.mifosng.platform.api.data.OfficeTransactionData;
|
||||
@@ -174,6 +175,9 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
|
||||
private static final Set<String> LOAN_TRANSACTION_DATA_PARAMETERS = new HashSet<String>(
|
||||
Arrays.asList("id", "transactionType", "date", "principal",
|
||||
"interest", "total", "totalWaived", "overpaid"));
|
||||
|
||||
private static final Set<String> LOAN_TRANSACTION_NEW_DATA_PARAMETERS = new HashSet<String>(
|
||||
Arrays.asList("id", "type", "date", "currency", "amount"));
|
||||
|
||||
private static final Set<String> CHARGES_DATA_PARAMETERS = new HashSet<String>(
|
||||
Arrays.asList("id", "name", "amount", "currency", "active",
|
||||
@@ -552,6 +556,17 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
|
||||
responseParameters);
|
||||
return helper.serializedJsonFrom(gsonDeserializer, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serializeLoanTransactionDataToJson(final boolean prettyPrint,
|
||||
final Set<String> responseParameters,
|
||||
final LoanTransactionNewData transaction) {
|
||||
final Gson gsonDeserializer = helper
|
||||
.createGsonBuilderWithParameterExclusionSerializationStrategy(
|
||||
LOAN_TRANSACTION_NEW_DATA_PARAMETERS, prettyPrint,
|
||||
responseParameters);
|
||||
return helper.serializedJsonFrom(gsonDeserializer, transaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serializeChargeDataToJson(final boolean prettyPrint,
|
||||
|
||||
+53
-36
@@ -55,7 +55,6 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
|
||||
* For late repayments, pay off in the same way as on-time payments,
|
||||
* interest first then principal.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
protected Money handleTransactionThatIsALateRepaymentOfInstallment(
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
@@ -64,37 +63,47 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
// pay of overdue and current interest due given transaction date
|
||||
MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
final MonetaryCurrency currency = transactionAmountUnprocessed.getCurrency();
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
|
||||
LoanRepaymentScheduleInstallment currentInstallmentBasedOnTransactionDate = nearestInstallment(loanTransaction.getTransactionDate(), installments);
|
||||
if (loanTransaction.isWaiver()) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||
|
||||
final Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
final Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
} else {
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (
|
||||
installment.isInterestDue(currency) &&
|
||||
(installment.isOverdueOn(loanTransaction.getTransactionDate()) || installment.getInstallmentNumber().equals(currentInstallmentBasedOnTransactionDate.getInstallmentNumber()))
|
||||
) {
|
||||
Money interestPortion = installment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
Money principalPortion = Money.zero(currency);
|
||||
Money interestWaivedPortion = Money.zero(currency);
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
LoanRepaymentScheduleInstallment currentInstallmentBasedOnTransactionDate = nearestInstallment(loanTransaction.getTransactionDate(), installments);
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (
|
||||
installment.isInterestDue(currency) &&
|
||||
(installment.isOverdueOn(loanTransaction.getTransactionDate()) || installment.getInstallmentNumber().equals(currentInstallmentBasedOnTransactionDate.getInstallmentNumber()))
|
||||
) {
|
||||
Money interestPortion = installment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
Money principalPortion = Money.zero(currency);
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
}
|
||||
}
|
||||
|
||||
// With whatever is remaining, pay off principal components of installments
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.isPrincipalNotCompleted(currency) && transactionAmountRemaining.isGreaterThanZero()) {
|
||||
Money principalPortion = installment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
Money interestPortion = Money.zero(currency);
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With whatever is remaining, pay off principal components of installments
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.isPrincipalNotCompleted(currency) && transactionAmountRemaining.isGreaterThanZero()) {
|
||||
Money principalPortion = installment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
Money interestPortion = Money.zero(currency);
|
||||
Money interestWaivedPortion = Money.zero(currency);
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
}
|
||||
}
|
||||
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
@@ -119,18 +128,26 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessor extends AbstractLo
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
|
||||
Money interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
Money principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion,interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
|
||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
|
||||
if (loanTransaction.isWaiver()) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||
} else {
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
|
||||
+17
-13
@@ -79,20 +79,24 @@ public class CreocoreLoanRepaymentScheduleTransactionProcessor extends
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
|
||||
Money interestPortion = currentInstallment
|
||||
.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(interestPortion);
|
||||
|
||||
Money principalPortion = currentInstallment
|
||||
.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(principalPortion);
|
||||
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
|
||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
|
||||
if (loanTransaction.isWaiver()) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||
} else {
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public class DefaultLoanLifecycleStateMachine implements LoanLifecycleStateMachi
|
||||
newState = stateOf(LoanStatus.APPROVED, allowedLoanStatuses);
|
||||
}
|
||||
break;
|
||||
case LOAN_REPAYMENT:
|
||||
case LOAN_REPAYMENT_OR_WAIVER:
|
||||
if (this.anyOfAllowedWhenComingFrom(from, LoanStatus.ACTIVE, LoanStatus.CLOSED, LoanStatus.OVERPAID)) {
|
||||
newState = stateOf(LoanStatus.ACTIVE, allowedLoanStatuses);
|
||||
} else {
|
||||
|
||||
+46
-30
@@ -65,31 +65,38 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction,
|
||||
final LocalDate transactionDate, final Money paymentInAdvance) {
|
||||
|
||||
|
||||
final MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||
Money transactionAmountRemaining = paymentInAdvance;
|
||||
MonetaryCurrency currency = paymentInAdvance.getCurrency();
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
|
||||
if (loanTransaction.isWaiver()) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||
} else {
|
||||
|
||||
// placeholders for tracking the breakdown of components relevant to
|
||||
// this transaction.
|
||||
Money principalPortion = Money.zero(currency);
|
||||
Money interestPortion = Money.zero(currency);
|
||||
Money interestWaivedPortion = Money.zero(currency);
|
||||
if (currentInstallment.isPrincipalNotCompleted(currency)) {
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
if (currentInstallment.isPrincipalCompleted(currency)) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(currentInstallment.getInterest(currency));
|
||||
}
|
||||
|
||||
if (currentInstallment.isPrincipalNotCompleted(currency)) {
|
||||
principalPortion = currentInstallment
|
||||
.payPrincipalComponent(transactionAmountRemaining);
|
||||
if (currentInstallment.isPrincipalCompleted(currency)) {
|
||||
interestWaivedPortion = currentInstallment
|
||||
.waiveInterestComponent(currentInstallment
|
||||
.getInterest(currency));
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(principalPortion);
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
@@ -101,18 +108,27 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
|
||||
Money interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
Money principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
|
||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
|
||||
if (loanTransaction.isWaiver()) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||
} else {
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
|
||||
@@ -582,65 +582,42 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void waive(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param amount The amount of interest to be waived.
|
||||
* @param transactionDate The date on which the interest waiver is to be processed against.
|
||||
* @param defaultLoanLifecycleStateMachine
|
||||
*
|
||||
* @return {@link LoanTransaction} details of executed transaction
|
||||
*/
|
||||
public LoanTransaction waiveInterest(
|
||||
final BigDecimal amount,
|
||||
final LocalDate transactionDate,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
final Money amountToWaive = Money.of(loanCurrency(), amount);
|
||||
final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(this, amountToWaive, transactionDate);
|
||||
|
||||
loanTransaction.updateLoan(this);
|
||||
this.loanTransactions.add(loanTransaction);
|
||||
|
||||
LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
||||
if (loanTransactionDate.isBefore(this.getDisbursementDate())) {
|
||||
final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
|
||||
+ getApprovedOnDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("waive",
|
||||
"cannot.be.before.disbursement.date", errorMessage,
|
||||
loanTransactionDate, this.getDisbursementDate());
|
||||
}
|
||||
|
||||
if (loanTransactionDate.isAfter(new LocalDate())) {
|
||||
final String errorMessage = "The transaction date cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("waive",
|
||||
"cannot.be.a.future.date", errorMessage, loanTransactionDate);
|
||||
}
|
||||
|
||||
if (getTotalOutstanding().isGreaterThan(this.getInArrearsTolerance())) {
|
||||
final String errorMessage = "Waiver is only allowed when the total outstanding amount left on loan ("
|
||||
+ getTotalOutstanding()
|
||||
+ ") is less than the in arrears tolerance setting of "
|
||||
+ getInArrearsTolerance().getAmount();
|
||||
throw new InvalidLoanStateTransitionException("waive",
|
||||
"cannot.exceed.in.arrears.tolerance.setting", errorMessage,
|
||||
getTotalOutstanding(), getInArrearsTolerance());
|
||||
}
|
||||
|
||||
Money waived = Money.of(getCurrency(), loanTransaction.getAmount());
|
||||
if (waived.isGreaterThan(this.getInArrearsTolerance())) {
|
||||
final String errorMessage = "The amount being waived cannot exceed the in arrears tolerance setting of "
|
||||
+ getInArrearsTolerance().getAmount();
|
||||
throw new InvalidLoanStateTransitionException("waive",
|
||||
"cannot.exceed.in.arrears.tolerance.setting", errorMessage,
|
||||
waived, getInArrearsTolerance());
|
||||
}
|
||||
|
||||
doPostLoanTransactionChecks(loanTransaction, loanLifecycleStateMachine);
|
||||
handleRepaymentOrWaiverTransaction(waiveInterestTransaction, loanLifecycleStateMachine, null);
|
||||
|
||||
return waiveInterestTransaction;
|
||||
}
|
||||
|
||||
public void updateTotalChargesDueAtDisbursement(){
|
||||
public void updateTotalChargesDueAtDisbursement() {
|
||||
this.totalChargesDueAtDisbursement = deriveSumTotalOfChargesDueAtDisbursement();
|
||||
}
|
||||
|
||||
public void makeRepayment(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
handleRepaymentTransaction(loanTransaction, loanLifecycleStateMachine, false);
|
||||
handleRepaymentOrWaiverTransaction(loanTransaction, loanLifecycleStateMachine, null);
|
||||
}
|
||||
|
||||
private void handleRepaymentTransaction(
|
||||
private void handleRepaymentOrWaiverTransaction(
|
||||
final LoanTransaction loanTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final boolean adjusted) {
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final LoanTransaction adjustedTransaction) {
|
||||
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT, LoanStatus.fromInt(this.loanStatus));
|
||||
LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, LoanStatus.fromInt(this.loanStatus));
|
||||
this.loanStatus = statusEnum.getValue();
|
||||
|
||||
loanTransaction.updateLoan(this);
|
||||
@@ -649,33 +626,44 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
this.loanTransactions.add(loanTransaction);
|
||||
|
||||
if (loanTransaction.isNotRepayment()) {
|
||||
final String errorMessage = "A transaction of type repayment was expected but not received.";
|
||||
if (loanTransaction.isNotRepayment() && loanTransaction.isNotWaiver()) {
|
||||
final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
|
||||
throw new InvalidLoanTransactionTypeException("transaction",
|
||||
"is.not.a.repayment.transaction", errorMessage);
|
||||
"is.not.a.repayment.or.waiver.transaction", errorMessage);
|
||||
}
|
||||
|
||||
LocalDate loanTransactionDate = loanTransaction.getTransactionDate();
|
||||
if (loanTransactionDate.isBefore(this.getDisbursementDate())) {
|
||||
final String errorMessage = "The transaction date cannot be before the loan disbursement date: "
|
||||
+ getApprovedOnDate().toString();
|
||||
throw new InvalidLoanStateTransitionException("repayment",
|
||||
throw new InvalidLoanStateTransitionException("transaction",
|
||||
"cannot.be.before.disbursement.date", errorMessage,
|
||||
loanTransactionDate, this.getDisbursementDate());
|
||||
}
|
||||
|
||||
if (loanTransactionDate.isAfter(new LocalDate())) {
|
||||
final String errorMessage = "The transaction date cannot be in the future.";
|
||||
throw new InvalidLoanStateTransitionException("repayment",
|
||||
throw new InvalidLoanStateTransitionException("transaction",
|
||||
"cannot.be.a.future.date", errorMessage, loanTransactionDate);
|
||||
}
|
||||
|
||||
List<LoanTransaction> repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement();
|
||||
if (loanTransaction.isWaiver()) {
|
||||
Money totalInterestOutstandingOnLoan = getTotalInterestOutstandingOnLoan();
|
||||
if (adjustedTransaction != null) {
|
||||
totalInterestOutstandingOnLoan = totalInterestOutstandingOnLoan.plus(adjustedTransaction.getAmount());
|
||||
}
|
||||
if (loanTransaction.getAmount(loanCurrency()).isGreaterThan(totalInterestOutstandingOnLoan)) {
|
||||
final String errorMessage = "The amount of interest to waive cannot be greater than total interest outstanding on loan.";
|
||||
throw new InvalidLoanStateTransitionException("waive.interest",
|
||||
"amount.exceeds.total.outstanding.interest", errorMessage, loanTransaction.getAmount(), totalInterestOutstandingOnLoan.getAmount());
|
||||
}
|
||||
}
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessor.determineProcessor(this.transactionProcessingStrategy);
|
||||
if (isTransactionChronologicallyLatest && !adjusted) {
|
||||
if (isTransactionChronologicallyLatest && adjustedTransaction == null) {
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(loanTransaction, getCurrency(), this.repaymentScheduleInstallments);
|
||||
} else {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(repaymentsOrWaivers, getCurrency(), this.repaymentScheduleInstallments);
|
||||
}
|
||||
|
||||
@@ -777,8 +765,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
public Money possibleNextRepaymentAmount() {
|
||||
MonetaryCurrency currency = this.loanRepaymentScheduleDetail
|
||||
.getPrincipal().getCurrency();
|
||||
MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getPrincipal().getCurrency();
|
||||
Money possibleNextRepaymentAmount = Money.zero(currency);
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
@@ -790,26 +777,43 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
return possibleNextRepaymentAmount;
|
||||
}
|
||||
|
||||
public LoanTransaction deriveDefaultInterestWaiverTransaction() {
|
||||
|
||||
final Money totalInterestOutstanding = getTotalInterestOutstandingOnLoan();
|
||||
Money possibleInterestToWaive = totalInterestOutstanding.copy();
|
||||
LocalDate transactionDate = new LocalDate();
|
||||
|
||||
if (totalInterestOutstanding.isGreaterThanZero()) {
|
||||
// find earliest known instance of overdue interest and default to that
|
||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||
|
||||
final Money outstandingForPeriod = scheduledRepayment.getInterestOutstanding(loanCurrency());
|
||||
if (scheduledRepayment.isOverdueOn(new LocalDate()) && scheduledRepayment.isNotFullyCompleted() && outstandingForPeriod.isGreaterThanZero()) {
|
||||
transactionDate = scheduledRepayment.getDueDate();
|
||||
possibleInterestToWaive = outstandingForPeriod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LoanTransaction.waiver(this, possibleInterestToWaive, transactionDate);
|
||||
}
|
||||
|
||||
public void adjustExistingTransaction(
|
||||
LoanTransaction transactionForAdjustment,
|
||||
LoanTransaction newTransactionDetail,
|
||||
LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
final LoanTransaction transactionForAdjustment,
|
||||
final LoanTransaction newTransactionDetail,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||
|
||||
if (transactionForAdjustment.isNotRepayment()
|
||||
&& transactionForAdjustment.isNotWaiver()) {
|
||||
if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()) {
|
||||
final String errorMessage = "A transaction of type repayment or waiver was expected but not received.";
|
||||
throw new InvalidLoanTransactionTypeException("transaction",
|
||||
"is.not.a.repayment.or.waiver.transaction", errorMessage);
|
||||
}
|
||||
|
||||
transactionForAdjustment.contra();
|
||||
if (newTransactionDetail.isRepayment()) {
|
||||
handleRepaymentTransaction(newTransactionDetail, loanLifecycleStateMachine, true);
|
||||
}
|
||||
|
||||
if (newTransactionDetail.isWaiver()) {
|
||||
waive(newTransactionDetail, loanLifecycleStateMachine);
|
||||
if (newTransactionDetail.isRepayment() || newTransactionDetail.isWaiver()) {
|
||||
handleRepaymentOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine, transactionForAdjustment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,8 +1244,7 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
private Money getTotalInterestOnLoan() {
|
||||
Money cumulativeInterest = Money.zero(this.loanRepaymentScheduleDetail
|
||||
.getPrincipal().getCurrency());
|
||||
Money cumulativeInterest = Money.zero(loanCurrency());
|
||||
|
||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||
cumulativeInterest = cumulativeInterest.plus(scheduledRepayment
|
||||
@@ -1250,6 +1253,31 @@ public class Loan extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
return cumulativeInterest;
|
||||
}
|
||||
|
||||
private Money getTotalInterestOutstandingOnLoan() {
|
||||
Money cumulativeInterest = Money.zero(loanCurrency());
|
||||
|
||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||
cumulativeInterest = cumulativeInterest.plus(scheduledRepayment.getInterestOutstanding(loanCurrency()));
|
||||
}
|
||||
|
||||
return cumulativeInterest;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Money getTotalInterestOverdueOnLoan() {
|
||||
Money cumulativeInterestOverdue = Money.zero(this.loanRepaymentScheduleDetail.getPrincipal().getCurrency());
|
||||
|
||||
for (LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||
|
||||
final Money interestOutstandingForPeriod = scheduledRepayment.getInterestOutstanding(loanCurrency());
|
||||
if (scheduledRepayment.isOverdueOn(new LocalDate())) {
|
||||
cumulativeInterestOverdue = cumulativeInterestOverdue.plus(interestOutstandingForPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
return cumulativeInterestOverdue;
|
||||
}
|
||||
|
||||
private Money getTotalPrincipalOnLoan() {
|
||||
Money cumulativePrincipal = Money.zero(this.loanRepaymentScheduleDetail
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.mifosng.platform.loan.domain;
|
||||
|
||||
public enum LoanEvent {
|
||||
|
||||
LOAN_CREATED, LOAN_REJECTED, LOAN_WITHDRAWN, LOAN_APPROVED, LOAN_APPROVAL_UNDO, LOAN_DISBURSED, LOAN_DISBURSAL_UNDO, LOAN_REPAYMENT,
|
||||
LOAN_CREATED, LOAN_REJECTED, LOAN_WITHDRAWN, LOAN_APPROVED, LOAN_APPROVAL_UNDO, LOAN_DISBURSED, LOAN_DISBURSAL_UNDO, LOAN_REPAYMENT_OR_WAIVER,
|
||||
//
|
||||
REPAID_IN_FULL, LOAN_WRITE_OFF, LOAN_RESCHEDULE, INTERST_REBATE_OWED, LOAN_OVERPAYMENT;
|
||||
}
|
||||
|
||||
+18
-10
@@ -93,6 +93,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom<Ap
|
||||
return Money.of(currency, this.principalCompleted);
|
||||
}
|
||||
|
||||
public Money getPrincipalOutstanding(final MonetaryCurrency currency) {
|
||||
return getPrincipal(currency).minus(getPrincipalCompleted(currency));
|
||||
}
|
||||
|
||||
public Money getInterest(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.interest);
|
||||
}
|
||||
@@ -104,15 +108,23 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom<Ap
|
||||
public Money getInterestWaived(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.interestWaived);
|
||||
}
|
||||
|
||||
public Money getInterestOutstanding(final MonetaryCurrency currency) {
|
||||
return getInterest(currency).minus(getInterestCompleted(currency).plus(getInterestWaived(currency)));
|
||||
}
|
||||
|
||||
public boolean isInterestDue(final MonetaryCurrency currency) {
|
||||
return getInterest(currency).minus(getInterestCompleted(currency).plus(getInterestWaived(currency))).isGreaterThanZero();
|
||||
}
|
||||
|
||||
public Money getTotal(MonetaryCurrency currency) {
|
||||
public Money getTotal(final MonetaryCurrency currency) {
|
||||
return getPrincipal(currency).plus(getInterest(currency));
|
||||
}
|
||||
|
||||
public Money getTotalOutstanding(final MonetaryCurrency currency) {
|
||||
return getPrincipalOutstanding(currency).plus(getInterestOutstanding(currency));
|
||||
}
|
||||
|
||||
public void updateLoan(final Loan loan) {
|
||||
this.loan = loan;
|
||||
}
|
||||
@@ -151,12 +163,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom<Ap
|
||||
|
||||
public Money payInterestComponent(final Money transactionAmountRemaining) {
|
||||
|
||||
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||
|
||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||
Money interestPortionOfTransaction = Money.zero(currency);
|
||||
|
||||
Money interestExpected = getInterest(currency);
|
||||
Money interestDue = interestExpected.minus(getInterestCompleted(currency));
|
||||
final Money interestDue = getInterestOutstanding(currency);
|
||||
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
|
||||
this.interestCompleted = getInterestCompleted(currency).plus(interestDue).getAmount();
|
||||
interestPortionOfTransaction = interestPortionOfTransaction.plus(interestDue);
|
||||
@@ -172,11 +182,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom<Ap
|
||||
|
||||
public Money payPrincipalComponent(final Money transactionAmountRemaining) {
|
||||
|
||||
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||
Money principalPortionOfTransaction = Money.zero(currency);
|
||||
|
||||
Money principalExpected = getPrincipal(currency);
|
||||
Money principalDue = principalExpected.minus(getPrincipalCompleted(currency));
|
||||
final Money principalDue = getPrincipalOutstanding(currency);
|
||||
if (transactionAmountRemaining.isGreaterThanOrEqualTo(principalDue)) {
|
||||
this.principalCompleted = getPrincipalCompleted(currency).plus(principalDue).getAmount();
|
||||
principalPortionOfTransaction = principalPortionOfTransaction.plus(principalDue);
|
||||
@@ -194,8 +203,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom<Ap
|
||||
MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
|
||||
Money waivedInterestPortionOfTransaction = Money.zero(currency);
|
||||
|
||||
Money interestExpected = getInterest(currency);
|
||||
Money interestDue = interestExpected.minus(getInterestCompleted(currency));
|
||||
Money interestDue = getInterestOutstanding(currency);
|
||||
if (transactionAmountRemaining.isGreaterThanOrEqualTo(interestDue)) {
|
||||
this.interestWaived = getInterestWaived(currency).plus(interestDue).getAmount();
|
||||
waivedInterestPortionOfTransaction = waivedInterestPortionOfTransaction.plus(interestDue);
|
||||
|
||||
+9
-8
@@ -69,23 +69,23 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
|
||||
}
|
||||
|
||||
public static LoanTransaction disbursement(final Money amount, final LocalDate disbursementDate) {
|
||||
return new LoanTransaction(LoanTransactionType.DISBURSEMENT, amount.getAmount(), disbursementDate);
|
||||
return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, amount.getAmount(), disbursementDate);
|
||||
}
|
||||
|
||||
public static LoanTransaction repayment(final Money amount, final LocalDate paymentDate) {
|
||||
return new LoanTransaction(LoanTransactionType.REPAYMENT, amount.getAmount(), paymentDate);
|
||||
return new LoanTransaction(null, 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);
|
||||
return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, amount.getAmount(), paymentDate);
|
||||
}
|
||||
|
||||
public static LoanTransaction waiver(final Money waived, final LocalDate waiveDate) {
|
||||
return new LoanTransaction(LoanTransactionType.WAIVED, waived.getAmount(), waiveDate);
|
||||
public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) {
|
||||
return new LoanTransaction(loan, LoanTransactionType.WAIVED, waived.getAmount(), waiveDate);
|
||||
}
|
||||
|
||||
private static LoanTransaction contra(LoanTransaction originalTransaction) {
|
||||
LoanTransaction contra = new LoanTransaction(LoanTransactionType.REVERSAL, originalTransaction.getAmount().negate(), new LocalDate(originalTransaction.getDateOf()));
|
||||
private static LoanTransaction contra(final LoanTransaction originalTransaction) {
|
||||
LoanTransaction contra = new LoanTransaction(null, LoanTransactionType.REVERSAL, originalTransaction.getAmount().negate(), new LocalDate(originalTransaction.getDateOf()));
|
||||
contra.updateContra(originalTransaction);
|
||||
return contra;
|
||||
}
|
||||
@@ -94,7 +94,8 @@ public class LoanTransaction extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.contra = transaction;
|
||||
}
|
||||
|
||||
private LoanTransaction(final LoanTransactionType type, final BigDecimal amount, final LocalDate date) {
|
||||
private LoanTransaction(final Loan loan, final LoanTransactionType type, final BigDecimal amount, final LocalDate date) {
|
||||
this.loan = loan;
|
||||
this.typeOf = type.getValue();
|
||||
this.amount = amount;
|
||||
this.dateOf = date.toDateMidnight().toDate();
|
||||
|
||||
+17
-2
@@ -3,13 +3,28 @@ package org.mifosng.platform.loan.domain;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
*
|
||||
* Sort loan transactions by transaction date and transaction type placing
|
||||
*/
|
||||
public class LoanTransactionComparator implements Comparator<LoanTransaction> {
|
||||
|
||||
@Override
|
||||
public int compare(final LoanTransaction o1, final LoanTransaction o2) {
|
||||
return o1.getTransactionDate().compareTo(o2.getTransactionDate());
|
||||
int compareResult = 0;
|
||||
final int comparsion = o1.getTransactionDate().compareTo(o2.getTransactionDate());
|
||||
if (comparsion == 0) {
|
||||
// equal transaction dates
|
||||
if (o1.isWaiver() && o2.isNotWaiver()) {
|
||||
compareResult = -1;
|
||||
} else if (o1.isNotWaiver() && o2.isWaiver()) {
|
||||
compareResult = 1;
|
||||
} else {
|
||||
compareResult = 0;
|
||||
}
|
||||
} else {
|
||||
compareResult = comparsion;
|
||||
}
|
||||
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-14
@@ -68,22 +68,26 @@ public class MifosStyleLoanRepaymentScheduleTransactionProcessor extends
|
||||
final LoanRepaymentScheduleInstallment currentInstallment,
|
||||
final LoanTransaction loanTransaction,
|
||||
final Money transactionAmountUnprocessed) {
|
||||
|
||||
|
||||
Money transactionAmountRemaining = transactionAmountUnprocessed;
|
||||
|
||||
Money interestPortion = currentInstallment
|
||||
.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(interestPortion);
|
||||
|
||||
Money principalPortion = currentInstallment
|
||||
.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining
|
||||
.minus(principalPortion);
|
||||
|
||||
Money interestWaivedPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion,interestWaivedPortion, Money.zero(transactionAmountRemaining.getCurrency()));
|
||||
|
||||
Money principalPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money interestPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
Money chargesPortion = Money.zero(transactionAmountRemaining.getCurrency());
|
||||
|
||||
if (loanTransaction.isWaiver()) {
|
||||
interestWaivedPortion = currentInstallment.waiveInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestWaivedPortion);
|
||||
} else {
|
||||
|
||||
interestPortion = currentInstallment.payInterestComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(interestPortion);
|
||||
|
||||
principalPortion = currentInstallment.payPrincipalComponent(transactionAmountRemaining);
|
||||
transactionAmountRemaining = transactionAmountRemaining.minus(principalPortion);
|
||||
}
|
||||
|
||||
loanTransaction.updateComponents(principalPortion, interestPortion, interestWaivedPortion, chargesPortion);
|
||||
return transactionAmountRemaining;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ import org.mifosng.platform.api.data.LoanBasicDetailsData;
|
||||
import org.mifosng.platform.api.data.LoanPermissionData;
|
||||
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionNewData;
|
||||
|
||||
public interface LoanReadPlatformService {
|
||||
|
||||
@@ -29,7 +30,7 @@ public interface LoanReadPlatformService {
|
||||
|
||||
LoanTransactionData retrieveNewLoanRepaymentDetails(Long loanId);
|
||||
|
||||
LoanTransactionData retrieveNewLoanWaiverDetails(Long loanId);
|
||||
LoanTransactionNewData retrieveNewLoanWaiveInterestDetails(Long loanId);
|
||||
|
||||
LoanTransactionData retrieveLoanTransactionDetails(Long loanId, Long transactionId);
|
||||
}
|
||||
+9
-18
@@ -20,6 +20,7 @@ import org.mifosng.platform.api.data.LoanProductData;
|
||||
import org.mifosng.platform.api.data.LoanRepaymentTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanSchedulePeriodData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionData;
|
||||
import org.mifosng.platform.api.data.LoanTransactionNewData;
|
||||
import org.mifosng.platform.api.data.MoneyData;
|
||||
import org.mifosng.platform.client.service.ClientReadPlatformService;
|
||||
import org.mifosng.platform.currency.domain.ApplicationCurrency;
|
||||
@@ -285,40 +286,30 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanTransactionData retrieveNewLoanWaiverDetails(final Long loanId) {
|
||||
public LoanTransactionNewData retrieveNewLoanWaiveInterestDetails(final Long loanId) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
// TODO - OPTIMIZE - write simple sql query to fetch back date of
|
||||
// possible next transaction date.
|
||||
// TODO - OPTIMIZE - write simple sql query to fetch back overdue interest that can be waived along with the date of repayment period interest is overdue.
|
||||
Loan loan = this.loanRepository.findOne(loanId);
|
||||
if (loan == null) {
|
||||
throw new LoanNotFoundException(loanId);
|
||||
}
|
||||
|
||||
final String currencyCode = loan.repaymentScheduleDetail()
|
||||
.getPrincipal().getCurrencyCode();
|
||||
ApplicationCurrency currency = this.applicationCurrencyRepository
|
||||
.findOneByCode(currencyCode);
|
||||
final String currencyCode = loan.repaymentScheduleDetail().getPrincipal().getCurrencyCode();
|
||||
ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(currencyCode);
|
||||
if (currency == null) {
|
||||
throw new CurrencyNotFoundException(currencyCode);
|
||||
}
|
||||
|
||||
CurrencyData currencyData = new CurrencyData(currency.getCode(),
|
||||
currency.getName(), currency.getDecimalPlaces(),
|
||||
currency.getDisplaySymbol(), currency.getNameCode());
|
||||
|
||||
Money totalOutstanding = loan.getTotalOutstanding();
|
||||
MoneyData totalOutstandingData = MoneyData.of(currencyData,
|
||||
totalOutstanding.getAmount());
|
||||
LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction();
|
||||
|
||||
LoanTransactionData newWaiverDetails = new LoanTransactionData();
|
||||
newWaiverDetails.setTransactionType(LoanEnumerations
|
||||
.transactionType(LoanTransactionType.WAIVED));
|
||||
newWaiverDetails.setDate(new LocalDate());
|
||||
newWaiverDetails.setTotal(totalOutstandingData);
|
||||
|
||||
return newWaiverDetails;
|
||||
EnumOptionData waiverEnum = LoanEnumerations.transactionType(LoanTransactionType.WAIVED);
|
||||
|
||||
return new LoanTransactionNewData(waiverEnum, currencyData, waiveOfInterest.getTransactionDate(), waiveOfInterest.getAmount());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public interface LoanWritePlatformService {
|
||||
EntityIdentifier adjustLoanTransaction(AdjustLoanTransactionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
|
||||
EntityIdentifier waiveLoanAmount(LoanTransactionCommand command);
|
||||
EntityIdentifier waiveInterestOnLoan(LoanTransactionCommand command);
|
||||
|
||||
@PreAuthorize(value = "hasRole('ORGANISATION_ADMINISTRATION_SUPER_USER_ROLE')")
|
||||
EntityIdentifier addLoanCharge(LoanChargeCommand command);
|
||||
|
||||
+8
-15
@@ -495,7 +495,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
LocalDate transactionDate = command.getTransactionDate();
|
||||
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmount, transactionDate);
|
||||
if (transactionToAdjust.isWaiver()) {
|
||||
newTransactionDetail = LoanTransaction.waiver(transactionAmount, transactionDate);
|
||||
newTransactionDetail = LoanTransaction.waiver(loan, transactionAmount, transactionDate);
|
||||
}
|
||||
|
||||
loan.adjustExistingTransaction(transactionToAdjust, newTransactionDetail, defaultLoanLifecycleStateMachine());
|
||||
@@ -515,33 +515,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public EntityIdentifier waiveLoanAmount(LoanTransactionCommand command) {
|
||||
public EntityIdentifier waiveInterestOnLoan(final LoanTransactionCommand command) {
|
||||
|
||||
context.authenticatedUser();
|
||||
|
||||
LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
|
||||
final LoanTransactionCommandValidator validator = new LoanTransactionCommandValidator(command);
|
||||
validator.validate();
|
||||
|
||||
Loan loan = this.loanRepository.findOne(command.getLoanId());
|
||||
final Loan loan = this.loanRepository.findOne(command.getLoanId());
|
||||
if (loan == null) {
|
||||
throw new LoanNotFoundException(command.getLoanId());
|
||||
}
|
||||
|
||||
Money waived = Money.of(loan.repaymentScheduleDetail()
|
||||
.getPrincipal().getCurrency(),
|
||||
command.getTransactionAmount());
|
||||
|
||||
LoanTransaction waiver = LoanTransaction.waiver(waived, command.getTransactionDate());
|
||||
|
||||
loan.waive(waiver, defaultLoanLifecycleStateMachine());
|
||||
|
||||
this.loanTransactionRepository.save(waiver);
|
||||
final LoanTransaction waiveTransaction = loan.waiveInterest(command.getTransactionAmount(), command.getTransactionDate(), defaultLoanLifecycleStateMachine());
|
||||
|
||||
this.loanTransactionRepository.save(waiveTransaction);
|
||||
this.loanRepository.save(loan);
|
||||
|
||||
String noteText = command.getNote();
|
||||
final String noteText = command.getNote();
|
||||
if (StringUtils.isNotBlank(noteText)) {
|
||||
Note note = Note.loanTransactionNote(loan, waiver, noteText);
|
||||
final Note note = Note.loanTransactionNote(loan, waiveTransaction, noteText);
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user