From 6172b62456e543736189ee19f331c127b37be231 Mon Sep 17 00:00:00 2001 From: Peter Bagrij Date: Mon, 5 Jun 2023 13:00:21 +0200 Subject: [PATCH] FINERACT-1926 AssetOwnerTransferDetails added --- .../LoanAccountOwnerTransferBusinessStep.java | 28 ++++- .../investor/data/ExternalTransferData.java | 1 + .../data/ExternalTransferDataDetails.java | 34 +++++ .../domain/ExternalAssetOwnerTransfer.java | 10 +- .../ExternalAssetOwnerTransferDetails.java | 61 +++++++++ .../ExternalAssetOwnersTransferMapper.java | 6 + .../ExternalAssetOwnersWriteServiceImpl.java | 3 +- .../investor/module-changelog-master.xml | 1 + ..._external_asset_owner_transfer_details.xml | 118 ++++++++++++++++++ ...nAccountOwnerTransferBusinessStepTest.java | 2 + .../portfolio/loanaccount/domain/Loan.java | 12 +- .../loanaccount/domain/LoanTransaction.java | 18 +-- .../src/main/resources/jpa/persistence.xml | 1 + ...nitiateExternalAssetOwnerTransferTest.java | 33 +++++ 14 files changed, 301 insertions(+), 27 deletions(-) create mode 100644 fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferDataDetails.java create mode 100644 fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferDetails.java create mode 100644 fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0007_add_external_asset_owner_transfer_details.xml diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java index 5e5ac6273..ff8a7aeb2 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java @@ -21,6 +21,7 @@ package org.apache.fineract.investor.cob.loan; import java.math.BigDecimal; import java.time.LocalDate; import java.util.List; +import java.util.Objects; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.cob.loan.LoanCOBBusinessStep; @@ -28,6 +29,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.investor.data.ExternalTransferStatus; import org.apache.fineract.investor.data.ExternalTransferSubStatus; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer; +import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferDetails; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; @@ -89,7 +91,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep final ExternalAssetOwnerTransfer buybackExternalAssetOwnerTransfer) { ExternalAssetOwnerTransfer activeExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository .findOne((root, query, criteriaBuilder) -> criteriaBuilder.and(criteriaBuilder.equal(root.get("loanId"), loan.getId()), - criteriaBuilder.equal(root.get("ownerId"), buybackExternalAssetOwnerTransfer.getOwnerId()), + criteriaBuilder.equal(root.get("owner"), buybackExternalAssetOwnerTransfer.getOwner()), criteriaBuilder.equal(root.get("status"), ExternalTransferStatus.ACTIVE), criteriaBuilder.equal(root.get("effectiveDateTo"), FUTURE_DATE_9999_12_31))) .orElseThrow(); @@ -102,11 +104,12 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep ExternalAssetOwnerTransfer buybackExternalAssetOwnerTransfer, ExternalAssetOwnerTransfer activeExternalAssetOwnerTransfer) { activeExternalAssetOwnerTransfer.setEffectiveDateTo(settlementDate); buybackExternalAssetOwnerTransfer.setEffectiveDateTo(buybackExternalAssetOwnerTransfer.getEffectiveDateFrom()); + buybackExternalAssetOwnerTransfer + .setExternalAssetOwnerTransferDetails(createAssetOwnerTransferDetails(loan, buybackExternalAssetOwnerTransfer)); externalAssetOwnerTransferRepository.save(activeExternalAssetOwnerTransfer); buybackExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository.save(buybackExternalAssetOwnerTransfer); externalAssetOwnerTransferLoanMappingRepository.deleteByLoanIdAndOwnerTransfer(loan.getId(), activeExternalAssetOwnerTransfer); // TODO: create asset ownership accounting entries - // TODO: create asset ownership transaction entries return buybackExternalAssetOwnerTransfer; } @@ -116,8 +119,9 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep if (isTransferable(loan)) { newExternalAssetOwnerTransfer = createActiveEntry(settlementDate, externalAssetOwnerTransfer); createActiveMapping(loan.getId(), newExternalAssetOwnerTransfer); + newExternalAssetOwnerTransfer + .setExternalAssetOwnerTransferDetails(createAssetOwnerTransferDetails(loan, newExternalAssetOwnerTransfer)); // TODO: create asset ownership accounting entries - // TODO: create asset ownership transaction entries } else { ExternalTransferSubStatus subStatus = ExternalTransferSubStatus.BALANCE_ZERO; if (loan.getTotalOverpaid().compareTo(BigDecimal.ZERO) > 0) { @@ -129,6 +133,23 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep return newExternalAssetOwnerTransfer; } + private ExternalAssetOwnerTransferDetails createAssetOwnerTransferDetails(Loan loan, + ExternalAssetOwnerTransfer externalAssetOwnerTransfer) { + ExternalAssetOwnerTransferDetails details = new ExternalAssetOwnerTransferDetails(); + details.setExternalAssetOwnerTransfer(externalAssetOwnerTransfer); + details.setTotalOutstanding(Objects.requireNonNullElse(loan.getLoanSummary().getTotalOutstanding(), BigDecimal.ZERO)); + details.setTotalPrincipalOutstanding( + Objects.requireNonNullElse(loan.getLoanSummary().getTotalPrincipalOutstanding(), BigDecimal.ZERO)); + details.setTotalInterestOutstanding( + Objects.requireNonNullElse(loan.getLoanSummary().getTotalInterestOutstanding(), BigDecimal.ZERO)); + details.setTotalFeeChargesOutstanding( + Objects.requireNonNullElse(loan.getLoanSummary().getTotalFeeChargesOutstanding(), BigDecimal.ZERO)); + details.setTotalPenaltyChargesOutstanding( + Objects.requireNonNullElse(loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), BigDecimal.ZERO)); + details.setTotalOverpaid(Objects.requireNonNullElse(loan.getTotalOverpaid(), BigDecimal.ZERO)); + return details; + } + private void createActiveMapping(Long loanId, ExternalAssetOwnerTransfer externalAssetOwnerTransfer) { ExternalAssetOwnerTransferLoanMapping externalAssetOwnerTransferLoanMapping = new ExternalAssetOwnerTransferLoanMapping(); externalAssetOwnerTransferLoanMapping.setLoanId(loanId); @@ -157,7 +178,6 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep final ExternalTransferSubStatus subStatus, final LocalDate effectiveDateFrom, final LocalDate effectiveDateTo) { ExternalAssetOwnerTransfer newExternalAssetOwnerTransfer = new ExternalAssetOwnerTransfer(); newExternalAssetOwnerTransfer.setOwner(externalAssetOwnerTransfer.getOwner()); - newExternalAssetOwnerTransfer.setOwnerId(externalAssetOwnerTransfer.getOwnerId()); newExternalAssetOwnerTransfer.setExternalId(externalAssetOwnerTransfer.getExternalId()); newExternalAssetOwnerTransfer.setStatus(status); newExternalAssetOwnerTransfer.setSubStatus(subStatus); diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java index ca1d1d878..9cbb0a539 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferData.java @@ -27,6 +27,7 @@ public class ExternalTransferData { private Long transferId; private ExternalTransferOwnerData owner; private ExternalTransferLoanMappingData loan; + private ExternalTransferDataDetails details; private String transferExternalId; private String purchasePriceRatio; private LocalDate settlementDate; diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferDataDetails.java b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferDataDetails.java new file mode 100644 index 000000000..4dd5d551c --- /dev/null +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferDataDetails.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.investor.data; + +import java.math.BigDecimal; +import lombok.Data; + +@Data +public class ExternalTransferDataDetails { + + private Long detailsId; + private BigDecimal totalOutstanding; + private BigDecimal totalPrincipalOutstanding; + private BigDecimal totalInterestOutstanding; + private BigDecimal totalFeeChargesOutstanding; + private BigDecimal totalPenaltyChargesOutstanding; + private BigDecimal totalOverpaid; +} diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java index d9735a1d3..ab9f7f671 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransfer.java @@ -18,12 +18,14 @@ */ package org.apache.fineract.investor.domain; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import java.time.LocalDate; import lombok.Getter; @@ -41,13 +43,13 @@ import org.apache.fineract.investor.data.ExternalTransferSubStatus; @Entity public class ExternalAssetOwnerTransfer extends AbstractAuditableWithUTCDateTimeCustom { - @Column(name = "owner_id", nullable = false) - private Long ownerId; - @ManyToOne - @JoinColumn(name = "owner_id", insertable = false, updatable = false, nullable = false) + @JoinColumn(name = "owner_id", nullable = false) private ExternalAssetOwner owner; + @OneToOne(mappedBy = "externalAssetOwnerTransfer", cascade = CascadeType.ALL) + private ExternalAssetOwnerTransferDetails externalAssetOwnerTransferDetails; + @Column(name = "external_id", length = 100, nullable = false) private ExternalId externalId; diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferDetails.java b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferDetails.java new file mode 100644 index 000000000..badfa672d --- /dev/null +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/domain/ExternalAssetOwnerTransferDetails.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.investor.domain; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; + +@Getter +@Setter +@Table(name = "m_external_asset_owner_transfer_details") +@NoArgsConstructor +@Entity +public class ExternalAssetOwnerTransferDetails extends AbstractAuditableWithUTCDateTimeCustom { + + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "asset_owner_transfer_id", referencedColumnName = "id") + private ExternalAssetOwnerTransfer externalAssetOwnerTransfer; + + @Column(name = "total_outstanding_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal totalOutstanding; + + @Column(name = "principal_outstanding_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal totalPrincipalOutstanding; + + @Column(name = "interest_outstanding_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal totalInterestOutstanding; + + @Column(name = "fee_charges_outstanding_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal totalFeeChargesOutstanding; + + @Column(name = "penalty_charges_outstanding_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal totalPenaltyChargesOutstanding; + + @Column(name = "total_overpaid_derived", scale = 6, precision = 19, nullable = false) + private BigDecimal totalOverpaid; +} diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersTransferMapper.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersTransferMapper.java index a29ba4167..094a60459 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersTransferMapper.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersTransferMapper.java @@ -20,9 +20,11 @@ package org.apache.fineract.investor.service; import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig; import org.apache.fineract.investor.data.ExternalTransferData; +import org.apache.fineract.investor.data.ExternalTransferDataDetails; import org.apache.fineract.investor.data.ExternalTransferOwnerData; import org.apache.fineract.investor.domain.ExternalAssetOwner; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer; +import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferDetails; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -39,8 +41,12 @@ public interface ExternalAssetOwnersTransferMapper { @Mapping(target = "purchasePriceRatio", source = "purchasePriceRatio") @Mapping(target = "settlementDate", source = "settlementDate") @Mapping(target = "status", source = "status") + @Mapping(target = "details", source = "externalAssetOwnerTransferDetails") ExternalTransferData mapTransfer(ExternalAssetOwnerTransfer source); ExternalTransferOwnerData mapOwner(ExternalAssetOwner source); + @Mapping(target = "detailsId", source = "id") + ExternalTransferDataDetails mapDetails(ExternalAssetOwnerTransferDetails details); + } diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java index 25df924a7..4b1920f89 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java @@ -144,7 +144,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW ExternalAssetOwnerTransfer externalAssetOwnerTransfer = new ExternalAssetOwnerTransfer(); externalAssetOwnerTransfer.setExternalId(externalId); - externalAssetOwnerTransfer.setOwnerId(effectiveTransfer.getOwnerId()); + externalAssetOwnerTransfer.setOwner(effectiveTransfer.getOwner()); externalAssetOwnerTransfer.setStatus(ExternalTransferStatus.BUYBACK); externalAssetOwnerTransfer.setLoanId(effectiveTransfer.getLoanId()); externalAssetOwnerTransfer.setExternalLoanId(effectiveTransfer.getExternalLoanId()); @@ -218,7 +218,6 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW final JsonElement json = fromApiJsonHelper.parse(apiRequestBodyAsJson); ExternalAssetOwner owner = getOwner(json); - externalAssetOwnerTransfer.setOwnerId(owner.getId()); externalAssetOwnerTransfer.setOwner(owner); externalAssetOwnerTransfer.setExternalId(getTransferExternalIdFromJson(json)); externalAssetOwnerTransfer.setStatus(ExternalTransferStatus.PENDING); diff --git a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml index 6ce35e811..e8df64dc0 100644 --- a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml +++ b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/module-changelog-master.xml @@ -28,4 +28,5 @@ + diff --git a/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0007_add_external_asset_owner_transfer_details.xml b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0007_add_external_asset_owner_transfer_details.xml new file mode 100644 index 000000000..516b35315 --- /dev/null +++ b/fineract-investor/src/main/resources/db/changelog/tenant/module/investor/parts/0007_add_external_asset_owner_transfer_details.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java index 9492e630b..be1aa36d6 100644 --- a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java +++ b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java @@ -167,6 +167,8 @@ public class LoanAccountOwnerTransferBusinessStepTest { // given final Loan loanForProcessing = Mockito.mock(Loan.class); when(loanForProcessing.getId()).thenReturn(1L); + LoanSummary loanSummary = Mockito.mock(LoanSummary.class); + when(loanForProcessing.getLoanSummary()).thenReturn(loanSummary); ExternalAssetOwnerTransfer firstResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class); ExternalAssetOwnerTransfer secondResponseItem = Mockito.mock(ExternalAssetOwnerTransfer.class); when(firstResponseItem.getStatus()).thenReturn(ExternalTransferStatus.BUYBACK); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index 94ba578e0..ee278d5c1 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -3213,7 +3213,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { } if (loanTransaction.isRecoveryRepayment() - && loanTransaction.getAmount(loanCurrency()).getAmount().compareTo(getSummary().getTotalWrittenOff()) > 0) { + && loanTransaction.getAmount(loanCurrency()).getAmount().compareTo(getLoanSummary().getTotalWrittenOff()) > 0) { final String errorMessage = "The transaction amount cannot greater than the remaining written off amount."; throw new InvalidLoanStateTransitionException("transaction", "cannot.be.greater.than.total.written.off", errorMessage); } @@ -3884,13 +3884,13 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { addLoanTransaction(mapEntry.getValue()); } updateLoanSummaryDerivedFields(); - LoanTransaction loanTransaction = findlatestTransaction(); + LoanTransaction loanTransaction = getLatestTransaction(); doPostLoanTransactionChecks(loanTransaction.getTransactionDate(), loanLifecycleStateMachine); } return changedTransactionDetail; } - private LoanTransaction findlatestTransaction() { + public LoanTransaction getLatestTransaction() { LoanTransaction transaction = null; for (LoanTransaction loanTransaction : this.loanTransactions) { if (!loanTransaction.isReversed() @@ -4472,10 +4472,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return this.loanOfficer; } - public LoanSummary getSummary() { - return this.summary; - } - public Set getCollateral() { return this.collateral; } @@ -6511,7 +6507,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { public LoanRepaymentScheduleInstallment fetchLoanForeclosureDetail(final LocalDate closureDate) { Money[] receivables = retriveIncomeOutstandingTillDate(closureDate); - Money totalPrincipal = Money.of(getCurrency(), this.getSummary().getTotalPrincipalOutstanding()); + Money totalPrincipal = Money.of(getCurrency(), this.getLoanSummary().getTotalPrincipalOutstanding()); totalPrincipal = totalPrincipal.minus(receivables[3]); final Set compoundingDetails = null; final LocalDate currentDate = DateUtils.getBusinessLocalDate(); 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 3e2393baa..9772541f8 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 @@ -249,25 +249,25 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom { public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate, final ExternalId externalId) { return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate, - loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), - loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), - loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId); + loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(), + loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(), + loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId); } public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate, final ExternalId externalId) { return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate, - loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), - loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), - loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId); + loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(), + loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(), + loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId); } public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate, final ExternalId externalId) { return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate, - loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), - loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), - loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId); + loan.getLoanSummary().getTotalOutstanding(), loan.getLoanSummary().getTotalPrincipalOutstanding(), + loan.getLoanSummary().getTotalInterestOutstanding(), loan.getLoanSummary().getTotalFeeChargesOutstanding(), + loan.getLoanSummary().getTotalPenaltyChargesOutstanding(), null, false, null, externalId); } public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail, diff --git a/fineract-provider/src/main/resources/jpa/persistence.xml b/fineract-provider/src/main/resources/jpa/persistence.xml index 40eb0b48e..c4119fd3a 100644 --- a/fineract-provider/src/main/resources/jpa/persistence.xml +++ b/fineract-provider/src/main/resources/jpa/persistence.xml @@ -71,6 +71,7 @@ org.apache.fineract.investor.domain.ExternalAssetOwner org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping + org.apache.fineract.investor.domain.ExternalAssetOwnerTransferDetails org.apache.fineract.portfolio.collateral.domain.LoanCollateral org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java index a218ab0da..68b915d4d 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java @@ -19,6 +19,8 @@ package org.apache.fineract.integrationtests.investor.externalassetowner; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -124,6 +126,13 @@ public class InitiateExternalAssetOwnerTransferTest { JsonPath.from(loanDetails).get("netDisbursalAmount").toString()); LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap); + // Add Charge Penalty + Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec, + ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", true)); + Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanID, + LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), "02 March 2020", "10")); + assertNotNull(penalty1LoanChargeId); + String transferExternalId = UUID.randomUUID().toString(); PostInitiateTransferResponse saleResponse = externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "sale", new PostInitiateTransferRequest().settlementDate("2020-03-02").dateFormat("yyyy-MM-dd").locale("en") @@ -154,12 +163,14 @@ public class InitiateExternalAssetOwnerTransferTest { assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(1); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.ACTIVE, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(9999, 12, 31), externalTransferData.getEffectiveTo()); + validateExternalTransferDataDetailsWithPenalty(externalTransferData); String buybackTransferExternalId = "36efeb06-d835-48a1-99eb-09bd1d348c1e"; PostInitiateTransferResponse buybackResponse = externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "buyback", @@ -177,18 +188,21 @@ public class InitiateExternalAssetOwnerTransferTest { assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(1); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.ACTIVE, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(9999, 12, 31), externalTransferData.getEffectiveTo()); + validateExternalTransferDataDetailsWithPenalty(externalTransferData); externalTransferData = retrieveResponse.getContent().get(2); assertEquals(buybackTransferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.BUYBACK, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(9999, 12, 31), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 4)); @@ -202,18 +216,21 @@ public class InitiateExternalAssetOwnerTransferTest { assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(1); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.ACTIVE, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveTo()); + validateExternalTransferDataDetailsWithPenalty(externalTransferData); externalTransferData = retrieveResponse.getContent().get(2); assertEquals(buybackTransferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.BUYBACK, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 3), externalTransferData.getEffectiveTo()); + validateExternalTransferDataDetailsWithPenalty(externalTransferData); } finally { requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); @@ -223,6 +240,16 @@ public class InitiateExternalAssetOwnerTransferTest { } } + private static void validateExternalTransferDataDetailsWithPenalty(ExternalTransferData externalTransferData) { + assertNotNull(externalTransferData.getDetails()); + assertEquals(new BigDecimal("15767.420000"), externalTransferData.getDetails().getTotalOutstanding()); + assertEquals(new BigDecimal("15000.000000"), externalTransferData.getDetails().getTotalPrincipalOutstanding()); + assertEquals(new BigDecimal("757.420000"), externalTransferData.getDetails().getTotalInterestOutstanding()); + assertEquals(new BigDecimal("10.000000"), externalTransferData.getDetails().getTotalPenaltyChargesOutstanding()); + assertEquals(new BigDecimal("0.000000"), externalTransferData.getDetails().getTotalFeeChargesOutstanding()); + assertEquals(new BigDecimal("0.000000"), externalTransferData.getDetails().getTotalOverpaid()); + } + @Test public void saleIsNotAllowedWhenTransferIsAlreadyPending() { try { @@ -386,12 +413,14 @@ public class InitiateExternalAssetOwnerTransferTest { assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(9999, 12, 31), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(1); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.BUYBACK, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(9999, 12, 31), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 3)); final String jobName = "Loan COB"; @@ -405,24 +434,28 @@ public class InitiateExternalAssetOwnerTransferTest { assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(1); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.BUYBACK, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(2); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.CANCELLED, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); externalTransferData = retrieveResponse.getContent().get(3); assertEquals(transferExternalId, externalTransferData.getTransferExternalId()); assertEquals(ExternalTransferData.StatusEnum.CANCELLED, externalTransferData.getStatus()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getSettlementDate()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveFrom()); assertEquals(LocalDate.of(2020, 3, 2), externalTransferData.getEffectiveTo()); + assertNull(externalTransferData.getDetails()); } finally { requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());