FINERACT-2234: Improve "Add periodic accrual entries" business step performance
This commit is contained in:
committed by
Adam Saghy
parent
4ebf768286
commit
11912881af
+3
-6
@@ -31,6 +31,7 @@ import java.util.HashSet;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import lombok.Getter;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
import org.apache.fineract.portfolio.client.domain.Client;
|
import org.apache.fineract.portfolio.client.domain.Client;
|
||||||
@@ -52,7 +53,8 @@ public class ClientCollateralManagement extends AbstractPersistableCustom<Long>
|
|||||||
@JoinColumn(name = "collateral_id", nullable = false)
|
@JoinColumn(name = "collateral_id", nullable = false)
|
||||||
private CollateralManagementDomain collateral;
|
private CollateralManagementDomain collateral;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "clientCollateralManagement", fetch = FetchType.EAGER)
|
@Getter
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "clientCollateralManagement", fetch = FetchType.LAZY)
|
||||||
private Set<LoanCollateralManagement> loanCollateralManagementSet = new HashSet<>();
|
private Set<LoanCollateralManagement> loanCollateralManagementSet = new HashSet<>();
|
||||||
|
|
||||||
public ClientCollateralManagement() {
|
public ClientCollateralManagement() {
|
||||||
@@ -129,9 +131,4 @@ public class ClientCollateralManagement extends AbstractPersistableCustom<Long>
|
|||||||
public CollateralManagementDomain getCollaterals() {
|
public CollateralManagementDomain getCollaterals() {
|
||||||
return this.collateral;
|
return this.collateral;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<LoanCollateralManagement> getLoanCollateralManagementSet() {
|
|
||||||
return this.loanCollateralManagementSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-23
@@ -43,7 +43,7 @@ public class AccrualPeriodData {
|
|||||||
private Money transactionAccrued;
|
private Money transactionAccrued;
|
||||||
private final List<AccrualChargeData> charges = new ArrayList<>();
|
private final List<AccrualChargeData> charges = new ArrayList<>();
|
||||||
|
|
||||||
public AccrualPeriodData addCharge(AccrualChargeData charge) {
|
public AccrualPeriodData addCharge(final AccrualChargeData charge) {
|
||||||
charges.add(charge);
|
charges.add(charge);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -56,36 +56,14 @@ public class AccrualPeriodData {
|
|||||||
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAmount).reduce(null, MathUtil::plus);
|
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAmount).reduce(null, MathUtil::plus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money getPenaltyAmount() {
|
|
||||||
return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getChargeAmount).reduce(null, MathUtil::plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Money getChargeAccrued() {
|
|
||||||
return charges.stream().map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Money getFeeAccrued() {
|
public Money getFeeAccrued() {
|
||||||
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
|
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money getFeeTransactionAccrued() {
|
|
||||||
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getTransactionAccrued).reduce(null,
|
|
||||||
MathUtil::plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Money getPenaltyAccrued() {
|
public Money getPenaltyAccrued() {
|
||||||
return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
|
return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money getPenaltyTransactionAccrued() {
|
|
||||||
return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getTransactionAccrued).reduce(null,
|
|
||||||
MathUtil::plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Money getChargeAccruable() {
|
|
||||||
return charges.stream().map(AccrualChargeData::getChargeAccruable).reduce(null, MathUtil::plus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Money getFeeAccruable() {
|
public Money getFeeAccruable() {
|
||||||
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccruable).reduce(null,
|
return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccruable).reduce(null,
|
||||||
MathUtil::plus);
|
MathUtil::plus);
|
||||||
|
|||||||
+14
-24
@@ -43,6 +43,7 @@ import java.util.Objects;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
@@ -76,7 +77,7 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@Column(name = "charge_time_enum", nullable = false)
|
@Column(name = "charge_time_enum", nullable = false)
|
||||||
private Integer chargeTime;
|
private Integer chargeTime;
|
||||||
|
|
||||||
@Column(name = "submitted_on_date", nullable = true)
|
@Column(name = "submitted_on_date")
|
||||||
private LocalDate submittedOnDate;
|
private LocalDate submittedOnDate;
|
||||||
|
|
||||||
@Column(name = "due_for_collection_as_of_date")
|
@Column(name = "due_for_collection_as_of_date")
|
||||||
@@ -88,10 +89,10 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@Column(name = "charge_payment_mode_enum")
|
@Column(name = "charge_payment_mode_enum")
|
||||||
private Integer chargePaymentMode;
|
private Integer chargePaymentMode;
|
||||||
|
|
||||||
@Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true)
|
@Column(name = "calculation_percentage", scale = 6, precision = 19)
|
||||||
private BigDecimal percentage;
|
private BigDecimal percentage;
|
||||||
|
|
||||||
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "calculation_on_amount", scale = 6, precision = 19)
|
||||||
private BigDecimal amountPercentageAppliedTo;
|
private BigDecimal amountPercentageAppliedTo;
|
||||||
|
|
||||||
@Column(name = "charge_amount_or_percentage", scale = 6, precision = 19, nullable = false)
|
@Column(name = "charge_amount_or_percentage", scale = 6, precision = 19, nullable = false)
|
||||||
@@ -100,13 +101,14 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
|
||||||
@Column(name = "amount_paid_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "amount_paid_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal amountPaid;
|
private BigDecimal amountPaid;
|
||||||
|
|
||||||
@Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true)
|
@Setter
|
||||||
|
@Column(name = "amount_waived_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal amountWaived;
|
private BigDecimal amountWaived;
|
||||||
|
|
||||||
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "amount_writtenoff_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal amountWrittenOff;
|
private BigDecimal amountWrittenOff;
|
||||||
|
|
||||||
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
|
@Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false)
|
||||||
@@ -121,13 +123,13 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@Column(name = "waived", nullable = false)
|
@Column(name = "waived", nullable = false)
|
||||||
private boolean waived = false;
|
private boolean waived = false;
|
||||||
|
|
||||||
@Column(name = "min_cap", scale = 6, precision = 19, nullable = true)
|
@Column(name = "min_cap", scale = 6, precision = 19)
|
||||||
private BigDecimal minCap;
|
private BigDecimal minCap;
|
||||||
|
|
||||||
@Column(name = "max_cap", scale = 6, precision = 19, nullable = true)
|
@Column(name = "max_cap", scale = 6, precision = 19)
|
||||||
private BigDecimal maxCap;
|
private BigDecimal maxCap;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loancharge", orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loancharge", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private Set<LoanInstallmentCharge> loanInstallmentCharge = new HashSet<>();
|
private Set<LoanInstallmentCharge> loanInstallmentCharge = new HashSet<>();
|
||||||
|
|
||||||
@Column(name = "is_active", nullable = false)
|
@Column(name = "is_active", nullable = false)
|
||||||
@@ -136,13 +138,13 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
@Column(name = "external_id")
|
@Column(name = "external_id")
|
||||||
private ExternalId externalId;
|
private ExternalId externalId;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private LoanOverdueInstallmentCharge overdueInstallmentCharge;
|
private LoanOverdueInstallmentCharge overdueInstallmentCharge;
|
||||||
|
|
||||||
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, optional = true, orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToOne(mappedBy = "loancharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
|
private LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
|
@OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private Set<LoanChargePaidBy> loanChargePaidBySet = new HashSet<>();
|
private Set<LoanChargePaidBy> loanChargePaidBySet = new HashSet<>();
|
||||||
|
|
||||||
protected LoanCharge() {
|
protected LoanCharge() {
|
||||||
@@ -786,14 +788,6 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
this.loanInstallmentCharge.clear();
|
this.loanInstallmentCharge.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLoanInstallmentCharges(final Collection<LoanInstallmentCharge> installmentCharges) {
|
|
||||||
this.loanInstallmentCharge.addAll(installmentCharges);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNoLoanInstallmentCharges() {
|
|
||||||
return this.loanInstallmentCharge.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<LoanInstallmentCharge> installmentCharges() {
|
public Set<LoanInstallmentCharge> installmentCharges() {
|
||||||
return this.loanInstallmentCharge;
|
return this.loanInstallmentCharge;
|
||||||
}
|
}
|
||||||
@@ -943,10 +937,6 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
|||||||
return this.dueDate != null;
|
return this.dueDate != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAmountWaived(final BigDecimal amountWaived) {
|
|
||||||
this.amountWaived = amountWaived;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void undoWaived() {
|
public void undoWaived() {
|
||||||
this.waived = false;
|
this.waived = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-43
@@ -25,6 +25,7 @@ import jakarta.persistence.Enumerated;
|
|||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.OneToOne;
|
import jakarta.persistence.OneToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
import lombok.Getter;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||||
@@ -61,29 +62,37 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom<
|
|||||||
@Column(name = "rest_frequency_type_enum", nullable = false)
|
@Column(name = "rest_frequency_type_enum", nullable = false)
|
||||||
private Integer restFrequencyType;
|
private Integer restFrequencyType;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@Column(name = "rest_frequency_interval", nullable = false)
|
@Column(name = "rest_frequency_interval", nullable = false)
|
||||||
private Integer restInterval;
|
private Integer restInterval;
|
||||||
|
|
||||||
@Column(name = "rest_frequency_nth_day_enum", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "rest_frequency_nth_day_enum")
|
||||||
private Integer restFrequencyNthDay;
|
private Integer restFrequencyNthDay;
|
||||||
|
|
||||||
@Column(name = "rest_frequency_weekday_enum", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "rest_frequency_weekday_enum")
|
||||||
private Integer restFrequencyWeekday;
|
private Integer restFrequencyWeekday;
|
||||||
|
|
||||||
@Column(name = "rest_frequency_on_day", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "rest_frequency_on_day")
|
||||||
private Integer restFrequencyOnDay;
|
private Integer restFrequencyOnDay;
|
||||||
|
|
||||||
@Column(name = "compounding_frequency_type_enum", nullable = true)
|
@Column(name = "compounding_frequency_type_enum")
|
||||||
private Integer compoundingFrequencyType;
|
private Integer compoundingFrequencyType;
|
||||||
|
|
||||||
@Column(name = "compounding_frequency_interval", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "compounding_frequency_interval")
|
||||||
private Integer compoundingInterval;
|
private Integer compoundingInterval;
|
||||||
|
|
||||||
@Column(name = "compounding_frequency_nth_day_enum", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "compounding_frequency_nth_day_enum")
|
||||||
private Integer compoundingFrequencyNthDay;
|
private Integer compoundingFrequencyNthDay;
|
||||||
@Column(name = "compounding_frequency_weekday_enum", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "compounding_frequency_weekday_enum")
|
||||||
private Integer compoundingFrequencyWeekday;
|
private Integer compoundingFrequencyWeekday;
|
||||||
@Column(name = "compounding_frequency_on_day", nullable = true)
|
@Getter
|
||||||
|
@Column(name = "compounding_frequency_on_day")
|
||||||
private Integer compoundingFrequencyOnDay;
|
private Integer compoundingFrequencyOnDay;
|
||||||
|
|
||||||
@Column(name = "is_compounding_to_be_posted_as_transaction")
|
@Column(name = "is_compounding_to_be_posted_as_transaction")
|
||||||
@@ -91,6 +100,7 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom<
|
|||||||
@Column(name = "allow_compounding_on_eod")
|
@Column(name = "allow_compounding_on_eod")
|
||||||
private Boolean allowCompoundingOnEod;
|
private Boolean allowCompoundingOnEod;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@Column(name = "pre_close_interest_calculation_strategy")
|
@Column(name = "pre_close_interest_calculation_strategy")
|
||||||
@Enumerated(EnumType.ORDINAL)
|
@Enumerated(EnumType.ORDINAL)
|
||||||
private LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy;
|
private LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy;
|
||||||
@@ -161,42 +171,10 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom<
|
|||||||
return RecalculationFrequencyType.fromInt(this.restFrequencyType);
|
return RecalculationFrequencyType.fromInt(this.restFrequencyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getRestInterval() {
|
|
||||||
return this.restInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecalculationFrequencyType getCompoundingFrequencyType() {
|
public RecalculationFrequencyType getCompoundingFrequencyType() {
|
||||||
return RecalculationFrequencyType.fromInt(this.compoundingFrequencyType);
|
return RecalculationFrequencyType.fromInt(this.compoundingFrequencyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getCompoundingInterval() {
|
|
||||||
return this.compoundingInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getRestFrequencyNthDay() {
|
|
||||||
return this.restFrequencyNthDay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getRestFrequencyWeekday() {
|
|
||||||
return this.restFrequencyWeekday;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getRestFrequencyOnDay() {
|
|
||||||
return this.restFrequencyOnDay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getCompoundingFrequencyNthDay() {
|
|
||||||
return this.compoundingFrequencyNthDay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getCompoundingFrequencyWeekday() {
|
|
||||||
return this.compoundingFrequencyWeekday;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getCompoundingFrequencyOnDay() {
|
|
||||||
return this.compoundingFrequencyOnDay;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCompoundingToBePostedAsTransaction() {
|
public boolean isCompoundingToBePostedAsTransaction() {
|
||||||
return this.isCompoundingToBePostedAsTransaction != null && this.isCompoundingToBePostedAsTransaction;
|
return this.isCompoundingToBePostedAsTransaction != null && this.isCompoundingToBePostedAsTransaction;
|
||||||
}
|
}
|
||||||
@@ -209,7 +187,4 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom<
|
|||||||
return disallowInterestCalculationOnPastDue;
|
return disallowInterestCalculationOnPastDue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy() {
|
|
||||||
return preCloseInterestCalculationStrategy;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-6
@@ -159,10 +159,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
|||||||
@Column(name = "is_re_aged", nullable = false)
|
@Column(name = "is_re_aged", nullable = false)
|
||||||
private boolean isReAged;
|
private boolean isReAged;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "loanRepaymentScheduleInstallment")
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanRepaymentScheduleInstallment")
|
||||||
private Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>();
|
private Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "loanRepaymentScheduleInstallment")
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanRepaymentScheduleInstallment")
|
||||||
private Set<PostDatedChecks> postDatedChecks = new HashSet<>();
|
private Set<PostDatedChecks> postDatedChecks = new HashSet<>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "installment")
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "installment")
|
||||||
@@ -296,10 +296,6 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-143
@@ -33,13 +33,11 @@ import java.time.LocalDate;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
@@ -47,10 +45,8 @@ import org.apache.fineract.infrastructure.core.service.MathUtil;
|
|||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.organisation.office.domain.Office;
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
|
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +67,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
private Office office;
|
private Office office;
|
||||||
|
|
||||||
@ManyToOne(optional = true)
|
@ManyToOne(optional = true)
|
||||||
@JoinColumn(name = "payment_detail_id", nullable = true)
|
@JoinColumn(name = "payment_detail_id")
|
||||||
private PaymentDetail paymentDetail;
|
private PaymentDetail paymentDetail;
|
||||||
|
|
||||||
@Column(name = "transaction_type_enum", nullable = false)
|
@Column(name = "transaction_type_enum", nullable = false)
|
||||||
@@ -80,63 +76,67 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
@Column(name = "transaction_date", nullable = false)
|
@Column(name = "transaction_date", nullable = false)
|
||||||
private LocalDate dateOf;
|
private LocalDate dateOf;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@Column(name = "submitted_on_date", nullable = false)
|
@Column(name = "submitted_on_date", nullable = false)
|
||||||
private LocalDate submittedOnDate;
|
private LocalDate submittedOnDate;
|
||||||
|
|
||||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||||
private BigDecimal amount;
|
private BigDecimal amount;
|
||||||
|
|
||||||
@Column(name = "principal_portion_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "principal_portion_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal principalPortion;
|
private BigDecimal principalPortion;
|
||||||
|
|
||||||
@Column(name = "interest_portion_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "interest_portion_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal interestPortion;
|
private BigDecimal interestPortion;
|
||||||
|
|
||||||
@Column(name = "fee_charges_portion_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_portion_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal feeChargesPortion;
|
private BigDecimal feeChargesPortion;
|
||||||
|
|
||||||
@Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal penaltyChargesPortion;
|
private BigDecimal penaltyChargesPortion;
|
||||||
|
|
||||||
@Column(name = "overpayment_portion_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "overpayment_portion_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal overPaymentPortion;
|
private BigDecimal overPaymentPortion;
|
||||||
|
|
||||||
@Column(name = "unrecognized_income_portion", scale = 6, precision = 19, nullable = true)
|
@Column(name = "unrecognized_income_portion", scale = 6, precision = 19)
|
||||||
private BigDecimal unrecognizedIncomePortion;
|
private BigDecimal unrecognizedIncomePortion;
|
||||||
|
|
||||||
@Column(name = "is_reversed", nullable = false)
|
@Column(name = "is_reversed", nullable = false)
|
||||||
private boolean reversed;
|
private boolean reversed;
|
||||||
|
|
||||||
@Column(name = "external_id", length = 100, nullable = true, unique = true)
|
@Column(name = "external_id", length = 100, unique = true)
|
||||||
private ExternalId externalId;
|
private ExternalId externalId;
|
||||||
|
|
||||||
@Column(name = "reversal_external_id", length = 100, nullable = true, unique = true)
|
@Column(name = "reversal_external_id", length = 100, unique = true)
|
||||||
private ExternalId reversalExternalId;
|
private ExternalId reversalExternalId;
|
||||||
|
|
||||||
@Column(name = "reversed_on_date", nullable = true)
|
@Column(name = "reversed_on_date")
|
||||||
private LocalDate reversedOnDate;
|
private LocalDate reversedOnDate;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true, fetch = FetchType.EAGER)
|
@Setter
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<>();
|
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<>();
|
||||||
|
|
||||||
@Column(name = "outstanding_loan_balance_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "outstanding_loan_balance_derived", scale = 6, precision = 19)
|
||||||
private BigDecimal outstandingLoanBalance;
|
private BigDecimal outstandingLoanBalance;
|
||||||
|
|
||||||
@Column(name = "manually_adjusted_or_reversed", nullable = false)
|
@Column(name = "manually_adjusted_or_reversed", nullable = false)
|
||||||
private boolean manuallyAdjustedOrReversed;
|
private boolean manuallyAdjustedOrReversed;
|
||||||
|
|
||||||
@Column(name = "charge_refund_charge_type", length = 1, nullable = true, unique = true)
|
@Column(name = "charge_refund_charge_type", length = 1, unique = true)
|
||||||
private String chargeRefundChargeType;
|
private String chargeRefundChargeType;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
||||||
private Set<LoanCollateralManagement> loanCollateralManagementSet = new HashSet<>();
|
private Set<LoanCollateralManagement> loanCollateralManagementSet = new HashSet<>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "loanTransaction")
|
@Getter
|
||||||
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
||||||
private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = new HashSet<>();
|
private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = new HashSet<>();
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "fromTransaction")
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "fromTransaction")
|
||||||
private Set<LoanTransactionRelation> loanTransactionRelations = new HashSet<>();
|
private Set<LoanTransactionRelation> loanTransactionRelations = new HashSet<>();
|
||||||
|
|
||||||
|
@Setter
|
||||||
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction")
|
||||||
private LoanReAgeParameter loanReAgeParameter;
|
private LoanReAgeParameter loanReAgeParameter;
|
||||||
|
|
||||||
@@ -209,17 +209,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
txnExternalId);
|
txnExternalId);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ExternalId externalId) {
|
final LocalDate paymentDate, final ExternalId externalId) {
|
||||||
return new LoanTransaction(null, office, LoanTransactionType.RECOVERY_REPAYMENT, paymentDetail, amount.getAmount(), paymentDate,
|
return new LoanTransaction(null, office, LoanTransactionType.RECOVERY_REPAYMENT, paymentDetail, amount.getAmount(), paymentDate,
|
||||||
@@ -527,10 +516,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return Money.of(currency, this.interestPortion);
|
return Money.of(currency, this.interestPortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money getUnrecognizedIncomePortion(final MonetaryCurrency currency) {
|
|
||||||
return Money.of(currency, this.unrecognizedIncomePortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Money getFeeChargesPortion(final MonetaryCurrency currency) {
|
public Money getFeeChargesPortion(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.feeChargesPortion);
|
return Money.of(currency, this.feeChargesPortion);
|
||||||
}
|
}
|
||||||
@@ -563,10 +548,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return !isReversed();
|
return !isReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReversed() {
|
|
||||||
this.reversed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setManuallyAdjustedOrReversed() {
|
public void setManuallyAdjustedOrReversed() {
|
||||||
this.manuallyAdjustedOrReversed = true;
|
this.manuallyAdjustedOrReversed = true;
|
||||||
}
|
}
|
||||||
@@ -621,10 +602,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return LoanTransactionType.INCOME_POSTING.equals(getTypeOf()) && isNotReversed();
|
return LoanTransactionType.INCOME_POSTING.equals(getTypeOf()) && isNotReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotIncomePosting() {
|
|
||||||
return !isIncomePosting();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDisbursement() {
|
public boolean isDisbursement() {
|
||||||
return LoanTransactionType.DISBURSEMENT.equals(getTypeOf()) && isNotReversed();
|
return LoanTransactionType.DISBURSEMENT.equals(getTypeOf()) && isNotReversed();
|
||||||
}
|
}
|
||||||
@@ -649,10 +626,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return LoanTransactionType.WAIVE_CHARGES.equals(getTypeOf()) && isNotReversed();
|
return LoanTransactionType.WAIVE_CHARGES.equals(getTypeOf()) && isNotReversed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotInterestWaiver() {
|
|
||||||
return !isInterestWaiver();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isWaiver() {
|
public boolean isWaiver() {
|
||||||
return isInterestWaiver() || isChargesWaiver();
|
return isInterestWaiver() || isChargesWaiver();
|
||||||
}
|
}
|
||||||
@@ -720,87 +693,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return !MathUtil.isEmpty(amount);
|
return !MathUtil.isEmpty(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> toMapData(final String currencyCode) {
|
|
||||||
final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(this.typeOf);
|
|
||||||
|
|
||||||
thisTransactionData.put("id", getId());
|
|
||||||
thisTransactionData.put("officeId", this.office.getId());
|
|
||||||
thisTransactionData.put("type", transactionType);
|
|
||||||
thisTransactionData.put("reversed", isReversed());
|
|
||||||
thisTransactionData.put("date", getTransactionDate());
|
|
||||||
thisTransactionData.put("currencyCode", currencyCode);
|
|
||||||
thisTransactionData.put("amount", this.amount);
|
|
||||||
thisTransactionData.put("netDisbursalAmount", this.loan.getNetDisbursalAmount());
|
|
||||||
|
|
||||||
if (transactionType.isChargeback()
|
|
||||||
&& (getLoan().getCreditAllocationRules() == null || getLoan().getCreditAllocationRules().size() == 0)) {
|
|
||||||
thisTransactionData.put("principalPortion", this.amount);
|
|
||||||
} else {
|
|
||||||
thisTransactionData.put("principalPortion", this.principalPortion);
|
|
||||||
}
|
|
||||||
|
|
||||||
thisTransactionData.put("interestPortion", this.interestPortion);
|
|
||||||
thisTransactionData.put("feeChargesPortion", this.feeChargesPortion);
|
|
||||||
thisTransactionData.put("penaltyChargesPortion", this.penaltyChargesPortion);
|
|
||||||
thisTransactionData.put("overPaymentPortion", this.overPaymentPortion);
|
|
||||||
if (transactionType.isChargeRefund()) {
|
|
||||||
thisTransactionData.put("chargeRefundChargeType", this.chargeRefundChargeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.paymentDetail != null) {
|
|
||||||
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.loanChargesPaid.isEmpty()) {
|
|
||||||
final List<Map<String, Object>> loanChargesPaidData = new ArrayList<>();
|
|
||||||
for (final LoanChargePaidBy chargePaidBy : this.loanChargesPaid) {
|
|
||||||
final Map<String, Object> loanChargePaidData = new LinkedHashMap<>();
|
|
||||||
loanChargePaidData.put("chargeId", chargePaidBy.getLoanCharge().getCharge().getId());
|
|
||||||
loanChargePaidData.put("isPenalty", chargePaidBy.getLoanCharge().isPenaltyCharge());
|
|
||||||
loanChargePaidData.put("loanChargeId", chargePaidBy.getLoanCharge().getId());
|
|
||||||
loanChargePaidData.put("amount", chargePaidBy.getAmount());
|
|
||||||
|
|
||||||
loanChargesPaidData.add(loanChargePaidData);
|
|
||||||
}
|
|
||||||
thisTransactionData.put("loanChargesPaid", loanChargesPaidData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transactionType.isChargeback() && this.overPaymentPortion != null && this.overPaymentPortion.compareTo(BigDecimal.ZERO) > 0) {
|
|
||||||
BigDecimal principalPaid = overPaymentPortion;
|
|
||||||
BigDecimal feePaid = BigDecimal.ZERO;
|
|
||||||
BigDecimal penaltyPaid = BigDecimal.ZERO;
|
|
||||||
if (getLoanTransactionToRepaymentScheduleMappings().size() > 0) {
|
|
||||||
principalPaid = getLoanTransactionToRepaymentScheduleMappings().stream()
|
|
||||||
.map(mapping -> Optional.ofNullable(mapping.getPrincipalPortion()).orElse(BigDecimal.ZERO))
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
feePaid = getLoanTransactionToRepaymentScheduleMappings().stream()
|
|
||||||
.map(mapping -> Optional.ofNullable(mapping.getFeeChargesPortion()).orElse(BigDecimal.ZERO))
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
penaltyPaid = getLoanTransactionToRepaymentScheduleMappings().stream()
|
|
||||||
.map(mapping -> Optional.ofNullable(mapping.getPenaltyChargesPortion()).orElse(BigDecimal.ZERO))
|
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
||||||
}
|
|
||||||
thisTransactionData.put("principalPaid", principalPaid);
|
|
||||||
thisTransactionData.put("feePaid", feePaid);
|
|
||||||
thisTransactionData.put("penaltyPaid", penaltyPaid);
|
|
||||||
}
|
|
||||||
|
|
||||||
LoanTransactionRelation loanTransactionRelation = loanTransactionRelations.stream()
|
|
||||||
.filter(e -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT.equals(e.getRelationType())).findAny().orElse(null);
|
|
||||||
if (loanTransactionRelation != null) {
|
|
||||||
LoanCharge loanCharge = loanTransactionRelation.getToCharge();
|
|
||||||
thisTransactionData.put("loanChargeData", loanCharge.toData());
|
|
||||||
}
|
|
||||||
|
|
||||||
return thisTransactionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLoanChargesPaid(final Set<LoanChargePaidBy> loanChargesPaid) {
|
|
||||||
this.loanChargesPaid = loanChargesPaid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRefund() {
|
public boolean isRefund() {
|
||||||
return LoanTransactionType.REFUND.equals(getTypeOf()) && isNotReversed();
|
return LoanTransactionType.REFUND.equals(getTypeOf()) && isNotReversed();
|
||||||
}
|
}
|
||||||
@@ -878,7 +770,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateMappingDetail(final Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings,
|
private void updateMappingDetail(final Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings,
|
||||||
final LoanTransactionToRepaymentScheduleMapping updatedMapping, boolean overwrite) {
|
final LoanTransactionToRepaymentScheduleMapping updatedMapping, boolean overwrite) {
|
||||||
boolean isMappingUpdated = false;
|
boolean isMappingUpdated = false;
|
||||||
LoanRepaymentScheduleInstallment updatedInstallment = updatedMapping.getLoanRepaymentScheduleInstallment();
|
LoanRepaymentScheduleInstallment updatedInstallment = updatedMapping.getLoanRepaymentScheduleInstallment();
|
||||||
@@ -910,7 +802,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
retainMappings.add(newMapping);
|
retainMappings.add(newMapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isMappingUpdated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanChargePaidMappings(final Collection<LoanChargePaidBy> updatedMappings) {
|
public void updateLoanChargePaidMappings(final Collection<LoanChargePaidBy> updatedMappings) {
|
||||||
@@ -921,7 +812,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
this.loanChargesPaid.retainAll(retainMappings);
|
this.loanChargesPaid.retainAll(retainMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateLoanChargePaid(final Collection<LoanChargePaidBy> retainMappings, final LoanChargePaidBy updatedMapping) {
|
private void updateLoanChargePaid(final Collection<LoanChargePaidBy> retainMappings, final LoanChargePaidBy updatedMapping) {
|
||||||
boolean updated = false;
|
boolean updated = false;
|
||||||
LoanCharge updatedCharge = updatedMapping.getLoanCharge();
|
LoanCharge updatedCharge = updatedMapping.getLoanCharge();
|
||||||
Integer updatedInstallment = updatedMapping.getInstallmentNumber();
|
Integer updatedInstallment = updatedMapping.getInstallmentNumber();
|
||||||
@@ -941,11 +832,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
this.loanChargesPaid.add(newMapping);
|
this.loanChargesPaid.add(newMapping);
|
||||||
retainMappings.add(newMapping);
|
retainMappings.add(newMapping);
|
||||||
}
|
}
|
||||||
return updated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<LoanTransactionToRepaymentScheduleMapping> getLoanTransactionToRepaymentScheduleMappings() {
|
|
||||||
return this.loanTransactionToRepaymentScheduleMappings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean isAllowTypeTransactionAtTheTimeOfLastUndo() {
|
public Boolean isAllowTypeTransactionAtTheTimeOfLastUndo() {
|
||||||
@@ -961,10 +847,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
|| this.isIncomePosting());
|
|| this.isIncomePosting());
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getSubmittedOnDate() {
|
|
||||||
return submittedOnDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasLoanTransactionRelations() {
|
public boolean hasLoanTransactionRelations() {
|
||||||
return !loanTransactionRelations.isEmpty();
|
return !loanTransactionRelations.isEmpty();
|
||||||
}
|
}
|
||||||
@@ -996,10 +878,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
|
|||||||
return DateUtils.isEqual(getTransactionDate(), date);
|
return DateUtils.isEqual(getTransactionDate(), date);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoanReAgeParameter(LoanReAgeParameter loanReAgeParameter) {
|
|
||||||
this.loanReAgeParameter = loanReAgeParameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean happenedBefore(LoanTransaction loanTransaction) {
|
public boolean happenedBefore(LoanTransaction loanTransaction) {
|
||||||
return LoanTransactionComparator.INSTANCE.compare(this, loanTransaction) < 0;
|
return LoanTransactionComparator.INSTANCE.compare(this, loanTransaction) < 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -172,6 +172,13 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactionsTransactional(
|
||||||
|
final LocalDate disbursementDate, final LocalDate targetDate, final List<LoanTransaction> loanTransactions,
|
||||||
|
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges) {
|
||||||
|
return reprocessProgressiveLoanTransactions(disbursementDate, targetDate, loanTransactions, currency, installments, charges);
|
||||||
|
}
|
||||||
|
|
||||||
// only for progressive loans
|
// only for progressive loans
|
||||||
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactions(
|
public Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessProgressiveLoanTransactions(
|
||||||
LocalDate disbursementDate, LocalDate targetDate, List<LoanTransaction> loanTransactions, MonetaryCurrency currency,
|
LocalDate disbursementDate, LocalDate targetDate, List<LoanTransaction> loanTransactions, MonetaryCurrency currency,
|
||||||
|
|||||||
+7
-7
@@ -93,13 +93,13 @@ public class InternalProgressiveLoanApiResource implements InitializingBean {
|
|||||||
.map(progressiveLoanInterestScheduleModelParserService::toJson).orElse(null);
|
.map(progressiveLoanInterestScheduleModelParserService::toJson).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(Loan loan) {
|
private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) {
|
||||||
List<LoanTransaction> transactionsToReprocess = loan.retrieveListOfTransactionsForReprocessing();
|
final List<LoanTransaction> transactionsToReprocess = loan.retrieveListOfTransactionsForReprocessing();
|
||||||
LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate();
|
final LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate();
|
||||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor
|
final Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor
|
||||||
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), businessDate, transactionsToReprocess, loan.getCurrency(),
|
.reprocessProgressiveLoanTransactionsTransactional(loan.getDisbursementDate(), businessDate, transactionsToReprocess,
|
||||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
||||||
ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight();
|
final ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight();
|
||||||
final List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
|
final List<Long> replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft()
|
||||||
.getTransactionChanges().stream().filter(change -> change.getOldTransaction() != null)
|
.getTransactionChanges().stream().filter(change -> change.getOldTransaction() != null)
|
||||||
.map(change -> change.getNewTransaction().getId()).filter(Objects::nonNull).toList();
|
.map(change -> change.getNewTransaction().getId()).filter(Objects::nonNull).toList();
|
||||||
|
|||||||
+2
@@ -34,8 +34,10 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCollateralManagement
|
|||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Transactional(readOnly = true)
|
||||||
public class ClientCollateralManagementReadPlatformServiceImpl implements ClientCollateralManagementReadPlatformService {
|
public class ClientCollateralManagementReadPlatformServiceImpl implements ClientCollateralManagementReadPlatformService {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
|
|||||||
+2
@@ -45,9 +45,11 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanResch
|
|||||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||||
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Transactional(readOnly = true)
|
||||||
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
|
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
|
||||||
|
|
||||||
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
|
||||||
|
|||||||
+2
@@ -33,6 +33,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
|||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class CumulativeLoanSummaryDataProvider extends CommonLoanSummaryDataProvider {
|
public class CumulativeLoanSummaryDataProvider extends CommonLoanSummaryDataProvider {
|
||||||
@@ -65,6 +66,7 @@ public class CumulativeLoanSummaryDataProvider extends CommonLoanSummaryDataProv
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
public LoanSummaryData withTransactionAmountsSummary(Long loanId, LoanSummaryData defaultSummaryData,
|
public LoanSummaryData withTransactionAmountsSummary(Long loanId, LoanSummaryData defaultSummaryData,
|
||||||
LoanScheduleData repaymentSchedule, Collection<LoanTransactionBalance> loanTransactionBalances) {
|
LoanScheduleData repaymentSchedule, Collection<LoanTransactionBalance> loanTransactionBalances) {
|
||||||
Loan loan = null;
|
Loan loan = null;
|
||||||
|
|||||||
+162
-153
@@ -140,11 +140,12 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void addPeriodicAccruals(@NotNull LocalDate tillDate, @NotNull Loan loan) {
|
public void addPeriodicAccruals(@NotNull final LocalDate tillDate, @NotNull final Loan loan) {
|
||||||
if (loan.isClosed() || loan.getStatus().isOverpaid()) {
|
if (loan.isClosed() || loan.getStatus().isOverpaid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addAccruals(loan, tillDate, true, false, true);
|
final boolean chargeOnDueDate = isChargeOnDueDate();
|
||||||
|
addAccruals(loan, tillDate, true, false, true, chargeOnDueDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,8 +154,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void addAccruals(@NotNull LocalDate tillDate) throws JobExecutionException {
|
public void addAccruals(@NotNull LocalDate tillDate) throws JobExecutionException {
|
||||||
List<Loan> loans = loanRepositoryWrapper.findLoansForAddAccrual(AccountingRuleType.ACCRUAL_PERIODIC, tillDate,
|
final boolean chargeOnDueDate = isChargeOnDueDate();
|
||||||
!isChargeOnDueDate());
|
List<Loan> loans = loanRepositoryWrapper.findLoansForAddAccrual(AccountingRuleType.ACCRUAL_PERIODIC, tillDate, !chargeOnDueDate);
|
||||||
|
|
||||||
List<Future<?>> loanTasks = new ArrayList<>();
|
List<Future<?>> loanTasks = new ArrayList<>();
|
||||||
|
|
||||||
@@ -168,7 +169,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(outerLoan.getId());
|
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(outerLoan.getId());
|
||||||
try {
|
try {
|
||||||
log.debug("Adding accruals for loan '{}'", loan.getId());
|
log.debug("Adding accruals for loan '{}'", loan.getId());
|
||||||
addAccruals(loan, tillDate, false, false, true);
|
addAccruals(loan, tillDate, false, false, true, chargeOnDueDate);
|
||||||
log.debug("Successfully processed loan: '{}' for accrual entries", loan.getId());
|
log.debug("Successfully processed loan: '{}' for accrual entries", loan.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to add accrual for loan {}", loan.getId(), e);
|
log.error("Failed to add accrual for loan {}", loan.getId(), e);
|
||||||
@@ -224,7 +225,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
addAccruals(loan, accruedTill, true, false, addJournal);
|
final boolean chargeOnDueDate = isChargeOnDueDate();
|
||||||
|
addAccruals(loan, accruedTill, true, false, addJournal, chargeOnDueDate);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String globalisationMessageCode = "error.msg.accrual.exception";
|
String globalisationMessageCode = "error.msg.accrual.exception";
|
||||||
throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e);
|
throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e);
|
||||||
@@ -286,7 +288,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
@Override
|
@Override
|
||||||
public void processAccrualsOnLoanClosure(@NonNull final Loan loan, final boolean addJournal) {
|
public void processAccrualsOnLoanClosure(@NonNull final Loan loan, final boolean addJournal) {
|
||||||
// check and process accruals for loan WITHOUT interest recalculation details and compounding posted as income
|
// check and process accruals for loan WITHOUT interest recalculation details and compounding posted as income
|
||||||
addAccruals(loan, loan.getLastLoanRepaymentScheduleInstallment().getDueDate(), false, true, addJournal);
|
final boolean chargeOnDueDate = isChargeOnDueDate();
|
||||||
|
addAccruals(loan, loan.getLastLoanRepaymentScheduleInstallment().getDueDate(), false, true, addJournal, chargeOnDueDate);
|
||||||
if (isProgressiveAccrual(loan)) {
|
if (isProgressiveAccrual(loan)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -325,28 +328,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
|
|
||||||
// PeriodicAccruals
|
// PeriodicAccruals
|
||||||
|
|
||||||
private void addAccruals(@NotNull Loan loan, @NotNull LocalDate tillDate, boolean periodic, boolean isFinal, boolean addJournal) {
|
private void addAccruals(@NotNull final Loan loan, @NotNull LocalDate tillDate, final boolean periodic, final boolean isFinal,
|
||||||
|
final boolean addJournal, final boolean chargeOnDueDate) {
|
||||||
if ((!isFinal && !loan.isOpen()) || loan.isNpa() || loan.isChargedOff()
|
if ((!isFinal && !loan.isOpen()) || loan.isNpa() || loan.isChargedOff()
|
||||||
|| !loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
|| !loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails();
|
final LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails();
|
||||||
if (recalculationDetails != null && recalculationDetails.isCompoundingToBePostedAsTransaction()) {
|
if (recalculationDetails != null && recalculationDetails.isCompoundingToBePostedAsTransaction()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<LoanTransaction> existingAccruals = retrieveListOfAccrualTransactions(loan);
|
final List<LoanTransaction> existingAccruals = retrieveListOfAccrualTransactions(loan);
|
||||||
LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate();
|
final LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate();
|
||||||
reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal);
|
reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal);
|
||||||
ensureAccrualTransactionMappings(loan);
|
ensureAccrualTransactionMappings(loan, existingAccruals, chargeOnDueDate);
|
||||||
if (DateUtils.isAfter(tillDate, lastDueDate)) {
|
if (DateUtils.isAfter(tillDate, lastDueDate)) {
|
||||||
tillDate = lastDueDate;
|
tillDate = lastDueDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean progressiveAccrual = isProgressiveAccrual(loan);
|
final boolean progressiveAccrual = isProgressiveAccrual(loan);
|
||||||
LocalDate accruedTill = loan.getAccruedTill();
|
final LocalDate accruedTill = loan.getAccruedTill();
|
||||||
LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
final LocalDate businessDate = DateUtils.getBusinessLocalDate();
|
||||||
LocalDate accrualDate = isFinal
|
final LocalDate accrualDate = isFinal
|
||||||
? (progressiveAccrual ? (DateUtils.isBefore(lastDueDate, businessDate) ? lastDueDate : businessDate)
|
? (progressiveAccrual ? (DateUtils.isBefore(lastDueDate, businessDate) ? lastDueDate : businessDate)
|
||||||
: getFinalAccrualTransactionDate(loan))
|
: getFinalAccrualTransactionDate(loan))
|
||||||
: tillDate;
|
: tillDate;
|
||||||
@@ -358,51 +362,51 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic, isFinal);
|
final AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic, isFinal, chargeOnDueDate);
|
||||||
boolean mergeTransactions = isFinal || progressiveAccrual;
|
final boolean mergeTransactions = isFinal || progressiveAccrual;
|
||||||
MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency();
|
final MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency();
|
||||||
List<LoanTransaction> accrualTransactions = new ArrayList<>();
|
List<LoanTransaction> accrualTransactions = new ArrayList<>();
|
||||||
Money totalInterestPortion = null;
|
Money totalInterestPortion = null;
|
||||||
LoanTransaction mergeAccrualTransaction = null;
|
LoanTransaction mergeAccrualTransaction = null;
|
||||||
LoanTransaction mergeAdjustTransaction = null;
|
LoanTransaction mergeAdjustTransaction = null;
|
||||||
for (AccrualPeriodData period : accrualPeriods.getPeriods()) {
|
for (AccrualPeriodData period : accrualPeriods.getPeriods()) {
|
||||||
Money interestAccruable = MathUtil.nullToZero(period.getInterestAccruable(), currency);
|
final Money interestAccruable = MathUtil.nullToZero(period.getInterestAccruable(), currency);
|
||||||
Money interestPortion = MathUtil.minus(interestAccruable, period.getInterestAccrued());
|
final Money interestPortion = MathUtil.minus(interestAccruable, period.getInterestAccrued());
|
||||||
Money feeAccruable = MathUtil.nullToZero(period.getFeeAccruable(), currency);
|
final Money feeAccruable = MathUtil.nullToZero(period.getFeeAccruable(), currency);
|
||||||
Money feePortion = MathUtil.minus(feeAccruable, period.getFeeAccrued());
|
final Money feePortion = MathUtil.minus(feeAccruable, period.getFeeAccrued());
|
||||||
Money penaltyAccruable = MathUtil.nullToZero(period.getPenaltyAccruable(), currency);
|
final Money penaltyAccruable = MathUtil.nullToZero(period.getPenaltyAccruable(), currency);
|
||||||
Money penaltyPortion = MathUtil.minus(penaltyAccruable, period.getPenaltyAccrued());
|
final Money penaltyPortion = MathUtil.minus(penaltyAccruable, period.getPenaltyAccrued());
|
||||||
if (MathUtil.isEmpty(interestPortion) && MathUtil.isEmpty(feePortion) && MathUtil.isEmpty(penaltyPortion)) {
|
if (MathUtil.isEmpty(interestPortion) && MathUtil.isEmpty(feePortion) && MathUtil.isEmpty(penaltyPortion)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (mergeTransactions) {
|
if (mergeTransactions) {
|
||||||
totalInterestPortion = MathUtil.plus(totalInterestPortion, interestPortion);
|
totalInterestPortion = MathUtil.plus(totalInterestPortion, interestPortion);
|
||||||
if (progressiveAccrual) {
|
if (progressiveAccrual) {
|
||||||
Money feeAdjustmentPortion = MathUtil.negate(feePortion);
|
final Money feeAdjustmentPortion = MathUtil.negate(feePortion);
|
||||||
Money penaltyAdjustmentPortion = MathUtil.negate(penaltyPortion);
|
final Money penaltyAdjustmentPortion = MathUtil.negate(penaltyPortion);
|
||||||
mergeAdjustTransaction = createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, period,
|
mergeAdjustTransaction = createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, period,
|
||||||
accrualTransactions, null, feeAdjustmentPortion, penaltyAdjustmentPortion, true);
|
accrualTransactions, null, feeAdjustmentPortion, penaltyAdjustmentPortion, true);
|
||||||
}
|
}
|
||||||
mergeAccrualTransaction = createOrMergeAccrualTransaction(loan, mergeAccrualTransaction, accrualDate, period,
|
mergeAccrualTransaction = createOrMergeAccrualTransaction(loan, mergeAccrualTransaction, accrualDate, period,
|
||||||
accrualTransactions, null, feePortion, penaltyPortion, false);
|
accrualTransactions, null, feePortion, penaltyPortion, false);
|
||||||
} else {
|
} else {
|
||||||
LocalDate dueDate = period.getDueDate();
|
final LocalDate dueDate = period.getDueDate();
|
||||||
if (!isFinal && DateUtils.isAfter(dueDate, tillDate) && DateUtils.isBefore(tillDate, accruedTill)) {
|
if (!isFinal && DateUtils.isAfter(dueDate, tillDate) && DateUtils.isBefore(tillDate, accruedTill)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
LocalDate periodAccrualDate = DateUtils.isBefore(dueDate, accrualDate) ? dueDate : accrualDate;
|
final LocalDate periodAccrualDate = DateUtils.isBefore(dueDate, accrualDate) ? dueDate : accrualDate;
|
||||||
LoanTransaction accrualTransaction = addAccrualTransaction(loan, periodAccrualDate, period, interestPortion, feePortion,
|
final LoanTransaction accrualTransaction = addAccrualTransaction(loan, periodAccrualDate, period, interestPortion,
|
||||||
penaltyPortion, false);
|
feePortion, penaltyPortion, false);
|
||||||
if (accrualTransaction != null) {
|
if (accrualTransaction != null) {
|
||||||
accrualTransactions.add(accrualTransaction);
|
accrualTransactions.add(accrualTransaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber());
|
final LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber());
|
||||||
installment.updateAccrualPortion(interestAccruable, feeAccruable, penaltyAccruable);
|
installment.updateAccrualPortion(interestAccruable, feeAccruable, penaltyAccruable);
|
||||||
}
|
}
|
||||||
if (mergeTransactions && !MathUtil.isEmpty(totalInterestPortion)) {
|
if (mergeTransactions && !MathUtil.isEmpty(totalInterestPortion)) {
|
||||||
if (progressiveAccrual) {
|
if (progressiveAccrual) {
|
||||||
Money interestAdjustmentPortion = MathUtil.negate(totalInterestPortion);
|
final Money interestAdjustmentPortion = MathUtil.negate(totalInterestPortion);
|
||||||
createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, null, accrualTransactions,
|
createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, null, accrualTransactions,
|
||||||
interestAdjustmentPortion, null, null, true);
|
interestAdjustmentPortion, null, null, true);
|
||||||
}
|
}
|
||||||
@@ -416,21 +420,22 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
if (!isFinal || progressiveAccrual) {
|
if (!isFinal || progressiveAccrual) {
|
||||||
loan.setAccruedTill(isFinal ? accrualDate : tillDate);
|
loan.setAccruedTill(isFinal ? accrualDate : tillDate);
|
||||||
}
|
}
|
||||||
List<AccountingBridgeLoanTransactionDTO> newTransactionDTOs = new ArrayList<>();
|
|
||||||
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
accrualTransactions = loanTransactionRepository.saveAll(accrualTransactions);
|
||||||
accrualTransaction = loanTransactionRepository.saveAndFlush(accrualTransaction);
|
loanTransactionRepository.flush();
|
||||||
if (addJournal) {
|
|
||||||
LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
|
if (addJournal) {
|
||||||
|
final List<AccountingBridgeLoanTransactionDTO> newTransactionDTOs = new ArrayList<>();
|
||||||
|
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
||||||
|
final LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
|
||||||
? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
|
? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
|
||||||
: new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
|
: new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
|
businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
|
||||||
AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper.mapToLoanTransactionData(accrualTransaction,
|
final AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper
|
||||||
currency.getCode());
|
.mapToLoanTransactionData(accrualTransaction, currency.getCode());
|
||||||
newTransactionDTOs.add(transactionDTO);
|
newTransactionDTOs.add(transactionDTO);
|
||||||
}
|
}
|
||||||
}
|
final AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(),
|
||||||
if (addJournal) {
|
|
||||||
AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(),
|
|
||||||
loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(),
|
loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(),
|
||||||
loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(),
|
loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(),
|
||||||
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), false,
|
loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), false,
|
||||||
@@ -439,46 +444,47 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccrualPeriodsData calculateAccrualAmounts(@NotNull Loan loan, @NotNull LocalDate tillDate, boolean periodic, boolean isFinal) {
|
private AccrualPeriodsData calculateAccrualAmounts(@NotNull final Loan loan, @NotNull final LocalDate tillDate, final boolean periodic,
|
||||||
boolean chargeOnDueDate = isChargeOnDueDate();
|
final boolean isFinal, final boolean chargeOnDueDate) {
|
||||||
LoanProductRelatedDetail productDetail = loan.getLoanProductRelatedDetail();
|
final LoanProductRelatedDetail productDetail = loan.getLoanProductRelatedDetail();
|
||||||
MonetaryCurrency currency = productDetail.getCurrency();
|
final MonetaryCurrency currency = productDetail.getCurrency();
|
||||||
LoanScheduleGenerator scheduleGenerator = loanScheduleFactory.create(productDetail.getLoanScheduleType(),
|
final LoanScheduleGenerator scheduleGenerator = loanScheduleFactory.create(productDetail.getLoanScheduleType(),
|
||||||
productDetail.getInterestMethod());
|
productDetail.getInterestMethod());
|
||||||
int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
final int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
||||||
LocalDate interestCalculationTillDate = loan.isProgressiveSchedule()
|
final LocalDate interestCalculationTillDate = loan.isProgressiveSchedule()
|
||||||
&& loan.getLoanProductRelatedDetail().isInterestRecognitionOnDisbursementDate() ? tillDate.plusDays(1L) : tillDate;
|
&& loan.getLoanProductRelatedDetail().isInterestRecognitionOnDisbursementDate() ? tillDate.plusDays(1L) : tillDate;
|
||||||
List<LoanRepaymentScheduleInstallment> installments = isFinal ? loan.getRepaymentScheduleInstallments()
|
final List<LoanRepaymentScheduleInstallment> installments = isFinal ? loan.getRepaymentScheduleInstallments()
|
||||||
: getInstallmentsToAccrue(loan, interestCalculationTillDate, periodic);
|
: getInstallmentsToAccrue(loan, interestCalculationTillDate, periodic, chargeOnDueDate);
|
||||||
AccrualPeriodsData accrualPeriods = AccrualPeriodsData.create(installments, firstInstallmentNumber, currency);
|
final AccrualPeriodsData accrualPeriods = AccrualPeriodsData.create(installments, firstInstallmentNumber, currency);
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
addInterestAccrual(loan, interestCalculationTillDate, scheduleGenerator, installment, accrualPeriods);
|
addInterestAccrual(loan, interestCalculationTillDate, scheduleGenerator, installment, accrualPeriods);
|
||||||
addChargeAccrual(loan, tillDate, chargeOnDueDate, installment, accrualPeriods, isFinal);
|
addChargeAccrual(loan, tillDate, chargeOnDueDate, installment, accrualPeriods);
|
||||||
}
|
}
|
||||||
return accrualPeriods;
|
return accrualPeriods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<LoanRepaymentScheduleInstallment> getInstallmentsToAccrue(@NotNull Loan loan, @NotNull LocalDate tillDate,
|
private List<LoanRepaymentScheduleInstallment> getInstallmentsToAccrue(@NotNull final Loan loan, @NotNull final LocalDate tillDate,
|
||||||
boolean periodic) {
|
final boolean periodic, final boolean chargeOnDueDate) {
|
||||||
LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
final LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate();
|
||||||
int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
final int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
||||||
return loan.getRepaymentScheduleInstallments(i -> !i.isDownPayment() && (!isChargeOnDueDate()
|
return loan.getRepaymentScheduleInstallments(i -> !i.isDownPayment()
|
||||||
|| (periodic ? !isBeforePeriod(tillDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber))
|
&& (!chargeOnDueDate || (periodic ? !isBeforePeriod(tillDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber))
|
||||||
: isFullPeriod(tillDate, i)))
|
: isFullPeriod(tillDate, i)))
|
||||||
&& !isAfterPeriod(organisationStartDate, i));
|
&& !isAfterPeriod(organisationStartDate, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addInterestAccrual(@NotNull Loan loan, @NotNull LocalDate tillDate, LoanScheduleGenerator scheduleGenerator,
|
private void addInterestAccrual(@NotNull final Loan loan, @NotNull final LocalDate tillDate,
|
||||||
@NotNull LoanRepaymentScheduleInstallment installment, @NotNull AccrualPeriodsData accrualPeriods) {
|
final LoanScheduleGenerator scheduleGenerator, @NotNull final LoanRepaymentScheduleInstallment installment,
|
||||||
|
@NotNull final AccrualPeriodsData accrualPeriods) {
|
||||||
if (installment.isAdditional() || installment.isReAged()) {
|
if (installment.isAdditional() || installment.isReAged()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber());
|
final AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber());
|
||||||
MonetaryCurrency currency = accrualPeriods.getCurrency();
|
final MonetaryCurrency currency = accrualPeriods.getCurrency();
|
||||||
Money interest = null;
|
Money interest = null;
|
||||||
boolean isPastPeriod = isAfterPeriod(tillDate, installment);
|
final boolean isPastPeriod = isAfterPeriod(tillDate, installment);
|
||||||
boolean isInPeriod = isInPeriod(tillDate, installment, false);
|
final boolean isInPeriod = isInPeriod(tillDate, installment, false);
|
||||||
if (isPastPeriod || loan.isClosed() || loan.isOverPaid()) {
|
if (isPastPeriod || loan.isClosed() || loan.isOverPaid()) {
|
||||||
interest = installment.getInterestCharged(currency).minus(installment.getCreditedInterest());
|
interest = installment.getInterestCharged(currency).minus(installment.getCreditedInterest());
|
||||||
} else {
|
} else {
|
||||||
@@ -497,13 +503,14 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
unrecognizedWaived = MathUtil.min(unrecognizedWaived,
|
unrecognizedWaived = MathUtil.min(unrecognizedWaived,
|
||||||
MathUtil.minusToZero(installment.getInterestWaived(currency), transactionWaived), false);
|
MathUtil.minusToZero(installment.getInterestWaived(currency), transactionWaived), false);
|
||||||
period.setUnrecognizedWaive(unrecognizedWaived);
|
period.setUnrecognizedWaive(unrecognizedWaived);
|
||||||
Money waived = isPastPeriod ? installment.getInterestWaived(currency) : MathUtil.plus(transactionWaived, unrecognizedWaived);
|
final Money waived = isPastPeriod ? installment.getInterestWaived(currency)
|
||||||
|
: MathUtil.plus(transactionWaived, unrecognizedWaived);
|
||||||
accruable = MathUtil.minusToZero(period.getInterestAmount(), waived);
|
accruable = MathUtil.minusToZero(period.getInterestAmount(), waived);
|
||||||
}
|
}
|
||||||
period.setInterestAccruable(accruable);
|
period.setInterestAccruable(accruable);
|
||||||
Money transactionAccrued = MathUtil.toMoney(calcInterestAccruedAmount(installment, accrualPeriods, tillDate), currency);
|
final Money transactionAccrued = MathUtil.toMoney(calcInterestAccruedAmount(installment, accrualPeriods, tillDate), currency);
|
||||||
period.setTransactionAccrued(transactionAccrued);
|
period.setTransactionAccrued(transactionAccrued);
|
||||||
Money accrued = MathUtil.minusToZero(transactionAccrued, transactionWaived);
|
final Money accrued = MathUtil.minusToZero(transactionAccrued, transactionWaived);
|
||||||
period.setInterestAccrued(accrued);
|
period.setInterestAccrued(accrued);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,13 +565,13 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addChargeAccrual(@NotNull Loan loan, @NotNull LocalDate tillDate, boolean chargeOnDueDate,
|
private void addChargeAccrual(@NotNull final Loan loan, @NotNull final LocalDate tillDate, final boolean chargeOnDueDate,
|
||||||
@NotNull LoanRepaymentScheduleInstallment installment, @NotNull AccrualPeriodsData accrualPeriods, boolean isFinal) {
|
@NotNull final LoanRepaymentScheduleInstallment installment, @NotNull final AccrualPeriodsData accrualPeriods) {
|
||||||
AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber());
|
final AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber());
|
||||||
LocalDate dueDate = installment.getDueDate();
|
final LocalDate dueDate = installment.getDueDate();
|
||||||
Collection<LoanCharge> loanCharges;
|
final Collection<LoanCharge> loanCharges = loan
|
||||||
loanCharges = loan.getLoanCharges(lc -> !lc.isDueAtDisbursement() && (lc.isInstalmentFee() ? !DateUtils.isBefore(tillDate, dueDate)
|
.getLoanCharges(lc -> !lc.isDueAtDisbursement() && (lc.isInstalmentFee() ? !DateUtils.isBefore(tillDate, dueDate)
|
||||||
: isChargeDue(lc, tillDate, chargeOnDueDate, installment, period.isFirstPeriod())));
|
: isChargeDue(lc, tillDate, chargeOnDueDate, installment, period.isFirstPeriod())));
|
||||||
for (LoanCharge loanCharge : loanCharges) {
|
for (LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isActive()) {
|
if (loanCharge.isActive()) {
|
||||||
addChargeAccrual(loanCharge, tillDate, chargeOnDueDate, installment, accrualPeriods);
|
addChargeAccrual(loanCharge, tillDate, chargeOnDueDate, installment, accrualPeriods);
|
||||||
@@ -572,23 +579,23 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addChargeAccrual(@NotNull LoanCharge loanCharge, @NotNull LocalDate tillDate, boolean chargeOnDueDate,
|
private void addChargeAccrual(@NotNull final LoanCharge loanCharge, @NotNull final LocalDate tillDate, final boolean chargeOnDueDate,
|
||||||
@NotNull LoanRepaymentScheduleInstallment installment, @NotNull AccrualPeriodsData accrualPeriods) {
|
@NotNull final LoanRepaymentScheduleInstallment installment, @NotNull final AccrualPeriodsData accrualPeriods) {
|
||||||
MonetaryCurrency currency = accrualPeriods.getCurrency();
|
final MonetaryCurrency currency = accrualPeriods.getCurrency();
|
||||||
Integer firstInstallmentNumber = accrualPeriods.getFirstInstallmentNumber();
|
final Integer firstInstallmentNumber = accrualPeriods.getFirstInstallmentNumber();
|
||||||
boolean installmentFee = loanCharge.isInstalmentFee();
|
final boolean installmentFee = loanCharge.isInstalmentFee();
|
||||||
LoanRepaymentScheduleInstallment dueInstallment = (installmentFee || chargeOnDueDate) ? installment
|
final LoanRepaymentScheduleInstallment dueInstallment = (installmentFee || chargeOnDueDate) ? installment
|
||||||
: loanCharge.getLoan().getRepaymentScheduleInstallment(
|
: loanCharge.getLoan().getRepaymentScheduleInstallment(
|
||||||
i -> isInPeriod(loanCharge.getDueDate(), i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
|
i -> isInPeriod(loanCharge.getDueDate(), i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
|
||||||
AccrualPeriodData duePeriod = accrualPeriods.getPeriodByInstallmentNumber(dueInstallment.getInstallmentNumber());
|
final AccrualPeriodData duePeriod = accrualPeriods.getPeriodByInstallmentNumber(dueInstallment.getInstallmentNumber());
|
||||||
boolean isFullPeriod = isFullPeriod(tillDate, dueInstallment);
|
final boolean isFullPeriod = isFullPeriod(tillDate, dueInstallment);
|
||||||
|
|
||||||
Money chargeAmount;
|
Money chargeAmount;
|
||||||
Money waived;
|
Money waived;
|
||||||
Collection<LoanChargePaidBy> paidBys;
|
Collection<LoanChargePaidBy> paidBys;
|
||||||
Long installmentChargeId = null;
|
Long installmentChargeId = null;
|
||||||
if (installmentFee) {
|
if (installmentFee) {
|
||||||
LoanInstallmentCharge installmentCharge = loanCharge.getInstallmentLoanCharge(dueInstallment.getInstallmentNumber());
|
final LoanInstallmentCharge installmentCharge = loanCharge.getInstallmentLoanCharge(dueInstallment.getInstallmentNumber());
|
||||||
if (installmentCharge == null) {
|
if (installmentCharge == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -603,13 +610,13 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
waived = isFullPeriod ? loanCharge.getAmountWaived(currency)
|
waived = isFullPeriod ? loanCharge.getAmountWaived(currency)
|
||||||
: MathUtil.toMoney(calcChargeWaivedAmount(paidBys, tillDate), currency);
|
: MathUtil.toMoney(calcChargeWaivedAmount(paidBys, tillDate), currency);
|
||||||
}
|
}
|
||||||
AccrualChargeData chargeData = new AccrualChargeData(loanCharge.getId(), installmentChargeId, loanCharge.isPenaltyCharge())
|
final AccrualChargeData chargeData = new AccrualChargeData(loanCharge.getId(), installmentChargeId, loanCharge.isPenaltyCharge())
|
||||||
.setChargeAmount(chargeAmount);
|
.setChargeAmount(chargeAmount);
|
||||||
chargeData.setChargeAccruable(MathUtil.minusToZero(chargeAmount, waived));
|
chargeData.setChargeAccruable(MathUtil.minusToZero(chargeAmount, waived));
|
||||||
|
|
||||||
Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency);
|
final Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency);
|
||||||
Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived);
|
final Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived);
|
||||||
Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency);
|
final Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency);
|
||||||
chargeData.setTransactionAccrued(transactionAccrued);
|
chargeData.setTransactionAccrued(transactionAccrued);
|
||||||
chargeData.setChargeAccrued(MathUtil.minusToZero(transactionAccrued, transactionWaived));
|
chargeData.setChargeAccrued(MathUtil.minusToZero(transactionAccrued, transactionWaived));
|
||||||
|
|
||||||
@@ -617,42 +624,43 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private BigDecimal calcChargeWaivedAmount(@NotNull Collection<LoanChargePaidBy> loanChargePaidBy, @NotNull LocalDate tillDate) {
|
private BigDecimal calcChargeWaivedAmount(@NotNull final Collection<LoanChargePaidBy> loanChargePaidBy,
|
||||||
|
@NotNull final LocalDate tillDate) {
|
||||||
return loanChargePaidBy.stream().filter(pb -> {
|
return loanChargePaidBy.stream().filter(pb -> {
|
||||||
LoanTransaction t = pb.getLoanTransaction();
|
final LoanTransaction t = pb.getLoanTransaction();
|
||||||
return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate);
|
return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate);
|
||||||
}).map(LoanChargePaidBy::getAmount).reduce(BigDecimal.ZERO, MathUtil::add);
|
}).map(LoanChargePaidBy::getAmount).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private BigDecimal calcChargeUnrecognizedWaivedAmount(@NotNull Collection<LoanChargePaidBy> loanChargePaidBy,
|
private BigDecimal calcChargeUnrecognizedWaivedAmount(@NotNull final Collection<LoanChargePaidBy> loanChargePaidBy,
|
||||||
@NotNull LocalDate tillDate) {
|
@NotNull final LocalDate tillDate) {
|
||||||
return loanChargePaidBy.stream().filter(pb -> {
|
return loanChargePaidBy.stream().filter(pb -> {
|
||||||
LoanTransaction t = pb.getLoanTransaction();
|
final LoanTransaction t = pb.getLoanTransaction();
|
||||||
return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate);
|
return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate);
|
||||||
}).map(pb -> pb.getLoanTransaction().getUnrecognizedIncomePortion()).reduce(BigDecimal.ZERO, MathUtil::add);
|
}).map(pb -> pb.getLoanTransaction().getUnrecognizedIncomePortion()).reduce(BigDecimal.ZERO, MathUtil::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private BigDecimal calcChargeAccruedAmount(@NotNull Collection<LoanChargePaidBy> loanChargePaidBy) {
|
private BigDecimal calcChargeAccruedAmount(@NotNull final Collection<LoanChargePaidBy> loanChargePaidBy) {
|
||||||
return loanChargePaidBy.stream().filter(pb -> ACCRUAL_PREDICATE.test(pb.getLoanTransaction()))
|
return loanChargePaidBy.stream().filter(pb -> ACCRUAL_PREDICATE.test(pb.getLoanTransaction()))
|
||||||
.map(pb -> pb.getLoanTransaction().isAccrual() ? pb.getAmount() : MathUtil.negate(pb.getAmount()))
|
.map(pb -> pb.getLoanTransaction().isAccrual() ? pb.getAmount() : MathUtil.negate(pb.getAmount()))
|
||||||
.reduce(BigDecimal.ZERO, MathUtil::add);
|
.reduce(BigDecimal.ZERO, MathUtil::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isChargeDue(@NotNull LoanCharge loanCharge, @NotNull LocalDate tillDate, boolean chargeOnDueDate,
|
private boolean isChargeDue(@NotNull final LoanCharge loanCharge, @NotNull final LocalDate tillDate, boolean chargeOnDueDate,
|
||||||
LoanRepaymentScheduleInstallment installment, boolean isFirstPeriod) {
|
final LoanRepaymentScheduleInstallment installment, final boolean isFirstPeriod) {
|
||||||
LocalDate fromDate = installment.getFromDate();
|
final LocalDate fromDate = installment.getFromDate();
|
||||||
LocalDate dueDate = installment.getDueDate();
|
final LocalDate dueDate = installment.getDueDate();
|
||||||
LocalDate toDate = DateUtils.isBefore(dueDate, tillDate) ? dueDate : tillDate;
|
final LocalDate toDate = DateUtils.isBefore(dueDate, tillDate) ? dueDate : tillDate;
|
||||||
chargeOnDueDate = chargeOnDueDate || loanCharge.getDueLocalDate().isBefore(loanCharge.getSubmittedOnDate());
|
chargeOnDueDate = chargeOnDueDate || loanCharge.getDueLocalDate().isBefore(loanCharge.getSubmittedOnDate());
|
||||||
return chargeOnDueDate ? loanCharge.isDueInPeriod(fromDate, toDate, isFirstPeriod)
|
return chargeOnDueDate ? loanCharge.isDueInPeriod(fromDate, toDate, isFirstPeriod)
|
||||||
: isInPeriod(loanCharge.getSubmittedOnDate(), fromDate, toDate, isFirstPeriod);
|
: isInPeriod(loanCharge.getSubmittedOnDate(), fromDate, toDate, isFirstPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanTransaction createOrMergeAccrualTransaction(@NotNull Loan loan, LoanTransaction transaction, LocalDate transactionDate,
|
private LoanTransaction createOrMergeAccrualTransaction(@NotNull final Loan loan, LoanTransaction transaction,
|
||||||
AccrualPeriodData accrualPeriod, List<LoanTransaction> accrualTransactions, Money interest, Money fee, Money penalty,
|
final LocalDate transactionDate, final AccrualPeriodData accrualPeriod, final List<LoanTransaction> accrualTransactions,
|
||||||
boolean adjustment) {
|
final Money interest, final Money fee, final Money penalty, final boolean adjustment) {
|
||||||
if (transaction == null) {
|
if (transaction == null) {
|
||||||
transaction = addAccrualTransaction(loan, transactionDate, accrualPeriod, interest, fee, penalty, adjustment);
|
transaction = addAccrualTransaction(loan, transactionDate, accrualPeriod, interest, fee, penalty, adjustment);
|
||||||
if (transaction != null) {
|
if (transaction != null) {
|
||||||
@@ -686,8 +694,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mergeAccrualTransaction(@NotNull LoanTransaction transaction, AccrualPeriodData accrualPeriod, Money interestPortion,
|
private void mergeAccrualTransaction(@NotNull final LoanTransaction transaction, final AccrualPeriodData accrualPeriod,
|
||||||
Money feePortion, Money penaltyPortion, boolean adjustment) {
|
Money interestPortion, Money feePortion, Money penaltyPortion, final boolean adjustment) {
|
||||||
interestPortion = MathUtil.negativeToZero(interestPortion);
|
interestPortion = MathUtil.negativeToZero(interestPortion);
|
||||||
feePortion = MathUtil.negativeToZero(feePortion);
|
feePortion = MathUtil.negativeToZero(feePortion);
|
||||||
penaltyPortion = MathUtil.negativeToZero(penaltyPortion);
|
penaltyPortion = MathUtil.negativeToZero(penaltyPortion);
|
||||||
@@ -700,44 +708,45 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
addTransactionMappings(transaction, accrualPeriod, adjustment);
|
addTransactionMappings(transaction, accrualPeriod, adjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTransactionMappings(@NotNull LoanTransaction transaction, AccrualPeriodData accrualPeriod, boolean adjustment) {
|
private void addTransactionMappings(@NotNull final LoanTransaction transaction, final AccrualPeriodData accrualPeriod,
|
||||||
|
final boolean adjustment) {
|
||||||
if (accrualPeriod == null) {
|
if (accrualPeriod == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Loan loan = transaction.getLoan();
|
final Loan loan = transaction.getLoan();
|
||||||
Integer installmentNumber = accrualPeriod.getInstallmentNumber();
|
final Integer installmentNumber = accrualPeriod.getInstallmentNumber();
|
||||||
LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(installmentNumber);
|
final LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(installmentNumber);
|
||||||
|
|
||||||
// add charges paid by mappings
|
// add charges paid by mappings
|
||||||
addPaidByMappings(transaction, installment, accrualPeriod, adjustment);
|
addPaidByMappings(transaction, installment, accrualPeriod, adjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPaidByMappings(@NotNull LoanTransaction transaction, LoanRepaymentScheduleInstallment installment,
|
private void addPaidByMappings(@NotNull final LoanTransaction transaction, final LoanRepaymentScheduleInstallment installment,
|
||||||
AccrualPeriodData accrualPeriod, boolean adjustment) {
|
final AccrualPeriodData accrualPeriod, final boolean adjustment) {
|
||||||
Loan loan = installment.getLoan();
|
final Loan loan = installment.getLoan();
|
||||||
MonetaryCurrency currency = loan.getCurrency();
|
final MonetaryCurrency currency = loan.getCurrency();
|
||||||
for (AccrualChargeData accrualCharge : accrualPeriod.getCharges()) {
|
for (AccrualChargeData accrualCharge : accrualPeriod.getCharges()) {
|
||||||
Money chargeAccruable = MathUtil.nullToZero(accrualCharge.getChargeAccruable(), currency);
|
final Money chargeAccruable = MathUtil.nullToZero(accrualCharge.getChargeAccruable(), currency);
|
||||||
Money chargePortion = MathUtil.minus(chargeAccruable, accrualCharge.getChargeAccrued());
|
Money chargePortion = MathUtil.minus(chargeAccruable, accrualCharge.getChargeAccrued());
|
||||||
chargePortion = MathUtil.negativeToZero(adjustment ? MathUtil.negate(chargePortion) : chargePortion);
|
chargePortion = MathUtil.negativeToZero(adjustment ? MathUtil.negate(chargePortion) : chargePortion);
|
||||||
if (MathUtil.isEmpty(chargePortion)) {
|
if (MathUtil.isEmpty(chargePortion)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
BigDecimal chargeAmount = MathUtil.toBigDecimal(chargePortion);
|
final BigDecimal chargeAmount = MathUtil.toBigDecimal(chargePortion);
|
||||||
LoanCharge loanCharge = loan.fetchLoanChargesById(accrualCharge.getLoanChargeId());
|
final LoanCharge loanCharge = loan.fetchLoanChargesById(accrualCharge.getLoanChargeId());
|
||||||
LoanChargePaidBy paidBy = new LoanChargePaidBy(transaction, loanCharge, chargeAmount, installment.getInstallmentNumber());
|
final LoanChargePaidBy paidBy = new LoanChargePaidBy(transaction, loanCharge, chargeAmount, installment.getInstallmentNumber());
|
||||||
loanCharge.getLoanChargePaidBySet().add(paidBy);
|
loanCharge.getLoanChargePaidBySet().add(paidBy);
|
||||||
transaction.getLoanChargesPaid().add(paidBy);
|
transaction.getLoanChargesPaid().add(paidBy);
|
||||||
Long installmentChargeId = accrualCharge.getLoanInstallmentChargeId();
|
final Long installmentChargeId = accrualCharge.getLoanInstallmentChargeId();
|
||||||
if (installmentChargeId != null) {
|
if (installmentChargeId != null) {
|
||||||
LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge(chargeAmount, loanCharge, installment);
|
final LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge(chargeAmount, loanCharge, installment);
|
||||||
loanCharge.getLoanInstallmentCharge().add(installmentCharge);
|
loanCharge.getLoanInstallmentCharge().add(installmentCharge);
|
||||||
installment.getInstallmentCharges().add(installmentCharge);
|
installment.getInstallmentCharges().add(installmentCharge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFullPeriod(@NotNull LocalDate tillDate, @NotNull LoanRepaymentScheduleInstallment installment) {
|
private boolean isFullPeriod(@NotNull final LocalDate tillDate, @NotNull final LoanRepaymentScheduleInstallment installment) {
|
||||||
return isAfterPeriod(tillDate, installment) || DateUtils.isEqual(tillDate, installment.getDueDate());
|
return isAfterPeriod(tillDate, installment) || DateUtils.isEqual(tillDate, installment.getDueDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,7 +756,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
if (loan.isChargedOff()) {
|
if (loan.isChargedOff()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ensureAccrualTransactionMappings(loan);
|
final boolean isChargeOnDueDate = isChargeOnDueDate();
|
||||||
|
ensureAccrualTransactionMappings(loan, accrualTransactions, isChargeOnDueDate);
|
||||||
LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
|
LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
|
||||||
LocalDate lastDueDate = lastInstallment.getDueDate();
|
LocalDate lastDueDate = lastInstallment.getDueDate();
|
||||||
if (isProgressiveAccrual(loan)) {
|
if (isProgressiveAccrual(loan)) {
|
||||||
@@ -800,7 +810,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
|
||||||
boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate();
|
boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate;
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment);
|
checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment);
|
||||||
}
|
}
|
||||||
@@ -979,8 +989,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
&& loan.getStatus().isClosedObligationsMet() && !loan.isNpa() && !loan.isChargedOff()) {
|
&& loan.getStatus().isClosedObligationsMet() && !loan.isNpa() && !loan.isChargedOff()) {
|
||||||
|
|
||||||
LocalDate closedDate = loan.getClosedOnDate();
|
LocalDate closedDate = loan.getClosedOnDate();
|
||||||
reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate, false);
|
reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate);
|
||||||
reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate, false);
|
reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate);
|
||||||
|
|
||||||
HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
|
HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
|
||||||
determineCumulativeIncomeFromInstallments(loan, cumulativeIncomeFromInstallments);
|
determineCumulativeIncomeFromInstallments(loan, cumulativeIncomeFromInstallments);
|
||||||
@@ -1128,38 +1138,42 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureAccrualTransactionMappings(Loan loan) {
|
private void ensureAccrualTransactionMappings(final Loan loan, final List<LoanTransaction> existingAccrualTransactions,
|
||||||
boolean chargeOnDueDate = isChargeOnDueDate();
|
final boolean chargeOnDueDate) {
|
||||||
List<LoanTransaction> transactions = retrieveListOfAccrualTransactions(loan);
|
final List<LoanTransaction> transactions = existingAccrualTransactions.stream()
|
||||||
|
.filter(t -> !MathUtil.isEmpty(t.getFeeChargesPortion()) || !MathUtil.isEmpty(t.getPenaltyChargesPortion())).toList();
|
||||||
|
|
||||||
|
if (transactions.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
||||||
for (LoanTransaction transaction : transactions) {
|
for (LoanTransaction transaction : transactions) {
|
||||||
if (!MathUtil.isEmpty(transaction.getFeeChargesPortion()) || !MathUtil.isEmpty(transaction.getPenaltyChargesPortion())) {
|
for (LoanChargePaidBy paidBy : transaction.getLoanChargesPaid()) {
|
||||||
int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
|
if (paidBy.getInstallmentNumber() == null) {
|
||||||
for (LoanChargePaidBy paidBy : transaction.getLoanChargesPaid()) {
|
final LoanCharge loanCharge = paidBy.getLoanCharge();
|
||||||
LoanCharge loanCharge = paidBy.getLoanCharge();
|
final LocalDate chargeDate = (chargeOnDueDate || loanCharge.isInstalmentFee()) ? transaction.getTransactionDate()
|
||||||
if (paidBy.getInstallmentNumber() == null) {
|
: loanCharge.getDueDate();
|
||||||
LocalDate chargeDate = (chargeOnDueDate || loanCharge.isInstalmentFee()) ? transaction.getTransactionDate()
|
final LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(
|
||||||
: loanCharge.getDueDate();
|
i -> isInPeriod(chargeDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
|
||||||
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(
|
paidBy.setInstallmentNumber(installment.getInstallmentNumber());
|
||||||
i -> isInPeriod(chargeDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
|
|
||||||
paidBy.setInstallmentNumber(installment.getInstallmentNumber());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanTransaction> retrieveListOfAccrualTransactions(Loan loan) {
|
private List<LoanTransaction> retrieveListOfAccrualTransactions(final Loan loan) {
|
||||||
return loan.getLoanTransactions().stream().filter(ACCRUAL_PREDICATE).sorted(LoanTransactionComparator.INSTANCE)
|
return loan.getLoanTransactions().stream().filter(ACCRUAL_PREDICATE).sorted(LoanTransactionComparator.INSTANCE)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanTransaction> retrieveListOfIncomePostingTransactions(Loan loan) {
|
private List<LoanTransaction> retrieveListOfIncomePostingTransactions(final Loan loan) {
|
||||||
return loan.getLoanTransactions().stream() //
|
return loan.getLoanTransactions().stream() //
|
||||||
.filter(transaction -> transaction.isNotReversed() && transaction.isIncomePosting()) //
|
.filter(transaction -> transaction.isNotReversed() && transaction.isIncomePosting()) //
|
||||||
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
|
.sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanTransaction getTransactionForDate(List<LoanTransaction> transactions, LocalDate effectiveDate) {
|
private LoanTransaction getTransactionForDate(final List<LoanTransaction> transactions, final LocalDate effectiveDate) {
|
||||||
for (LoanTransaction loanTransaction : transactions) {
|
for (LoanTransaction loanTransaction : transactions) {
|
||||||
if (DateUtils.isEqual(effectiveDate, loanTransaction.getTransactionDate())) {
|
if (DateUtils.isEqual(effectiveDate, loanTransaction.getTransactionDate())) {
|
||||||
return loanTransaction;
|
return loanTransaction;
|
||||||
@@ -1239,37 +1253,32 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean reverseTransactionsAfter(List<LoanTransaction> accrualTransactions, LocalDate effectiveDate, boolean addEvent) {
|
private void reverseTransactionsAfter(final List<LoanTransaction> accrualTransactions, final LocalDate effectiveDate,
|
||||||
boolean reversed = false;
|
final boolean addEvent) {
|
||||||
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
||||||
if (!accrualTransaction.isReversed() && DateUtils.isAfter(accrualTransaction.getTransactionDate(), effectiveDate)) {
|
if (!accrualTransaction.isReversed() && DateUtils.isAfter(accrualTransaction.getTransactionDate(), effectiveDate)) {
|
||||||
reverseAccrual(accrualTransaction, addEvent);
|
reverseAccrual(accrualTransaction, addEvent);
|
||||||
reversed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return reversed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean reverseTransactionsOnOrAfter(List<LoanTransaction> accrualTransactions, LocalDate date, boolean addEvent) {
|
private void reverseTransactionsOnOrAfter(final List<LoanTransaction> accrualTransactions, final LocalDate date) {
|
||||||
boolean reversed = false;
|
|
||||||
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
for (LoanTransaction accrualTransaction : accrualTransactions) {
|
||||||
if (!accrualTransaction.isReversed() && !DateUtils.isBefore(accrualTransaction.getTransactionDate(), date)) {
|
if (!accrualTransaction.isReversed() && !DateUtils.isBefore(accrualTransaction.getTransactionDate(), date)) {
|
||||||
reverseAccrual(accrualTransaction, addEvent);
|
reverseAccrual(accrualTransaction, false);
|
||||||
reversed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return reversed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reverseAccrual(LoanTransaction transaction, boolean addEvent) {
|
private void reverseAccrual(final LoanTransaction transaction, final boolean addEvent) {
|
||||||
transaction.reverse();
|
transaction.reverse();
|
||||||
if (addEvent) {
|
if (addEvent) {
|
||||||
LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction);
|
final LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction);
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
|
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate getFinalAccrualTransactionDate(Loan loan) {
|
private LocalDate getFinalAccrualTransactionDate(final Loan loan) {
|
||||||
return switch (loan.getStatus()) {
|
return switch (loan.getStatus()) {
|
||||||
case CLOSED_OBLIGATIONS_MET -> loan.getClosedOnDate();
|
case CLOSED_OBLIGATIONS_MET -> loan.getClosedOnDate();
|
||||||
case OVERPAID -> loan.getOverpaidOnDate();
|
case OVERPAID -> loan.getOverpaidOnDate();
|
||||||
|
|||||||
+2
@@ -43,6 +43,7 @@ import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
|
|||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails;
|
||||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@@ -59,6 +60,7 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
public LoanSummaryData withTransactionAmountsSummary(Long loanId, LoanSummaryData defaultSummaryData,
|
public LoanSummaryData withTransactionAmountsSummary(Long loanId, LoanSummaryData defaultSummaryData,
|
||||||
LoanScheduleData repaymentSchedule, Collection<LoanTransactionBalance> loanTransactionBalances) {
|
LoanScheduleData repaymentSchedule, Collection<LoanTransactionBalance> loanTransactionBalances) {
|
||||||
final Loan loan = loanRepository.findOneWithNotFoundDetection(loanId, true);
|
final Loan loan = loanRepository.findOneWithNotFoundDetection(loanId, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user