Merge pull request #1772 from BLasan/undoWaiveCharge
Feat: Undo waive charge
This commit is contained in:
+9
@@ -870,6 +870,15 @@ public class CommandWrapperBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommandWrapperBuilder undoWaiveChargeTransaction(final Long loanId, final Long transactionId) {
|
||||||
|
this.actionName = "UNDO";
|
||||||
|
this.entityName = "WAIVECHARGE";
|
||||||
|
this.entityId = transactionId;
|
||||||
|
this.loanId = loanId;
|
||||||
|
this.href = "/loans/" + loanId + "/transactions?command=undo";
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandWrapperBuilder createLoanApplication() {
|
public CommandWrapperBuilder createLoanApplication() {
|
||||||
this.actionName = "CREATE";
|
this.actionName = "CREATE";
|
||||||
this.entityName = "LOAN";
|
this.entityName = "LOAN";
|
||||||
|
|||||||
+13
@@ -182,6 +182,19 @@ public class Money implements Comparable<Money> {
|
|||||||
return this.minus(toSubtract.getAmount());
|
return this.minus(toSubtract.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money add(final Money moneyToAdd) {
|
||||||
|
final Money toAdd = checkCurrencyEqual(moneyToAdd);
|
||||||
|
return this.add(toAdd.getAmount());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Money add(final BigDecimal amountToAdd) {
|
||||||
|
if (amountToAdd == null || amountToAdd.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
final BigDecimal newAmount = this.amount.add(amountToAdd);
|
||||||
|
return Money.of(monetaryCurrency(), newAmount);
|
||||||
|
}
|
||||||
|
|
||||||
public Money minus(final BigDecimal amountToSubtract) {
|
public Money minus(final BigDecimal amountToSubtract) {
|
||||||
if (amountToSubtract == null || amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
|
if (amountToSubtract == null || amountToSubtract.compareTo(BigDecimal.ZERO) == 0) {
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.charge.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||||
|
|
||||||
|
public class LoanChargeWaiveCannotBeReversedException extends AbstractPlatformDomainRuleException {
|
||||||
|
|
||||||
|
/*** enum of reasons of why Loan Charge waive cannot undo **/
|
||||||
|
public enum LoanChargeWaiveCannotUndoReason {
|
||||||
|
|
||||||
|
ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, WAIVE_NOT_ALLOWED_FOR_CHARGE, NOT_WAIVED, ALREADY_REVERSED;
|
||||||
|
|
||||||
|
public String errorMessage() {
|
||||||
|
|
||||||
|
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||||
|
return "This loan charge has been completely paid";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||||
|
return "This loan charge has already been waived";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||||
|
return "This loan charge can be waived as the loan associated with it is currently inactive";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("WAIVE_NOT_ALLOWED_FOR_CHARGE")) {
|
||||||
|
return "This loan charge can be waived";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("NOT_WAIVED")) {
|
||||||
|
return "This loan charge waive cannot be reversed as this charge is not waived";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("ALREADY_REVERSED")) {
|
||||||
|
return "This loan charge waive cannot be reversed as this transaction is not reversed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return name().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String errorCode() {
|
||||||
|
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||||
|
return "error.msg.loan.charge.already.paid";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||||
|
return "error.msg.loan.charge.already.waived";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||||
|
return "error.msg.loan.charge.associated.loan.inactive";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("WAIVE_NOT_ALLOWED_FOR_CHARGE")) {
|
||||||
|
return "error.msg.loan.charge.waive.not.allowed";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("NOT_WAIVED")) {
|
||||||
|
return "error.msg.loan.charge.waive.cannot.undo";
|
||||||
|
} else if (name().toString().equalsIgnoreCase("ALREADY_REVERSED")) {
|
||||||
|
return "error.msg.transaction.cannot.reverse";
|
||||||
|
}
|
||||||
|
return name().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanChargeWaiveCannotBeReversedException(final LoanChargeWaiveCannotUndoReason reason, final Long id) {
|
||||||
|
super(reason.errorCode(), reason.errorMessage(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
@@ -41,6 +41,7 @@ public class BusinessEventNotificationConstants {
|
|||||||
LOAN_CLOSE_AS_RESCHEDULE("loan_close_as_reschedule"), //
|
LOAN_CLOSE_AS_RESCHEDULE("loan_close_as_reschedule"), //
|
||||||
LOAN_ADD_CHARGE("loan_add_charge"), //
|
LOAN_ADD_CHARGE("loan_add_charge"), //
|
||||||
LOAN_UPDATE_CHARGE("loan_update_charge"), //
|
LOAN_UPDATE_CHARGE("loan_update_charge"), //
|
||||||
|
LOAN_WAIVE_CHARGE_UNDO("loan_waive_charge_undo"), //
|
||||||
LOAN_WAIVE_CHARGE("loan_waive_charge"), //
|
LOAN_WAIVE_CHARGE("loan_waive_charge"), //
|
||||||
LOAN_DELETE_CHARGE("loan_delete_charge"), //
|
LOAN_DELETE_CHARGE("loan_delete_charge"), //
|
||||||
LOAN_CHARGE_PAYMENT("loan_charge_payment"), //
|
LOAN_CHARGE_PAYMENT("loan_charge_payment"), //
|
||||||
|
|||||||
+1
@@ -234,6 +234,7 @@ public class LoanChargesApiResource {
|
|||||||
} else {
|
} else {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
throw new UnrecognizedQueryParamException("command", commandParam);
|
throw new UnrecognizedQueryParamException("command", commandParam);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
@@ -34,6 +34,7 @@ import java.util.Set;
|
|||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
@@ -262,4 +263,19 @@ public class LoanTransactionsApiResource {
|
|||||||
return this.toApiJsonSerializer.serialize(result);
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("{transactionId}")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Undo a Waive Charge Transaction", description = "Undo a Waive Charge Transaction")
|
||||||
|
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PutChargeTransactionChangesRequest.class)))
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PutChargeTransactionChangesResponse.class))) })
|
||||||
|
public String undoWaiveCharge(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
|
||||||
|
@PathParam("transactionId") @Parameter(description = "transactionId") final Long transactionId) {
|
||||||
|
|
||||||
|
final CommandWrapper commandRequest = new CommandWrapperBuilder().undoWaiveChargeTransaction(loanId, transactionId).build();
|
||||||
|
CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||||
|
return this.toApiJsonSerializer.serialize(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
@@ -186,4 +186,35 @@ final class LoanTransactionsApiResourceSwagger {
|
|||||||
@Schema(example = "16")
|
@Schema(example = "16")
|
||||||
public Integer resourceId;
|
public Integer resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Schema(description = "PutChargeTransactionChangesResponse")
|
||||||
|
public static final class PutChargeTransactionChangesResponse {
|
||||||
|
|
||||||
|
private PutChargeTransactionChangesResponse() {}
|
||||||
|
|
||||||
|
static final class Changes {
|
||||||
|
|
||||||
|
private Changes() {}
|
||||||
|
|
||||||
|
@Schema(example = "amount")
|
||||||
|
public String amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Integer resourceId;
|
||||||
|
@Schema(example = "48")
|
||||||
|
public Integer loanId;
|
||||||
|
public PutChargeTransactionChangesResponse.Changes changes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(description = "PutChargeTransactionChangesRequest")
|
||||||
|
public static final class PutChargeTransactionChangesRequest {
|
||||||
|
|
||||||
|
private PutChargeTransactionChangesRequest() {}
|
||||||
|
|
||||||
|
@Schema(example = "1")
|
||||||
|
public Integer transactionId;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -529,6 +529,11 @@ public class Loan extends AbstractPersistableCustom {
|
|||||||
return this.summary;
|
return this.summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateLoanSummaryForUndoWaiveCharge(final BigDecimal amountWaived) {
|
||||||
|
this.summary.updateFeeChargesWaived(this.summary.getTotalFeeChargesWaived().subtract(amountWaived));
|
||||||
|
this.summary.updateFeeChargeOutstanding(this.summary.getTotalFeeChargesOutstanding().add(amountWaived));
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
|
private BigDecimal deriveSumTotalOfChargesDueAtDisbursement() {
|
||||||
|
|
||||||
Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO);
|
Money chargesDue = Money.of(getCurrency(), BigDecimal.ZERO);
|
||||||
|
|||||||
+31
@@ -131,6 +131,9 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
private LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
|
private LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
||||||
|
private Set<LoanChargePaidBy> loanChargePaidBySet;
|
||||||
|
|
||||||
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) {
|
public static LoanCharge createNewFromJson(final Loan loan, final Charge chargeDefinition, final JsonCommand command) {
|
||||||
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
final LocalDate dueDate = command.localDateValueOfParameterNamed("dueDate");
|
||||||
if (chargeDefinition.getChargeTimeType().equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue()) && dueDate == null) {
|
if (chargeDefinition.getChargeTimeType().equals(ChargeTimeType.SPECIFIED_DUE_DATE.getValue()) && dueDate == null) {
|
||||||
@@ -346,6 +349,10 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetOutstandingAmount(final BigDecimal amountOutstanding) {
|
||||||
|
this.amountOutstanding = amountOutstanding;
|
||||||
|
}
|
||||||
|
|
||||||
public Money waive(final MonetaryCurrency currency, final Integer loanInstallmentNumber) {
|
public Money waive(final MonetaryCurrency currency, final Integer loanInstallmentNumber) {
|
||||||
if (isInstalmentFee()) {
|
if (isInstalmentFee()) {
|
||||||
final LoanInstallmentCharge chargePerInstallment = getInstallmentLoanCharge(loanInstallmentNumber);
|
final LoanInstallmentCharge chargePerInstallment = getInstallmentLoanCharge(loanInstallmentNumber);
|
||||||
@@ -869,6 +876,18 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setInstallmentLoanCharge(final LoanInstallmentCharge loanInstallmentCharge, final Integer installmentNumber) {
|
||||||
|
LoanInstallmentCharge loanInstallmentChargeToBeRemoved = null;
|
||||||
|
for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
|
||||||
|
if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber().intValue())) {
|
||||||
|
loanInstallmentChargeToBeRemoved = loanChargePerInstallment;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.loanInstallmentCharge.remove(loanInstallmentChargeToBeRemoved);
|
||||||
|
this.loanInstallmentCharge.add(loanInstallmentCharge);
|
||||||
|
}
|
||||||
|
|
||||||
public void clearLoanInstallmentCharges() {
|
public void clearLoanInstallmentCharges() {
|
||||||
this.loanInstallmentCharge.clear();
|
this.loanInstallmentCharge.clear();
|
||||||
}
|
}
|
||||||
@@ -1021,6 +1040,10 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
return paidChargePerInstallment;
|
return paidChargePerInstallment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<LoanChargePaidBy> getLoanChargePaidBySet() {
|
||||||
|
return this.loanChargePaidBySet;
|
||||||
|
}
|
||||||
|
|
||||||
public Loan getLoan() {
|
public Loan getLoan() {
|
||||||
return this.loan;
|
return this.loan;
|
||||||
}
|
}
|
||||||
@@ -1036,4 +1059,12 @@ public class LoanCharge extends AbstractPersistableCustom {
|
|||||||
public boolean isDueDateCharge() {
|
public boolean isDueDateCharge() {
|
||||||
return this.dueDate != null;
|
return this.dueDate != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAmountWaived(final BigDecimal amountWaived) {
|
||||||
|
this.amountWaived = amountWaived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void undoWaived() {
|
||||||
|
this.waived = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -19,7 +19,6 @@
|
|||||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import javax.persistence.CascadeType;
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.JoinColumn;
|
import javax.persistence.JoinColumn;
|
||||||
@@ -35,7 +34,7 @@ public class LoanChargePaidBy extends AbstractPersistableCustom {
|
|||||||
@JoinColumn(name = "loan_transaction_id", nullable = false)
|
@JoinColumn(name = "loan_transaction_id", nullable = false)
|
||||||
private LoanTransaction loanTransaction;
|
private LoanTransaction loanTransaction;
|
||||||
|
|
||||||
@ManyToOne(cascade = CascadeType.ALL)
|
@ManyToOne(optional = false)
|
||||||
@JoinColumn(name = "loan_charge_id", nullable = false)
|
@JoinColumn(name = "loan_charge_id", nullable = false)
|
||||||
private LoanCharge loanCharge;
|
private LoanCharge loanCharge;
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -20,7 +20,12 @@ package org.apache.fineract.portfolio.loanaccount.domain;
|
|||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
public interface LoanChargePaidByRepository extends JpaRepository<LoanChargePaidBy, Long>, JpaSpecificationExecutor<LoanCharge> {
|
public interface LoanChargePaidByRepository extends JpaRepository<LoanChargePaidBy, Long>, JpaSpecificationExecutor<LoanCharge> {
|
||||||
// no added behaviour
|
|
||||||
|
@Query("select lp from LoanChargePaidBy lp where lp.loanCharge=:loanCharge and lp.installmentNumber=:installmentNumber")
|
||||||
|
LoanChargePaidBy getLoanChargePaidByLoanCharge(@Param("loanCharge") LoanCharge loanCharge,
|
||||||
|
@Param("installmentNumber") Integer installmentNo);
|
||||||
}
|
}
|
||||||
|
|||||||
+17
@@ -109,6 +109,11 @@ public class LoanInstallmentCharge extends AbstractPersistableCustom implements
|
|||||||
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
|
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void undoWaive(final BigDecimal amountOutstanding, final BigDecimal amountWaived) {
|
||||||
|
this.amountOutstanding = amountOutstanding;
|
||||||
|
this.amountWaived = amountWaived;
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal calculateOutstanding() {
|
private BigDecimal calculateOutstanding() {
|
||||||
if (this.amount == null) {
|
if (this.amount == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -221,6 +226,18 @@ public class LoanInstallmentCharge extends AbstractPersistableCustom implements
|
|||||||
this.paid = false;
|
this.paid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetAmountWaived(final BigDecimal amountWaived) {
|
||||||
|
this.amountWaived = amountWaived;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void undoWaiveFlag() {
|
||||||
|
this.waived = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetOutstandingAmount(final BigDecimal amountOutstanding) {
|
||||||
|
this.amountOutstanding = amountOutstanding;
|
||||||
|
}
|
||||||
|
|
||||||
public void resetToOriginal(final MonetaryCurrency currency) {
|
public void resetToOriginal(final MonetaryCurrency currency) {
|
||||||
this.amountPaid = BigDecimal.ZERO;
|
this.amountPaid = BigDecimal.ZERO;
|
||||||
this.amountWaived = BigDecimal.ZERO;
|
this.amountWaived = BigDecimal.ZERO;
|
||||||
|
|||||||
+8
@@ -223,6 +223,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
return Money.of(currency, this.principalCompleted);
|
return Money.of(currency, this.principalCompleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateLoanRepaymentSchedule(final BigDecimal amountWaived) {
|
||||||
|
this.feeChargesWaived = this.feeChargesWaived.subtract(amountWaived);
|
||||||
|
}
|
||||||
|
|
||||||
public Money getPrincipalWrittenOff(final MonetaryCurrency currency) {
|
public Money getPrincipalWrittenOff(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.principalWrittenOff);
|
return Money.of(currency, this.principalWrittenOff);
|
||||||
}
|
}
|
||||||
@@ -840,4 +844,8 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.rescheduleInterestPortion = rescheduleInterestPortion;
|
this.rescheduleInterestPortion = rescheduleInterestPortion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFeeChargesWaived(final BigDecimal newFeeChargesCharged) {
|
||||||
|
this.feeChargesWaived = newFeeChargesCharged;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -141,6 +141,14 @@ public final class LoanSummary {
|
|||||||
return Money.of(currency, this.totalOutstanding);
|
return Money.of(currency, this.totalOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateFeeChargeOutstanding(final BigDecimal totalFeeChargesOutstanding) {
|
||||||
|
this.totalFeeChargesOutstanding = totalFeeChargesOutstanding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateFeeChargesWaived(final BigDecimal totalFeeChargesWaived) {
|
||||||
|
this.totalFeeChargesWaived = totalFeeChargesWaived;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isRepaidInFull(final MonetaryCurrency currency) {
|
public boolean isRepaidInFull(final MonetaryCurrency currency) {
|
||||||
return getTotalOutstanding(currency).isZero();
|
return getTotalOutstanding(currency).isZero();
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -166,6 +166,18 @@ public class LoanTransaction extends AbstractPersistableCustom {
|
|||||||
createdDate, appUser);
|
createdDate, appUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLoanTransactionToRepaymentScheduleMappings(final Integer installmentId, final BigDecimal chargePerInstallment) {
|
||||||
|
for (LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping : this.loanTransactionToRepaymentScheduleMappings) {
|
||||||
|
final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loanTransactionToRepaymentScheduleMapping
|
||||||
|
.getLoanRepaymentScheduleInstallment();
|
||||||
|
if (loanRepaymentScheduleInstallment.getInstallmentNumber().equals(installmentId)) {
|
||||||
|
loanRepaymentScheduleInstallment.updateLoanRepaymentSchedule(chargePerInstallment);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static LoanTransaction recoveryRepayment(final Office office, final Money amount, final PaymentDetail paymentDetail,
|
public static LoanTransaction recoveryRepayment(final Office office, final Money amount, final PaymentDetail paymentDetail,
|
||||||
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
|
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
|
||||||
return new LoanTransaction(null, office, LoanTransactionType.RECOVERY_REPAYMENT, paymentDetail, amount.getAmount(), paymentDate,
|
return new LoanTransaction(null, office, LoanTransactionType.RECOVERY_REPAYMENT, paymentDetail, amount.getAmount(), paymentDate,
|
||||||
@@ -494,6 +506,14 @@ public class LoanTransaction extends AbstractPersistableCustom {
|
|||||||
return !isReversed();
|
return !isReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReversed() {
|
||||||
|
this.reversed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setManuallyAdjustedOrReversed() {
|
||||||
|
this.manuallyAdjustedOrReversed = true;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAnyTypeOfRepayment() {
|
public boolean isAnyTypeOfRepayment() {
|
||||||
return isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment();
|
return isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -34,6 +34,7 @@ public enum LoanTransactionType {
|
|||||||
*/
|
*/
|
||||||
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
|
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
|
||||||
WAIVE_CHARGES(9, "loanTransactionType.waiveCharges"), //
|
WAIVE_CHARGES(9, "loanTransactionType.waiveCharges"), //
|
||||||
|
// UNDO_WAIVE_CHARGE(20, "loanTransactionType.undoWaiveCharge"),
|
||||||
/**
|
/**
|
||||||
* Transaction represents an Accrual (For either interest, charge or a penalty
|
* Transaction represents an Accrual (For either interest, charge or a penalty
|
||||||
**/
|
**/
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.exception;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class InstallmentNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public InstallmentNotFoundException(final Long id) {
|
||||||
|
super("error.msg.transaction.id.invalid", "Transaction with identifier " + id + " does not contain any installment");
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.handler;
|
||||||
|
|
||||||
|
import org.apache.fineract.commands.annotation.CommandType;
|
||||||
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@CommandType(entity = "WAIVECHARGE", action = "UNDO")
|
||||||
|
public class UndoLoanChargeWaiveCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
|
final LoanWritePlatformService loanWritePlatformService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public UndoLoanChargeWaiveCommandHandler(final LoanWritePlatformService loanWritePlatformService) {
|
||||||
|
this.loanWritePlatformService = loanWritePlatformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
|
return this.loanWritePlatformService.undoWaiveLoanCharge(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -65,6 +65,8 @@ public interface LoanWritePlatformService {
|
|||||||
|
|
||||||
CommandProcessingResult waiveLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
CommandProcessingResult waiveLoanCharge(Long loanId, Long loanChargeId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult undoWaiveLoanCharge(JsonCommand command);
|
||||||
|
|
||||||
CommandProcessingResult loanReassignment(Long loanId, JsonCommand command);
|
CommandProcessingResult loanReassignment(Long loanId, JsonCommand command);
|
||||||
|
|
||||||
CommandProcessingResult bulkLoanReassignment(JsonCommand command);
|
CommandProcessingResult bulkLoanReassignment(JsonCommand command);
|
||||||
|
|||||||
+159
-1
@@ -22,6 +22,7 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -109,6 +110,8 @@ import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeUpdatedE
|
|||||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
|
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeWaivedException;
|
||||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LoanChargeCannotBeWaivedReason;
|
import org.apache.fineract.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LoanChargeCannotBeWaivedReason;
|
||||||
import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
|
import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.charge.exception.LoanChargeWaiveCannotBeReversedException;
|
||||||
|
import org.apache.fineract.portfolio.charge.exception.LoanChargeWaiveCannotBeReversedException.LoanChargeWaiveCannotUndoReason;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
import org.apache.fineract.portfolio.client.exception.ClientNotActiveException;
|
||||||
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
||||||
@@ -135,6 +138,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonit
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidByRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
|
||||||
@@ -156,6 +161,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepositor
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
|
import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
import org.apache.fineract.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.exception.InstallmentNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidLoanTransactionTypeException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
|
import org.apache.fineract.portfolio.loanaccount.exception.InvalidPaidInAdvanceAmountException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanForeclosureException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursementException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanMultiDisbursementException;
|
||||||
@@ -238,6 +245,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
private final CashierTransactionDataValidator cashierTransactionDataValidator;
|
private final CashierTransactionDataValidator cashierTransactionDataValidator;
|
||||||
private final GLIMAccountInfoRepository glimRepository;
|
private final GLIMAccountInfoRepository glimRepository;
|
||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
|
private final LoanChargePaidByRepository loanChargePaidByRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||||
@@ -267,7 +275,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy,
|
final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessingStrategy,
|
||||||
final CodeValueRepositoryWrapper codeValueRepository, final LoanRepositoryWrapper loanRepositoryWrapper,
|
final CodeValueRepositoryWrapper codeValueRepository, final LoanRepositoryWrapper loanRepositoryWrapper,
|
||||||
final CashierTransactionDataValidator cashierTransactionDataValidator, final GLIMAccountInfoRepository glimRepository,
|
final CashierTransactionDataValidator cashierTransactionDataValidator, final GLIMAccountInfoRepository glimRepository,
|
||||||
final LoanRepository loanRepository) {
|
final LoanRepository loanRepository, final LoanChargePaidByRepository loanChargePaidByRepository) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
||||||
this.loanAssembler = loanAssembler;
|
this.loanAssembler = loanAssembler;
|
||||||
@@ -308,6 +316,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
this.cashierTransactionDataValidator = cashierTransactionDataValidator;
|
this.cashierTransactionDataValidator = cashierTransactionDataValidator;
|
||||||
this.loanRepository = loanRepository;
|
this.loanRepository = loanRepository;
|
||||||
this.glimRepository = glimRepository;
|
this.glimRepository = glimRepository;
|
||||||
|
this.loanChargePaidByRepository = loanChargePaidByRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||||
@@ -1590,6 +1599,155 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult undoWaiveLoanCharge(final JsonCommand command) {
|
||||||
|
|
||||||
|
LoanTransaction loanTransaction = this.loanTransactionRepository.findById(command.entityId())
|
||||||
|
.orElseThrow(() -> new LoanTransactionNotFoundException(command.entityId()));
|
||||||
|
|
||||||
|
if (!loanTransaction.getTypeOf().getCode().equals(LoanTransactionType.WAIVE_CHARGES.getCode())) {
|
||||||
|
throw new InvalidLoanTransactionTypeException("Undo Waive Charge", "Waive an Installment Charge First",
|
||||||
|
"Transaction is not a waive charge type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<LoanChargePaidBy> loanChargePaidBySet = loanTransaction.getLoanChargesPaid();
|
||||||
|
Integer installmentNumber = null;
|
||||||
|
Long loanChargeId = null;
|
||||||
|
final Long loanId = loanTransaction.getLoan().getId();
|
||||||
|
|
||||||
|
for (LoanChargePaidBy loanChargePaidBy : loanChargePaidBySet) {
|
||||||
|
installmentNumber = loanChargePaidBy.getInstallmentNumber();
|
||||||
|
loanChargeId = loanChargePaidBy.getLoanCharge().getId();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppUser currentUser = getAppUserIfPresent();
|
||||||
|
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
|
checkClientOrGroupActive(loan);
|
||||||
|
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
|
||||||
|
|
||||||
|
// Charges may be waived only when the loan associated with them are
|
||||||
|
// active
|
||||||
|
if (!loan.status().isActive()) {
|
||||||
|
throw new LoanChargeWaiveCannotBeReversedException(LoanChargeWaiveCannotUndoReason.LOAN_INACTIVE, loanCharge.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate loan charge is not already paid
|
||||||
|
if (loanCharge.isPaid()) {
|
||||||
|
throw new LoanChargeWaiveCannotBeReversedException(LoanChargeWaiveCannotUndoReason.ALREADY_PAID, loanCharge.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, Object> changes = new LinkedHashMap<>(3);
|
||||||
|
|
||||||
|
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BusinessEvents.LOAN_WAIVE_CHARGE_UNDO,
|
||||||
|
constructEntityMap(BusinessEntity.LOAN_CHARGE, loanCharge));
|
||||||
|
|
||||||
|
if (loanCharge.isInstalmentFee()) {
|
||||||
|
LoanInstallmentCharge chargePerInstallment = null;
|
||||||
|
|
||||||
|
// final Integer installmentNumber = command.integerValueOfParameterNamed("installmentNumber");
|
||||||
|
if (installmentNumber != null) {
|
||||||
|
|
||||||
|
// Get installment charge.
|
||||||
|
chargePerInstallment = loanCharge.getInstallmentLoanCharge(installmentNumber);
|
||||||
|
|
||||||
|
if (!loanTransaction.isNotReversed()) {
|
||||||
|
throw new LoanChargeWaiveCannotBeReversedException(LoanChargeWaiveCannotUndoReason.ALREADY_REVERSED,
|
||||||
|
loanTransaction.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse waived transaction
|
||||||
|
loanTransaction.setReversed();
|
||||||
|
|
||||||
|
// Get installment amount waived.
|
||||||
|
BigDecimal amountWaived = chargePerInstallment.getAmountWaived(loan.getCurrency()).getAmount();
|
||||||
|
|
||||||
|
// Set manually adjusted value to `1`
|
||||||
|
loanTransaction.setManuallyAdjustedOrReversed();
|
||||||
|
|
||||||
|
// Save updated data
|
||||||
|
this.loanTransactionRepository.saveAndFlush(loanTransaction);
|
||||||
|
|
||||||
|
// Get installment outstanding amount
|
||||||
|
BigDecimal amountOutstandingPerInstallment = chargePerInstallment.getAmountOutstanding();
|
||||||
|
|
||||||
|
// Check whether the installment charge is not waived. If so throw new error
|
||||||
|
if (!chargePerInstallment.isWaived() || amountWaived == null) {
|
||||||
|
throw new LoanChargeWaiveCannotBeReversedException(LoanChargeWaiveCannotUndoReason.NOT_WAIVED, loanChargeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get loan charge total amount waived
|
||||||
|
BigDecimal totalAmountWaved = loanCharge.getAmountWaived(loan.getCurrency()).getAmount();
|
||||||
|
|
||||||
|
// Get loan charge outstanding amount
|
||||||
|
BigDecimal amountOutstanding = loanCharge.getAmountOutstanding(loan.getCurrency()).getAmount();
|
||||||
|
|
||||||
|
// Add the amount waived to outstanding amount
|
||||||
|
loanCharge.resetOutstandingAmount(amountOutstanding.add(amountWaived));
|
||||||
|
|
||||||
|
// Subtract the amount waived from the existing amount waived.
|
||||||
|
loanCharge.setAmountWaived(totalAmountWaved.subtract(amountWaived));
|
||||||
|
|
||||||
|
// Add the amount waived to the outstanding amount of the installment
|
||||||
|
chargePerInstallment.resetOutstandingAmount(amountOutstandingPerInstallment.add(amountWaived));
|
||||||
|
|
||||||
|
// Set the amount waived value to ZERO
|
||||||
|
chargePerInstallment.resetAmountWaived(BigDecimal.ZERO);
|
||||||
|
|
||||||
|
// Reset waived flag
|
||||||
|
chargePerInstallment.undoWaiveFlag();
|
||||||
|
|
||||||
|
// Get the fee charges waived amount per installment
|
||||||
|
BigDecimal feeChargesWaivedAmount = chargePerInstallment.getInstallment().getFeeChargesWaived(loan.getCurrency())
|
||||||
|
.getAmount();
|
||||||
|
|
||||||
|
// Subtract the amount waived from the existing fee charges waived amount.
|
||||||
|
chargePerInstallment.getInstallment().setFeeChargesWaived(feeChargesWaivedAmount.subtract(amountWaived));
|
||||||
|
|
||||||
|
// Set the last modification date.
|
||||||
|
chargePerInstallment.getInstallment().setLastModifiedDate(Instant.now());
|
||||||
|
|
||||||
|
// Update loan charge.
|
||||||
|
loanCharge.setInstallmentLoanCharge(chargePerInstallment, chargePerInstallment.getInstallment().getInstallmentNumber());
|
||||||
|
|
||||||
|
if (loanCharge.getAmount(loan.getCurrency()).compareTo(loanCharge.getAmountOutstanding(loan.getCurrency())) == 0
|
||||||
|
&& loanCharge.isWaived()) {
|
||||||
|
loanCharge.undoWaived();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loanChargeRepository.saveAndFlush(loanCharge);
|
||||||
|
|
||||||
|
loan.updateLoanSummaryForUndoWaiveCharge(amountWaived);
|
||||||
|
|
||||||
|
changes.put("amount", amountWaived);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new InstallmentNotFoundException(command.entityId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
|
this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEvents.LOAN_WAIVE_CHARGE_UNDO,
|
||||||
|
constructEntityMap(BusinessEntity.LOAN_CHARGE, loanCharge));
|
||||||
|
|
||||||
|
LoanTransaction loanTransactionData = this.loanTransactionRepository.getOne(command.entityId());
|
||||||
|
changes.put("principalPortion", loanTransactionData.getPrincipalPortion());
|
||||||
|
changes.put("interestPortion", loanTransactionData.getInterestPortion(loan.getCurrency()));
|
||||||
|
changes.put("feeChargesPortion", loanTransactionData.getFeeChargesPortion(loan.getCurrency()));
|
||||||
|
changes.put("penaltyChargesPortion", loanTransactionData.getPenaltyChargesPortion(loan.getCurrency()));
|
||||||
|
changes.put("outstandingLoanBalance", loanTransactionData.getOutstandingLoanBalance());
|
||||||
|
changes.put("id", loanTransactionData.getId());
|
||||||
|
changes.put("date", loanTransactionData.getTransactionDate());
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder() //
|
||||||
|
.withCommandId(command.commandId()) //
|
||||||
|
.withEntityId(command.entityId()) //
|
||||||
|
.withLoanId(loanId) //
|
||||||
|
.with(changes).build();
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult waiveLoanCharge(final Long loanId, final Long loanChargeId, final JsonCommand command) {
|
public CommandProcessingResult waiveLoanCharge(final Long loanId, final Long loanChargeId, final JsonCommand command) {
|
||||||
|
|||||||
+1
@@ -286,6 +286,7 @@ INSERT INTO `m_permission`
|
|||||||
('transaction_loan', 'WRITEOFF_LOAN_CHECKER', 'LOAN', 'WRITEOFF', '0'),
|
('transaction_loan', 'WRITEOFF_LOAN_CHECKER', 'LOAN', 'WRITEOFF', '0'),
|
||||||
('transaction_loan', 'CLOSE_LOAN_CHECKER', 'LOAN', 'CLOSE', '0'),
|
('transaction_loan', 'CLOSE_LOAN_CHECKER', 'LOAN', 'CLOSE', '0'),
|
||||||
('transaction_loan', 'CLOSEASRESCHEDULED_LOAN_CHECKER', 'LOAN', 'CLOSEASRESCHEDULED', '0'),
|
('transaction_loan', 'CLOSEASRESCHEDULED_LOAN_CHECKER', 'LOAN', 'CLOSEASRESCHEDULED', '0'),
|
||||||
|
('transaction_loan', 'UNDO_WAIVECHARGE', 'WAIVECHARGE', 'UNDO', '0'),
|
||||||
('transaction_savings', 'DEPOSIT_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'DEPOSIT', '1'),
|
('transaction_savings', 'DEPOSIT_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'DEPOSIT', '1'),
|
||||||
('transaction_savings', 'DEPOSIT_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'DEPOSIT', '0'),
|
('transaction_savings', 'DEPOSIT_SAVINGSACCOUNT_CHECKER', 'SAVINGSACCOUNT', 'DEPOSIT', '0'),
|
||||||
('transaction_savings', 'WITHDRAWAL_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'WITHDRAWAL', '1'),
|
('transaction_savings', 'WITHDRAWAL_SAVINGSACCOUNT', 'SAVINGSACCOUNT', 'WITHDRAWAL', '1'),
|
||||||
|
|||||||
Reference in New Issue
Block a user