diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java index 688733289..c6478ffc3 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/LoansApiResource.java @@ -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 typicalResponseParameters = new HashSet( - Arrays.asList("id", "transactionType", "date", "principal", "interest", "total", "totalWaived", "overpaid") - ); - - Set 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 typicalResponseParameters = new HashSet( + Arrays.asList("id", "transactionType", "date", "principal", "interest", "total", "totalWaived", "overpaid") + ); + + Set 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 typicalResponseParameters = new HashSet(Arrays.asList("id", "type", "date", "currency", "amount")); + + Set 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 diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java new file mode 100644 index 000000000..91ca339ca --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/LoanTransactionNewData.java @@ -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; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java index a3d5a15e3..53fbb84fe 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJsonSerializerService.java @@ -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 responseParameters, LoanAccountData loanAccount); String serializeLoanTransactionDataToJson(boolean prettyPrint, Set responseParameters, LoanTransactionData transaction); + + String serializeLoanTransactionDataToJson(boolean prettyPrint, Set responseParameters, LoanTransactionNewData newTransactionData); String serializeChargeDataToJson(boolean prettyPrint, Set responseParameters, Collection charges); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java index 11a3c143c..e92b1d4b6 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonApiJsonSerializerService.java @@ -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 LOAN_TRANSACTION_DATA_PARAMETERS = new HashSet( Arrays.asList("id", "transactionType", "date", "principal", "interest", "total", "totalWaived", "overpaid")); + + private static final Set LOAN_TRANSACTION_NEW_DATA_PARAMETERS = new HashSet( + Arrays.asList("id", "type", "date", "currency", "amount")); private static final Set CHARGES_DATA_PARAMETERS = new HashSet( 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 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, diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java index 2c70ef6da..db265d84e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/AdhikarLoanRepaymentScheduleTransactionProcessor.java @@ -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; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java index 66d3d37bc..3900077c7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/CreocoreLoanRepaymentScheduleTransactionProcessor.java @@ -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; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java index f446f4201..d34c04fdc 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/DefaultLoanLifecycleStateMachine.java @@ -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 { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java index 7b8ca1c80..925f23dc5 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/HeavensFamilyLoanRepaymentScheduleTransactionProcessor.java @@ -65,31 +65,38 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst final List 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; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java index e9eb1ebac..fb282980e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/Loan.java @@ -582,65 +582,42 @@ public class Loan extends AbstractAuditableCustom { } } } - - 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 { 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 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 repaymentsOrWaivers = retreiveListOfRepaymentOrWaiverTransactionsPostDisbursement(); loanRepaymentScheduleTransactionProcessor.handleTransaction(repaymentsOrWaivers, getCurrency(), this.repaymentScheduleInstallments); } @@ -777,8 +765,7 @@ public class Loan extends AbstractAuditableCustom { } 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 { 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 { } 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 { 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 diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java index 2c257140b..54a6c5ba2 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanEvent.java @@ -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; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java index 0b5fae54f..60ca9d134 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanRepaymentScheduleInstallment.java @@ -93,6 +93,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom { } 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 { 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(); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java index 57bf43267..c90e157d2 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/LoanTransactionComparator.java @@ -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 { @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; } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java index a9581b285..b02ae62fa 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/domain/MifosStyleLoanRepaymentScheduleTransactionProcessor.java @@ -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; } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java index 696cd7496..42a48c5fa 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformService.java @@ -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); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java index 6b0bb433a..c3381891a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanReadPlatformServiceImpl.java @@ -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 diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java index 5f9980947..dac17f5c5 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformService.java @@ -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); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java index a8c19cb8c..67fd1e55a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/loan/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -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); }