diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/collateralmanagement/domain/ClientCollateralManagement.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/collateralmanagement/domain/ClientCollateralManagement.java index b97602b5d..8a37529b6 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/collateralmanagement/domain/ClientCollateralManagement.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/collateralmanagement/domain/ClientCollateralManagement.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import lombok.Getter; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.portfolio.client.domain.Client; @@ -52,7 +53,8 @@ public class ClientCollateralManagement extends AbstractPersistableCustom @JoinColumn(name = "collateral_id", nullable = false) private CollateralManagementDomain collateral; - @OneToMany(cascade = CascadeType.ALL, mappedBy = "clientCollateralManagement", fetch = FetchType.EAGER) + @Getter + @OneToMany(cascade = CascadeType.ALL, mappedBy = "clientCollateralManagement", fetch = FetchType.LAZY) private Set loanCollateralManagementSet = new HashSet<>(); public ClientCollateralManagement() { @@ -129,9 +131,4 @@ public class ClientCollateralManagement extends AbstractPersistableCustom public CollateralManagementDomain getCollaterals() { return this.collateral; } - - public Set getLoanCollateralManagementSet() { - return this.loanCollateralManagementSet; - } - } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java index 30252632a..de7b1689c 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java @@ -43,7 +43,7 @@ public class AccrualPeriodData { private Money transactionAccrued; private final List charges = new ArrayList<>(); - public AccrualPeriodData addCharge(AccrualChargeData charge) { + public AccrualPeriodData addCharge(final AccrualChargeData charge) { charges.add(charge); return this; } @@ -56,36 +56,14 @@ public class AccrualPeriodData { 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() { 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() { 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() { return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccruable).reduce(null, MathUtil::plus); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java index c1bb2743c..b5f3aefe4 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanCharge.java @@ -43,6 +43,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; import lombok.Getter; +import lombok.Setter; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; @@ -76,7 +77,7 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "charge_time_enum", nullable = false) private Integer chargeTime; - @Column(name = "submitted_on_date", nullable = true) + @Column(name = "submitted_on_date") private LocalDate submittedOnDate; @Column(name = "due_for_collection_as_of_date") @@ -88,10 +89,10 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "charge_payment_mode_enum") private Integer chargePaymentMode; - @Column(name = "calculation_percentage", scale = 6, precision = 19, nullable = true) + @Column(name = "calculation_percentage", scale = 6, precision = 19) 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; @Column(name = "charge_amount_or_percentage", scale = 6, precision = 19, nullable = false) @@ -100,13 +101,14 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "amount", scale = 6, precision = 19, nullable = false) 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; - @Column(name = "amount_waived_derived", scale = 6, precision = 19, nullable = true) + @Setter + @Column(name = "amount_waived_derived", scale = 6, precision = 19) 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; @Column(name = "amount_outstanding_derived", scale = 6, precision = 19, nullable = false) @@ -121,13 +123,13 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "waived", nullable = 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; - @Column(name = "max_cap", scale = 6, precision = 19, nullable = true) + @Column(name = "max_cap", scale = 6, precision = 19) 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 = new HashSet<>(); @Column(name = "is_active", nullable = false) @@ -136,13 +138,13 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "external_id") 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; - @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; - @OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + @OneToMany(mappedBy = "loanCharge", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) private Set loanChargePaidBySet = new HashSet<>(); protected LoanCharge() { @@ -786,14 +788,6 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { this.loanInstallmentCharge.clear(); } - public void addLoanInstallmentCharges(final Collection installmentCharges) { - this.loanInstallmentCharge.addAll(installmentCharges); - } - - public boolean hasNoLoanInstallmentCharges() { - return this.loanInstallmentCharge.isEmpty(); - } - public Set installmentCharges() { return this.loanInstallmentCharge; } @@ -943,10 +937,6 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom { return this.dueDate != null; } - public void setAmountWaived(final BigDecimal amountWaived) { - this.amountWaived = amountWaived; - } - public void undoWaived() { this.waived = false; } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalculationDetails.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalculationDetails.java index 0c54b9ec3..53380a8d1 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalculationDetails.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalculationDetails.java @@ -25,6 +25,7 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; +import lombok.Getter; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; 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) private Integer restFrequencyType; + @Getter @Column(name = "rest_frequency_interval", nullable = false) private Integer restInterval; - @Column(name = "rest_frequency_nth_day_enum", nullable = true) + @Getter + @Column(name = "rest_frequency_nth_day_enum") private Integer restFrequencyNthDay; - @Column(name = "rest_frequency_weekday_enum", nullable = true) + @Getter + @Column(name = "rest_frequency_weekday_enum") private Integer restFrequencyWeekday; - @Column(name = "rest_frequency_on_day", nullable = true) + @Getter + @Column(name = "rest_frequency_on_day") private Integer restFrequencyOnDay; - @Column(name = "compounding_frequency_type_enum", nullable = true) + @Column(name = "compounding_frequency_type_enum") private Integer compoundingFrequencyType; - @Column(name = "compounding_frequency_interval", nullable = true) + @Getter + @Column(name = "compounding_frequency_interval") private Integer compoundingInterval; - @Column(name = "compounding_frequency_nth_day_enum", nullable = true) + @Getter + @Column(name = "compounding_frequency_nth_day_enum") private Integer compoundingFrequencyNthDay; - @Column(name = "compounding_frequency_weekday_enum", nullable = true) + @Getter + @Column(name = "compounding_frequency_weekday_enum") private Integer compoundingFrequencyWeekday; - @Column(name = "compounding_frequency_on_day", nullable = true) + @Getter + @Column(name = "compounding_frequency_on_day") private Integer compoundingFrequencyOnDay; @Column(name = "is_compounding_to_be_posted_as_transaction") @@ -91,6 +100,7 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom< @Column(name = "allow_compounding_on_eod") private Boolean allowCompoundingOnEod; + @Getter @Column(name = "pre_close_interest_calculation_strategy") @Enumerated(EnumType.ORDINAL) private LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy; @@ -161,42 +171,10 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom< return RecalculationFrequencyType.fromInt(this.restFrequencyType); } - public Integer getRestInterval() { - return this.restInterval; - } - public RecalculationFrequencyType getCompoundingFrequencyType() { 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() { return this.isCompoundingToBePostedAsTransaction != null && this.isCompoundingToBePostedAsTransaction; } @@ -209,7 +187,4 @@ public class LoanInterestRecalculationDetails extends AbstractPersistableCustom< return disallowInterestCalculationOnPastDue; } - public LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy() { - return preCloseInterestCalculationStrategy; - } } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index 191bc71b1..86b828bc1 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -159,10 +159,10 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa @Column(name = "is_re_aged", nullable = false) 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 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 = new HashSet<>(); @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); } - public void updateLoanRepaymentSchedule(final BigDecimal amountWaived) { - this.feeChargesWaived = this.feeChargesWaived.subtract(amountWaived); - } - public Money getPrincipalWrittenOff(final MonetaryCurrency currency) { return Money.of(currency, this.principalWrittenOff); } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index e41fc9f64..d65fb36ed 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -33,13 +33,11 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import lombok.Getter; +import lombok.Setter; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; import org.apache.fineract.infrastructure.core.domain.ExternalId; 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.Money; 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.loanschedule.domain.LoanScheduleType; -import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; /** @@ -71,7 +67,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom 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; @Column(name = "manually_adjusted_or_reversed", nullable = false) 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; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction") private Set 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 loanTransactionToRepaymentScheduleMappings = new HashSet<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "fromTransaction") private Set loanTransactionRelations = new HashSet<>(); + @Setter @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "loanTransaction") private LoanReAgeParameter loanReAgeParameter; @@ -209,17 +209,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom toMapData(final String currencyCode) { - final Map 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> loanChargesPaidData = new ArrayList<>(); - for (final LoanChargePaidBy chargePaidBy : this.loanChargesPaid) { - final Map 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 loanChargesPaid) { - this.loanChargesPaid = loanChargesPaid; - } - public boolean isRefund() { return LoanTransactionType.REFUND.equals(getTypeOf()) && isNotReversed(); } @@ -878,7 +770,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom retainMappings, + private void updateMappingDetail(final Collection retainMappings, final LoanTransactionToRepaymentScheduleMapping updatedMapping, boolean overwrite) { boolean isMappingUpdated = false; LoanRepaymentScheduleInstallment updatedInstallment = updatedMapping.getLoanRepaymentScheduleInstallment(); @@ -910,7 +802,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom updatedMappings) { @@ -921,7 +812,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom retainMappings, final LoanChargePaidBy updatedMapping) { + private void updateLoanChargePaid(final Collection retainMappings, final LoanChargePaidBy updatedMapping) { boolean updated = false; LoanCharge updatedCharge = updatedMapping.getLoanCharge(); Integer updatedInstallment = updatedMapping.getInstallmentNumber(); @@ -941,11 +832,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom getLoanTransactionToRepaymentScheduleMappings() { - return this.loanTransactionToRepaymentScheduleMappings; } public Boolean isAllowTypeTransactionAtTheTimeOfLastUndo() { @@ -961,10 +847,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom reprocessProgressiveLoanTransactionsTransactional( + final LocalDate disbursementDate, final LocalDate targetDate, final List loanTransactions, + final MonetaryCurrency currency, final List installments, final Set charges) { + return reprocessProgressiveLoanTransactions(disbursementDate, targetDate, loanTransactions, currency, installments, charges); + } + // only for progressive loans public Pair reprocessProgressiveLoanTransactions( LocalDate disbursementDate, LocalDate targetDate, List loanTransactions, MonetaryCurrency currency, diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java index 8e7febf15..dac63b237 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InternalProgressiveLoanApiResource.java @@ -93,13 +93,13 @@ public class InternalProgressiveLoanApiResource implements InitializingBean { .map(progressiveLoanInterestScheduleModelParserService::toJson).orElse(null); } - private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(Loan loan) { - List transactionsToReprocess = loan.retrieveListOfTransactionsForReprocessing(); - LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate(); - Pair changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor - .reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), businessDate, transactionsToReprocess, loan.getCurrency(), - loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); - ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight(); + private ProgressiveLoanInterestScheduleModel reprocessTransactionsAndGetModel(final Loan loan) { + final List transactionsToReprocess = loan.retrieveListOfTransactionsForReprocessing(); + final LocalDate businessDate = ThreadLocalContextUtil.getBusinessDate(); + final Pair changedTransactionDetailProgressiveLoanInterestScheduleModelPair = advancedPaymentScheduleTransactionProcessor + .reprocessProgressiveLoanTransactionsTransactional(loan.getDisbursementDate(), businessDate, transactionsToReprocess, + loan.getCurrency(), loan.getRepaymentScheduleInstallments(), loan.getActiveCharges()); + final ProgressiveLoanInterestScheduleModel model = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getRight(); final List replayedTransactions = changedTransactionDetailProgressiveLoanInterestScheduleModelPair.getLeft() .getTransactionChanges().stream().filter(change -> change.getOldTransaction() != null) .map(change -> change.getNewTransaction().getId()).filter(Objects::nonNull).toList(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralManagementReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralManagementReadPlatformServiceImpl.java index cd370fd64..4e69494cf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralManagementReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralManagementReadPlatformServiceImpl.java @@ -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.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException; +import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor +@Transactional(readOnly = true) public class ClientCollateralManagementReadPlatformServiceImpl implements ClientCollateralManagementReadPlatformService { private final PlatformSecurityContext context; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java index 6ef7855fe..11c3fba42 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java @@ -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.service.LoanUtilService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService { private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CumulativeLoanSummaryDataProvider.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CumulativeLoanSummaryDataProvider.java index b1d3d33fe..c58bae80f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CumulativeLoanSummaryDataProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CumulativeLoanSummaryDataProvider.java @@ -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.LoanSchedulePeriodData; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component public class CumulativeLoanSummaryDataProvider extends CommonLoanSummaryDataProvider { @@ -65,6 +66,7 @@ public class CumulativeLoanSummaryDataProvider extends CommonLoanSummaryDataProv } @Override + @Transactional(readOnly = true) public LoanSummaryData withTransactionAmountsSummary(Long loanId, LoanSummaryData defaultSummaryData, LoanScheduleData repaymentSchedule, Collection loanTransactionBalances) { Loan loan = null; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java index 492cb99a4..a6bcd8178 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java @@ -140,11 +140,12 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing */ @Override @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()) { 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 @Transactional public void addAccruals(@NotNull LocalDate tillDate) throws JobExecutionException { - List loans = loanRepositoryWrapper.findLoansForAddAccrual(AccountingRuleType.ACCRUAL_PERIODIC, tillDate, - !isChargeOnDueDate()); + final boolean chargeOnDueDate = isChargeOnDueDate(); + List loans = loanRepositoryWrapper.findLoansForAddAccrual(AccountingRuleType.ACCRUAL_PERIODIC, tillDate, !chargeOnDueDate); List> loanTasks = new ArrayList<>(); @@ -168,7 +169,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(outerLoan.getId()); try { 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()); } catch (Exception e) { log.error("Failed to add accrual for loan {}", loan.getId(), e); @@ -224,7 +225,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing return; } try { - addAccruals(loan, accruedTill, true, false, addJournal); + final boolean chargeOnDueDate = isChargeOnDueDate(); + addAccruals(loan, accruedTill, true, false, addJournal, chargeOnDueDate); } catch (Exception e) { String globalisationMessageCode = "error.msg.accrual.exception"; throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e); @@ -286,7 +288,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing @Override 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 - addAccruals(loan, loan.getLastLoanRepaymentScheduleInstallment().getDueDate(), false, true, addJournal); + final boolean chargeOnDueDate = isChargeOnDueDate(); + addAccruals(loan, loan.getLastLoanRepaymentScheduleInstallment().getDueDate(), false, true, addJournal, chargeOnDueDate); if (isProgressiveAccrual(loan)) { return; } @@ -325,28 +328,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing // 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() || !loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) { return; } - LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails(); + final LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails(); if (recalculationDetails != null && recalculationDetails.isCompoundingToBePostedAsTransaction()) { return; } - List existingAccruals = retrieveListOfAccrualTransactions(loan); - LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate(); + final List existingAccruals = retrieveListOfAccrualTransactions(loan); + final LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate(); reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal); - ensureAccrualTransactionMappings(loan); + ensureAccrualTransactionMappings(loan, existingAccruals, chargeOnDueDate); if (DateUtils.isAfter(tillDate, lastDueDate)) { tillDate = lastDueDate; } - boolean progressiveAccrual = isProgressiveAccrual(loan); - LocalDate accruedTill = loan.getAccruedTill(); - LocalDate businessDate = DateUtils.getBusinessLocalDate(); - LocalDate accrualDate = isFinal + final boolean progressiveAccrual = isProgressiveAccrual(loan); + final LocalDate accruedTill = loan.getAccruedTill(); + final LocalDate businessDate = DateUtils.getBusinessLocalDate(); + final LocalDate accrualDate = isFinal ? (progressiveAccrual ? (DateUtils.isBefore(lastDueDate, businessDate) ? lastDueDate : businessDate) : getFinalAccrualTransactionDate(loan)) : tillDate; @@ -358,51 +362,51 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } } - AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic, isFinal); - boolean mergeTransactions = isFinal || progressiveAccrual; - MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency(); + final AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic, isFinal, chargeOnDueDate); + final boolean mergeTransactions = isFinal || progressiveAccrual; + final MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency(); List accrualTransactions = new ArrayList<>(); Money totalInterestPortion = null; LoanTransaction mergeAccrualTransaction = null; LoanTransaction mergeAdjustTransaction = null; for (AccrualPeriodData period : accrualPeriods.getPeriods()) { - Money interestAccruable = MathUtil.nullToZero(period.getInterestAccruable(), currency); - Money interestPortion = MathUtil.minus(interestAccruable, period.getInterestAccrued()); - Money feeAccruable = MathUtil.nullToZero(period.getFeeAccruable(), currency); - Money feePortion = MathUtil.minus(feeAccruable, period.getFeeAccrued()); - Money penaltyAccruable = MathUtil.nullToZero(period.getPenaltyAccruable(), currency); - Money penaltyPortion = MathUtil.minus(penaltyAccruable, period.getPenaltyAccrued()); + final Money interestAccruable = MathUtil.nullToZero(period.getInterestAccruable(), currency); + final Money interestPortion = MathUtil.minus(interestAccruable, period.getInterestAccrued()); + final Money feeAccruable = MathUtil.nullToZero(period.getFeeAccruable(), currency); + final Money feePortion = MathUtil.minus(feeAccruable, period.getFeeAccrued()); + final Money penaltyAccruable = MathUtil.nullToZero(period.getPenaltyAccruable(), currency); + final Money penaltyPortion = MathUtil.minus(penaltyAccruable, period.getPenaltyAccrued()); if (MathUtil.isEmpty(interestPortion) && MathUtil.isEmpty(feePortion) && MathUtil.isEmpty(penaltyPortion)) { continue; } if (mergeTransactions) { totalInterestPortion = MathUtil.plus(totalInterestPortion, interestPortion); if (progressiveAccrual) { - Money feeAdjustmentPortion = MathUtil.negate(feePortion); - Money penaltyAdjustmentPortion = MathUtil.negate(penaltyPortion); + final Money feeAdjustmentPortion = MathUtil.negate(feePortion); + final Money penaltyAdjustmentPortion = MathUtil.negate(penaltyPortion); mergeAdjustTransaction = createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, period, accrualTransactions, null, feeAdjustmentPortion, penaltyAdjustmentPortion, true); } mergeAccrualTransaction = createOrMergeAccrualTransaction(loan, mergeAccrualTransaction, accrualDate, period, accrualTransactions, null, feePortion, penaltyPortion, false); } else { - LocalDate dueDate = period.getDueDate(); + final LocalDate dueDate = period.getDueDate(); if (!isFinal && DateUtils.isAfter(dueDate, tillDate) && DateUtils.isBefore(tillDate, accruedTill)) { continue; } - LocalDate periodAccrualDate = DateUtils.isBefore(dueDate, accrualDate) ? dueDate : accrualDate; - LoanTransaction accrualTransaction = addAccrualTransaction(loan, periodAccrualDate, period, interestPortion, feePortion, - penaltyPortion, false); + final LocalDate periodAccrualDate = DateUtils.isBefore(dueDate, accrualDate) ? dueDate : accrualDate; + final LoanTransaction accrualTransaction = addAccrualTransaction(loan, periodAccrualDate, period, interestPortion, + feePortion, penaltyPortion, false); if (accrualTransaction != null) { accrualTransactions.add(accrualTransaction); } } - LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber()); + final LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber()); installment.updateAccrualPortion(interestAccruable, feeAccruable, penaltyAccruable); } if (mergeTransactions && !MathUtil.isEmpty(totalInterestPortion)) { if (progressiveAccrual) { - Money interestAdjustmentPortion = MathUtil.negate(totalInterestPortion); + final Money interestAdjustmentPortion = MathUtil.negate(totalInterestPortion); createOrMergeAccrualTransaction(loan, mergeAdjustTransaction, accrualDate, null, accrualTransactions, interestAdjustmentPortion, null, null, true); } @@ -416,21 +420,22 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing if (!isFinal || progressiveAccrual) { loan.setAccruedTill(isFinal ? accrualDate : tillDate); } - List newTransactionDTOs = new ArrayList<>(); - for (LoanTransaction accrualTransaction : accrualTransactions) { - accrualTransaction = loanTransactionRepository.saveAndFlush(accrualTransaction); - if (addJournal) { - LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual() + + accrualTransactions = loanTransactionRepository.saveAll(accrualTransactions); + loanTransactionRepository.flush(); + + if (addJournal) { + final List newTransactionDTOs = new ArrayList<>(); + for (LoanTransaction accrualTransaction : accrualTransactions) { + final LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual() ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction) : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction); businessEventNotifierService.notifyPostBusinessEvent(businessEvent); - AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper.mapToLoanTransactionData(accrualTransaction, - currency.getCode()); + final AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper + .mapToLoanTransactionData(accrualTransaction, currency.getCode()); newTransactionDTOs.add(transactionDTO); } - } - if (addJournal) { - AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(), + final AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(), loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(), loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(), 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) { - boolean chargeOnDueDate = isChargeOnDueDate(); - LoanProductRelatedDetail productDetail = loan.getLoanProductRelatedDetail(); - MonetaryCurrency currency = productDetail.getCurrency(); - LoanScheduleGenerator scheduleGenerator = loanScheduleFactory.create(productDetail.getLoanScheduleType(), + private AccrualPeriodsData calculateAccrualAmounts(@NotNull final Loan loan, @NotNull final LocalDate tillDate, final boolean periodic, + final boolean isFinal, final boolean chargeOnDueDate) { + final LoanProductRelatedDetail productDetail = loan.getLoanProductRelatedDetail(); + final MonetaryCurrency currency = productDetail.getCurrency(); + final LoanScheduleGenerator scheduleGenerator = loanScheduleFactory.create(productDetail.getLoanScheduleType(), productDetail.getInterestMethod()); - int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments()); - LocalDate interestCalculationTillDate = loan.isProgressiveSchedule() + final int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments()); + final LocalDate interestCalculationTillDate = loan.isProgressiveSchedule() && loan.getLoanProductRelatedDetail().isInterestRecognitionOnDisbursementDate() ? tillDate.plusDays(1L) : tillDate; - List installments = isFinal ? loan.getRepaymentScheduleInstallments() - : getInstallmentsToAccrue(loan, interestCalculationTillDate, periodic); - AccrualPeriodsData accrualPeriods = AccrualPeriodsData.create(installments, firstInstallmentNumber, currency); + final List installments = isFinal ? loan.getRepaymentScheduleInstallments() + : getInstallmentsToAccrue(loan, interestCalculationTillDate, periodic, chargeOnDueDate); + final AccrualPeriodsData accrualPeriods = AccrualPeriodsData.create(installments, firstInstallmentNumber, currency); for (LoanRepaymentScheduleInstallment installment : installments) { addInterestAccrual(loan, interestCalculationTillDate, scheduleGenerator, installment, accrualPeriods); - addChargeAccrual(loan, tillDate, chargeOnDueDate, installment, accrualPeriods, isFinal); + addChargeAccrual(loan, tillDate, chargeOnDueDate, installment, accrualPeriods); } return accrualPeriods; } @NotNull - private List getInstallmentsToAccrue(@NotNull Loan loan, @NotNull LocalDate tillDate, - boolean periodic) { - LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate(); - int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments()); - return loan.getRepaymentScheduleInstallments(i -> !i.isDownPayment() && (!isChargeOnDueDate() - || (periodic ? !isBeforePeriod(tillDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber)) + private List getInstallmentsToAccrue(@NotNull final Loan loan, @NotNull final LocalDate tillDate, + final boolean periodic, final boolean chargeOnDueDate) { + final LocalDate organisationStartDate = this.configurationDomainService.retrieveOrganisationStartDate(); + final int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments()); + return loan.getRepaymentScheduleInstallments(i -> !i.isDownPayment() + && (!chargeOnDueDate || (periodic ? !isBeforePeriod(tillDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber)) : isFullPeriod(tillDate, i))) && !isAfterPeriod(organisationStartDate, i)); } - private void addInterestAccrual(@NotNull Loan loan, @NotNull LocalDate tillDate, LoanScheduleGenerator scheduleGenerator, - @NotNull LoanRepaymentScheduleInstallment installment, @NotNull AccrualPeriodsData accrualPeriods) { + private void addInterestAccrual(@NotNull final Loan loan, @NotNull final LocalDate tillDate, + final LoanScheduleGenerator scheduleGenerator, @NotNull final LoanRepaymentScheduleInstallment installment, + @NotNull final AccrualPeriodsData accrualPeriods) { if (installment.isAdditional() || installment.isReAged()) { return; } - AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber()); - MonetaryCurrency currency = accrualPeriods.getCurrency(); + final AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber()); + final MonetaryCurrency currency = accrualPeriods.getCurrency(); Money interest = null; - boolean isPastPeriod = isAfterPeriod(tillDate, installment); - boolean isInPeriod = isInPeriod(tillDate, installment, false); + final boolean isPastPeriod = isAfterPeriod(tillDate, installment); + final boolean isInPeriod = isInPeriod(tillDate, installment, false); if (isPastPeriod || loan.isClosed() || loan.isOverPaid()) { interest = installment.getInterestCharged(currency).minus(installment.getCreditedInterest()); } else { @@ -497,13 +503,14 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing unrecognizedWaived = MathUtil.min(unrecognizedWaived, MathUtil.minusToZero(installment.getInterestWaived(currency), transactionWaived), false); 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); } 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); - Money accrued = MathUtil.minusToZero(transactionAccrued, transactionWaived); + final Money accrued = MathUtil.minusToZero(transactionAccrued, transactionWaived); period.setInterestAccrued(accrued); } @@ -558,13 +565,13 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } } - private void addChargeAccrual(@NotNull Loan loan, @NotNull LocalDate tillDate, boolean chargeOnDueDate, - @NotNull LoanRepaymentScheduleInstallment installment, @NotNull AccrualPeriodsData accrualPeriods, boolean isFinal) { - AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber()); - LocalDate dueDate = installment.getDueDate(); - Collection loanCharges; - loanCharges = loan.getLoanCharges(lc -> !lc.isDueAtDisbursement() && (lc.isInstalmentFee() ? !DateUtils.isBefore(tillDate, dueDate) - : isChargeDue(lc, tillDate, chargeOnDueDate, installment, period.isFirstPeriod()))); + private void addChargeAccrual(@NotNull final Loan loan, @NotNull final LocalDate tillDate, final boolean chargeOnDueDate, + @NotNull final LoanRepaymentScheduleInstallment installment, @NotNull final AccrualPeriodsData accrualPeriods) { + final AccrualPeriodData period = accrualPeriods.getPeriodByInstallmentNumber(installment.getInstallmentNumber()); + final LocalDate dueDate = installment.getDueDate(); + final Collection loanCharges = loan + .getLoanCharges(lc -> !lc.isDueAtDisbursement() && (lc.isInstalmentFee() ? !DateUtils.isBefore(tillDate, dueDate) + : isChargeDue(lc, tillDate, chargeOnDueDate, installment, period.isFirstPeriod()))); for (LoanCharge loanCharge : loanCharges) { if (loanCharge.isActive()) { 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, - @NotNull LoanRepaymentScheduleInstallment installment, @NotNull AccrualPeriodsData accrualPeriods) { - MonetaryCurrency currency = accrualPeriods.getCurrency(); - Integer firstInstallmentNumber = accrualPeriods.getFirstInstallmentNumber(); - boolean installmentFee = loanCharge.isInstalmentFee(); - LoanRepaymentScheduleInstallment dueInstallment = (installmentFee || chargeOnDueDate) ? installment + private void addChargeAccrual(@NotNull final LoanCharge loanCharge, @NotNull final LocalDate tillDate, final boolean chargeOnDueDate, + @NotNull final LoanRepaymentScheduleInstallment installment, @NotNull final AccrualPeriodsData accrualPeriods) { + final MonetaryCurrency currency = accrualPeriods.getCurrency(); + final Integer firstInstallmentNumber = accrualPeriods.getFirstInstallmentNumber(); + final boolean installmentFee = loanCharge.isInstalmentFee(); + final LoanRepaymentScheduleInstallment dueInstallment = (installmentFee || chargeOnDueDate) ? installment : loanCharge.getLoan().getRepaymentScheduleInstallment( i -> isInPeriod(loanCharge.getDueDate(), i, i.getInstallmentNumber().equals(firstInstallmentNumber))); - AccrualPeriodData duePeriod = accrualPeriods.getPeriodByInstallmentNumber(dueInstallment.getInstallmentNumber()); - boolean isFullPeriod = isFullPeriod(tillDate, dueInstallment); + final AccrualPeriodData duePeriod = accrualPeriods.getPeriodByInstallmentNumber(dueInstallment.getInstallmentNumber()); + final boolean isFullPeriod = isFullPeriod(tillDate, dueInstallment); Money chargeAmount; Money waived; Collection paidBys; Long installmentChargeId = null; if (installmentFee) { - LoanInstallmentCharge installmentCharge = loanCharge.getInstallmentLoanCharge(dueInstallment.getInstallmentNumber()); + final LoanInstallmentCharge installmentCharge = loanCharge.getInstallmentLoanCharge(dueInstallment.getInstallmentNumber()); if (installmentCharge == null) { return; } @@ -603,13 +610,13 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing waived = isFullPeriod ? loanCharge.getAmountWaived(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); chargeData.setChargeAccruable(MathUtil.minusToZero(chargeAmount, waived)); - Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency); - Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived); - Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency); + final Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency); + final Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived); + final Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency); chargeData.setTransactionAccrued(transactionAccrued); chargeData.setChargeAccrued(MathUtil.minusToZero(transactionAccrued, transactionWaived)); @@ -617,42 +624,43 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } @NotNull - private BigDecimal calcChargeWaivedAmount(@NotNull Collection loanChargePaidBy, @NotNull LocalDate tillDate) { + private BigDecimal calcChargeWaivedAmount(@NotNull final Collection loanChargePaidBy, + @NotNull final LocalDate tillDate) { return loanChargePaidBy.stream().filter(pb -> { - LoanTransaction t = pb.getLoanTransaction(); + final LoanTransaction t = pb.getLoanTransaction(); return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate); }).map(LoanChargePaidBy::getAmount).reduce(BigDecimal.ZERO, MathUtil::add); } @NotNull - private BigDecimal calcChargeUnrecognizedWaivedAmount(@NotNull Collection loanChargePaidBy, - @NotNull LocalDate tillDate) { + private BigDecimal calcChargeUnrecognizedWaivedAmount(@NotNull final Collection loanChargePaidBy, + @NotNull final LocalDate tillDate) { return loanChargePaidBy.stream().filter(pb -> { - LoanTransaction t = pb.getLoanTransaction(); + final LoanTransaction t = pb.getLoanTransaction(); return !t.isReversed() && t.isWaiveCharge() && !DateUtils.isAfter(t.getTransactionDate(), tillDate); }).map(pb -> pb.getLoanTransaction().getUnrecognizedIncomePortion()).reduce(BigDecimal.ZERO, MathUtil::add); } @NotNull - private BigDecimal calcChargeAccruedAmount(@NotNull Collection loanChargePaidBy) { + private BigDecimal calcChargeAccruedAmount(@NotNull final Collection loanChargePaidBy) { return loanChargePaidBy.stream().filter(pb -> ACCRUAL_PREDICATE.test(pb.getLoanTransaction())) .map(pb -> pb.getLoanTransaction().isAccrual() ? pb.getAmount() : MathUtil.negate(pb.getAmount())) .reduce(BigDecimal.ZERO, MathUtil::add); } - private boolean isChargeDue(@NotNull LoanCharge loanCharge, @NotNull LocalDate tillDate, boolean chargeOnDueDate, - LoanRepaymentScheduleInstallment installment, boolean isFirstPeriod) { - LocalDate fromDate = installment.getFromDate(); - LocalDate dueDate = installment.getDueDate(); - LocalDate toDate = DateUtils.isBefore(dueDate, tillDate) ? dueDate : tillDate; + private boolean isChargeDue(@NotNull final LoanCharge loanCharge, @NotNull final LocalDate tillDate, boolean chargeOnDueDate, + final LoanRepaymentScheduleInstallment installment, final boolean isFirstPeriod) { + final LocalDate fromDate = installment.getFromDate(); + final LocalDate dueDate = installment.getDueDate(); + final LocalDate toDate = DateUtils.isBefore(dueDate, tillDate) ? dueDate : tillDate; chargeOnDueDate = chargeOnDueDate || loanCharge.getDueLocalDate().isBefore(loanCharge.getSubmittedOnDate()); return chargeOnDueDate ? loanCharge.isDueInPeriod(fromDate, toDate, isFirstPeriod) : isInPeriod(loanCharge.getSubmittedOnDate(), fromDate, toDate, isFirstPeriod); } - private LoanTransaction createOrMergeAccrualTransaction(@NotNull Loan loan, LoanTransaction transaction, LocalDate transactionDate, - AccrualPeriodData accrualPeriod, List accrualTransactions, Money interest, Money fee, Money penalty, - boolean adjustment) { + private LoanTransaction createOrMergeAccrualTransaction(@NotNull final Loan loan, LoanTransaction transaction, + final LocalDate transactionDate, final AccrualPeriodData accrualPeriod, final List accrualTransactions, + final Money interest, final Money fee, final Money penalty, final boolean adjustment) { if (transaction == null) { transaction = addAccrualTransaction(loan, transactionDate, accrualPeriod, interest, fee, penalty, adjustment); if (transaction != null) { @@ -686,8 +694,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing return transaction; } - private void mergeAccrualTransaction(@NotNull LoanTransaction transaction, AccrualPeriodData accrualPeriod, Money interestPortion, - Money feePortion, Money penaltyPortion, boolean adjustment) { + private void mergeAccrualTransaction(@NotNull final LoanTransaction transaction, final AccrualPeriodData accrualPeriod, + Money interestPortion, Money feePortion, Money penaltyPortion, final boolean adjustment) { interestPortion = MathUtil.negativeToZero(interestPortion); feePortion = MathUtil.negativeToZero(feePortion); penaltyPortion = MathUtil.negativeToZero(penaltyPortion); @@ -700,44 +708,45 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing 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) { return; } - Loan loan = transaction.getLoan(); - Integer installmentNumber = accrualPeriod.getInstallmentNumber(); - LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(installmentNumber); + final Loan loan = transaction.getLoan(); + final Integer installmentNumber = accrualPeriod.getInstallmentNumber(); + final LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(installmentNumber); // add charges paid by mappings addPaidByMappings(transaction, installment, accrualPeriod, adjustment); } - private void addPaidByMappings(@NotNull LoanTransaction transaction, LoanRepaymentScheduleInstallment installment, - AccrualPeriodData accrualPeriod, boolean adjustment) { - Loan loan = installment.getLoan(); - MonetaryCurrency currency = loan.getCurrency(); + private void addPaidByMappings(@NotNull final LoanTransaction transaction, final LoanRepaymentScheduleInstallment installment, + final AccrualPeriodData accrualPeriod, final boolean adjustment) { + final Loan loan = installment.getLoan(); + final MonetaryCurrency currency = loan.getCurrency(); 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()); chargePortion = MathUtil.negativeToZero(adjustment ? MathUtil.negate(chargePortion) : chargePortion); if (MathUtil.isEmpty(chargePortion)) { continue; } - BigDecimal chargeAmount = MathUtil.toBigDecimal(chargePortion); - LoanCharge loanCharge = loan.fetchLoanChargesById(accrualCharge.getLoanChargeId()); - LoanChargePaidBy paidBy = new LoanChargePaidBy(transaction, loanCharge, chargeAmount, installment.getInstallmentNumber()); + final BigDecimal chargeAmount = MathUtil.toBigDecimal(chargePortion); + final LoanCharge loanCharge = loan.fetchLoanChargesById(accrualCharge.getLoanChargeId()); + final LoanChargePaidBy paidBy = new LoanChargePaidBy(transaction, loanCharge, chargeAmount, installment.getInstallmentNumber()); loanCharge.getLoanChargePaidBySet().add(paidBy); transaction.getLoanChargesPaid().add(paidBy); - Long installmentChargeId = accrualCharge.getLoanInstallmentChargeId(); + final Long installmentChargeId = accrualCharge.getLoanInstallmentChargeId(); if (installmentChargeId != null) { - LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge(chargeAmount, loanCharge, installment); + final LoanInstallmentCharge installmentCharge = new LoanInstallmentCharge(chargeAmount, loanCharge, installment); loanCharge.getLoanInstallmentCharge().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()); } @@ -747,7 +756,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing if (loan.isChargedOff()) { return; } - ensureAccrualTransactionMappings(loan); + final boolean isChargeOnDueDate = isChargeOnDueDate(); + ensureAccrualTransactionMappings(loan, accrualTransactions, isChargeOnDueDate); LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment(); LocalDate lastDueDate = lastInstallment.getDueDate(); if (isProgressiveAccrual(loan)) { @@ -800,7 +810,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } } else { List installments = loan.getRepaymentScheduleInstallments(); - boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate(); + boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate; for (LoanRepaymentScheduleInstallment installment : installments) { checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment); } @@ -979,8 +989,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing && loan.getStatus().isClosedObligationsMet() && !loan.isNpa() && !loan.isChargedOff()) { LocalDate closedDate = loan.getClosedOnDate(); - reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate, false); - reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate, false); + reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate); + reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate); HashMap cumulativeIncomeFromInstallments = new HashMap<>(); determineCumulativeIncomeFromInstallments(loan, cumulativeIncomeFromInstallments); @@ -1128,38 +1138,42 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } - private void ensureAccrualTransactionMappings(Loan loan) { - boolean chargeOnDueDate = isChargeOnDueDate(); - List transactions = retrieveListOfAccrualTransactions(loan); + private void ensureAccrualTransactionMappings(final Loan loan, final List existingAccrualTransactions, + final boolean chargeOnDueDate) { + final List 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) { - if (!MathUtil.isEmpty(transaction.getFeeChargesPortion()) || !MathUtil.isEmpty(transaction.getPenaltyChargesPortion())) { - int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments()); - for (LoanChargePaidBy paidBy : transaction.getLoanChargesPaid()) { - LoanCharge loanCharge = paidBy.getLoanCharge(); - if (paidBy.getInstallmentNumber() == null) { - LocalDate chargeDate = (chargeOnDueDate || loanCharge.isInstalmentFee()) ? transaction.getTransactionDate() - : loanCharge.getDueDate(); - LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment( - i -> isInPeriod(chargeDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber))); - paidBy.setInstallmentNumber(installment.getInstallmentNumber()); - } + for (LoanChargePaidBy paidBy : transaction.getLoanChargesPaid()) { + if (paidBy.getInstallmentNumber() == null) { + final LoanCharge loanCharge = paidBy.getLoanCharge(); + final LocalDate chargeDate = (chargeOnDueDate || loanCharge.isInstalmentFee()) ? transaction.getTransactionDate() + : loanCharge.getDueDate(); + final LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment( + i -> isInPeriod(chargeDate, i, i.getInstallmentNumber().equals(firstInstallmentNumber))); + paidBy.setInstallmentNumber(installment.getInstallmentNumber()); } } } } - private List retrieveListOfAccrualTransactions(Loan loan) { + private List retrieveListOfAccrualTransactions(final Loan loan) { return loan.getLoanTransactions().stream().filter(ACCRUAL_PREDICATE).sorted(LoanTransactionComparator.INSTANCE) .collect(Collectors.toList()); } - private List retrieveListOfIncomePostingTransactions(Loan loan) { + private List retrieveListOfIncomePostingTransactions(final Loan loan) { return loan.getLoanTransactions().stream() // .filter(transaction -> transaction.isNotReversed() && transaction.isIncomePosting()) // .sorted(LoanTransactionComparator.INSTANCE).collect(Collectors.toList()); } - private LoanTransaction getTransactionForDate(List transactions, LocalDate effectiveDate) { + private LoanTransaction getTransactionForDate(final List transactions, final LocalDate effectiveDate) { for (LoanTransaction loanTransaction : transactions) { if (DateUtils.isEqual(effectiveDate, loanTransaction.getTransactionDate())) { return loanTransaction; @@ -1239,37 +1253,32 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } } - private boolean reverseTransactionsAfter(List accrualTransactions, LocalDate effectiveDate, boolean addEvent) { - boolean reversed = false; + private void reverseTransactionsAfter(final List accrualTransactions, final LocalDate effectiveDate, + final boolean addEvent) { for (LoanTransaction accrualTransaction : accrualTransactions) { if (!accrualTransaction.isReversed() && DateUtils.isAfter(accrualTransaction.getTransactionDate(), effectiveDate)) { reverseAccrual(accrualTransaction, addEvent); - reversed = true; } } - return reversed; } - private boolean reverseTransactionsOnOrAfter(List accrualTransactions, LocalDate date, boolean addEvent) { - boolean reversed = false; + private void reverseTransactionsOnOrAfter(final List accrualTransactions, final LocalDate date) { for (LoanTransaction accrualTransaction : accrualTransactions) { if (!accrualTransaction.isReversed() && !DateUtils.isBefore(accrualTransaction.getTransactionDate(), date)) { - reverseAccrual(accrualTransaction, addEvent); - reversed = true; + reverseAccrual(accrualTransaction, false); } } - return reversed; } - private void reverseAccrual(LoanTransaction transaction, boolean addEvent) { + private void reverseAccrual(final LoanTransaction transaction, final boolean addEvent) { transaction.reverse(); if (addEvent) { - LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction); + final LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction); businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data)); } } - private LocalDate getFinalAccrualTransactionDate(Loan loan) { + private LocalDate getFinalAccrualTransactionDate(final Loan loan) { return switch (loan.getStatus()) { case CLOSED_OBLIGATIONS_MET -> loan.getClosedOnDate(); case OVERPAID -> loan.getOverpaidOnDate(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java index bbae06809..0d3092ab7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanSummaryDataProvider.java @@ -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.ProgressiveLoanInterestScheduleModel; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component @AllArgsConstructor @@ -59,6 +60,7 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro } @Override + @Transactional(readOnly = true) public LoanSummaryData withTransactionAmountsSummary(Long loanId, LoanSummaryData defaultSummaryData, LoanScheduleData repaymentSchedule, Collection loanTransactionBalances) { final Loan loan = loanRepository.findOneWithNotFoundDetection(loanId, true);