From 5a8edd48ecf44eb3e1091585f7b770878f59b406 Mon Sep 17 00:00:00 2001 From: Oleksii Novikov Date: Wed, 19 Aug 2026 20:15:55 +0300 Subject: [PATCH] FINERACT-2455: Add WC business events for transactions --- ...apitalLoanTransactionAdjustmentDataV1.avsc | 23 ++++ .../WorkingCapitalLoanTransactionDataV1.avsc | 2 +- .../service/BusinessEventNotifierService.java | 2 + .../BusinessEventNotifierServiceImpl.java | 25 ++-- .../features/working-capital-write-off.adoc | 4 +- .../messaging/event/EventCheckHelper.java | 72 ++++++++++- ...ualAdjustmentTransactionBusinessEvent.java | 16 +-- ...alLoanAccrualTransactionBusinessEvent.java | 27 ++++ ...talLoanAdjustTransactionBusinessEvent.java | 41 ++++++ ...rgeAdjustmentTransactionBusinessEvent.java | 27 ++++ ...nUndoWriteOffTransactionBusinessEvent.java | 27 ++++ ...LoanWriteOffTransactionBusinessEvent.java} | 4 +- .../WorkingCapitalLoanAccountStepDef.java | 40 +++++- .../WorkingCapitalLoanChargeAccrual.feature | 6 + ...WorkingCapitalLoanChargeAdjustment.feature | 6 +- .../WorkingCapitalLoanChargeOff.feature | 13 +- ...kingCapitalLoanCreditBalanceRefund.feature | 6 +- ...pitalLoanTransactionBusinessEvents.feature | 10 +- .../WorkingCapitalLoanUndoTransaction.feature | 11 +- .../WorkingCapitalLoanWriteOff.feature | 2 + ...kingCapitalTransactionReprocessing.feature | 10 +- ...rkingCapitalLoanTransactionDataMapper.java | 14 +++ ...ustTransactionBusinessEventSerializer.java | 57 +++++++++ ...entConfigurationValidationServiceTest.java | 34 ++--- ...ualAdjustmentTransactionBusinessEvent.java | 40 ++++++ ...lLoanAccrualTransactionBusinessEvent.java} | 8 +- ...talLoanAdjustTransactionBusinessEvent.java | 69 ++++++++++ ...geAdjustmentTransactionBusinessEvent.java} | 9 +- ...UndoWriteOffTransactionBusinessEvent.java} | 8 +- ...LoanWriteOffTransactionBusinessEvent.java} | 8 +- ...alLoanAdjustTransactionEventPublisher.java | 80 ++++++++++++ ...orkingCapitalLoanChargeAccrualService.java | 6 + ...gCapitalLoanChargeOffWriteServiceImpl.java | 5 +- ...talLoanChargeWritePlatformServiceImpl.java | 8 +- ...kingCapitalLoanTransactionDataFactory.java | 50 ++++++++ ...oanTransactionReprocessingServiceImpl.java | 71 +++++++++-- ...ngCapitalLoanWriteOffWriteServiceImpl.java | 8 +- ...ngCapitalLoanWritePlatformServiceImpl.java | 11 +- .../module-changelog-master.xml | 1 + .../0071_wc_transaction_business_events.xml | 119 ++++++++++++++++++ ...ngCapitalLoanWriteOffWriteServiceTest.java | 8 +- .../ExternalEventConfigurationTestData.java | 13 +- 42 files changed, 876 insertions(+), 125 deletions(-) create mode 100644 fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionAdjustmentDataV1.avsc rename fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java => fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java (56%) create mode 100644 fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java create mode 100644 fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java create mode 100644 fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java create mode 100644 fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java rename fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/{WorkingCapitalLoanTransactionReversedBusinessEvent.java => WorkingCapitalLoanWriteOffTransactionBusinessEvent.java} (88%) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/workingcapitalloan/WorkingCapitalLoanAdjustTransactionBusinessEventSerializer.java create mode 100644 fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java rename fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/{WorkingCapitalLoanWrittenOffBusinessEvent.java => WorkingCapitalLoanAccrualTransactionBusinessEvent.java} (71%) create mode 100644 fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java rename fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/{WorkingCapitalLoanChargeAdjustmentPostBusinessEvent.java => WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java} (73%) rename fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/{WorkingCapitalLoanUndoWrittenOffBusinessEvent.java => WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java} (74%) rename fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/{WorkingCapitalLoanChargeAdjustmentPreBusinessEvent.java => WorkingCapitalLoanWriteOffTransactionBusinessEvent.java} (82%) create mode 100644 fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAdjustTransactionEventPublisher.java create mode 100644 fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionDataFactory.java create mode 100644 fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0071_wc_transaction_business_events.xml diff --git a/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionAdjustmentDataV1.avsc b/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionAdjustmentDataV1.avsc new file mode 100644 index 00000000000..3fcc801fe8b --- /dev/null +++ b/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionAdjustmentDataV1.avsc @@ -0,0 +1,23 @@ +{ + "name": "WorkingCapitalLoanTransactionAdjustmentDataV1", + "namespace": "org.apache.fineract.avro.workingcapitalloan.v1", + "type": "record", + "fields": [ + { + "default": null, + "name": "transactionToAdjust", + "type": [ + "null", + "org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionDataV1" + ] + }, + { + "default": null, + "name": "newTransactionDetail", + "type": [ + "null", + "org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionDataV1" + ] + } + ] +} diff --git a/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionDataV1.avsc b/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionDataV1.avsc index 57bc2f3a5b4..4a0a9f11554 100644 --- a/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionDataV1.avsc +++ b/fineract-avro-schemas/src/main/avro/workingcapitalloan/v1/WorkingCapitalLoanTransactionDataV1.avsc @@ -24,7 +24,7 @@ "name": "type", "type": [ "null", - "org.apache.fineract.avro.loan.v1.LoanTransactionEnumDataV1" + "org.apache.fineract.avro.generic.v1.StringEnumOptionDataV1" ] }, { diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java index d0176bc4191..63e52f774be 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierService.java @@ -37,6 +37,8 @@ public interface BusinessEventNotifierService { */ void notifyPostBusinessEvent(BusinessEvent businessEvent); + boolean isExternalEventPostingEnabled(BusinessEvent businessEvent); + /** * Method is to register a class as listener for pre-processing of any Business event */ diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java index be44e057329..1c30e670353 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/service/BusinessEventNotifierServiceImpl.java @@ -90,27 +90,30 @@ public > void addPreBusinessEventListener(Class ev @Override public void notifyPostBusinessEvent(BusinessEvent businessEvent) { throwExceptionIfBulkEvent(businessEvent); - boolean isExternalEvent = !(businessEvent instanceof NoExternalEvent); List businessEventListeners = findSuitableListeners(postListeners, businessEvent.getClass()); for (BusinessEventListener eventListener : businessEventListeners) { eventListener.onBusinessEvent(businessEvent); } - if (isExternalEvent && isExternalEventPostingEnabled()) { - // we only want to create external events for operations that were successful, hence the post listener - if (externalBusinessEventConfigurationService.isExternalEventConfiguredForPosting(businessEvent)) { - if (isExternalEventRecordingEnabled()) { - recordedEvents.get().add(businessEvent); + // we only want to create external events for operations that were successful, hence the post listener + if (isExternalEventPostingEnabled(businessEvent)) { + if (isExternalEventRecordingEnabled()) { + recordedEvents.get().add(businessEvent); + } else { + if (transactionHelper.hasTransaction()) { + storeTransactionalBusinessEvent(businessEvent); } else { - if (transactionHelper.hasTransaction()) { - storeTransactionalBusinessEvent(businessEvent); - } else { - externalEventService.postEvent(businessEvent); - } + externalEventService.postEvent(businessEvent); } } } } + @Override + public boolean isExternalEventPostingEnabled(final BusinessEvent businessEvent) { + return !(businessEvent instanceof NoExternalEvent) && isExternalEventPostingEnabled() + && externalBusinessEventConfigurationService.isExternalEventConfiguredForPosting(businessEvent); + } + private List findSuitableListeners(Map> listeners, Class eventClazz) { List result = new ArrayList<>(); for (Map.Entry> entry : listeners.entrySet()) { diff --git a/fineract-doc/src/docs/en/chapters/features/working-capital-write-off.adoc b/fineract-doc/src/docs/en/chapters/features/working-capital-write-off.adoc index 137acc69ee3..05a446481d0 100644 --- a/fineract-doc/src/docs/en/chapters/features/working-capital-write-off.adoc +++ b/fineract-doc/src/docs/en/chapters/features/working-capital-write-off.adoc @@ -226,8 +226,8 @@ For a loan that was charged off, the mirrors flip the reclassification instead: == Business Events -* `WorkingCapitalLoanWrittenOffBusinessEvent` — emitted after a write-off. -* `WorkingCapitalLoanUndoWrittenOffBusinessEvent` — emitted after an undo write-off. +* `WorkingCapitalLoanWriteOffTransactionBusinessEvent` — emitted after a write-off. +* `WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent` — emitted after an undo write-off. * `WorkingCapitalLoanBalanceChangedBusinessEvent` — emitted by both operations, since the write-off zeroes the outstanding balance and the undo restores it. * `WorkingCapitalLoanStatusChangedBusinessEvent` — emitted by both operations, for the transition to diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/EventCheckHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/EventCheckHelper.java index 04de24b44e1..51ee2aba3f1 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/EventCheckHelper.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/EventCheckHelper.java @@ -57,6 +57,7 @@ import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanDelinquencyScheduleTagDataV1; import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanJournalEntryDataV1; import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanSummaryDataV1; +import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionAdjustmentDataV1; import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionDataV1; import org.apache.fineract.client.feign.FineractFeignClient; import org.apache.fineract.client.models.ExternalTransferData; @@ -149,6 +150,10 @@ import org.apache.fineract.test.messaging.event.workingcapitalloan.loan.WorkingCapitalLoanUndoChargeOffEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.loan.WorkingCapitalLoanUndoDisbursalEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.AbstractWorkingCapitalLoanTransactionEvent; +import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent; +import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanAccrualTransactionBusinessEvent; +import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanAdjustTransactionBusinessEvent; +import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanChargeOffTransactionBusinessEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanCreditBalanceRefundTransactionBusinessEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanDisbursalTransactionBusinessEvent; @@ -159,8 +164,9 @@ import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanGoodwillCreditTransactionBusinessEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanPayoutRefundTransactionBusinessEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanRepaymentTransactionBusinessEvent; -import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanTransactionReversedBusinessEvent; import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanUndoDisbursalTransactionBusinessEvent; +import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent; +import org.apache.fineract.test.messaging.event.workingcapitalloan.transaction.WorkingCapitalLoanWriteOffTransactionBusinessEvent; import org.apache.fineract.test.messaging.store.EventStore; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -418,6 +424,7 @@ private void workingCapitalLoanTransactionEventCheck(final Class data.getType().getCode()).isEqualTo(transaction.getType().getCode())// .extractingData(WorkingCapitalLoanTransactionDataV1::getReversed).isEqualTo(expectedReversed); } @@ -1165,11 +1172,70 @@ public void workingCapitalLoanGoodwillCreditTransactionEventCheck(final Long loa expectedAmount, false); } - public void workingCapitalLoanTransactionReversedEventCheck(final Long loanId, final String transactionType) { + public void workingCapitalLoanAdjustTransactionReversalEventCheck(final Long loanId, final String transactionType) { waitForTransactionCommit(); final GetWorkingCapitalLoanTransactionIdResponse transaction = findLastWorkingCapitalLoanTransaction(loanId, transactionType, true, "Reversed " + transactionType + " transaction not found"); - workingCapitalLoanTransactionEventCheck(WorkingCapitalLoanTransactionReversedBusinessEvent.class, loanId, transaction, null, true); + eventAssertion.assertEvent(WorkingCapitalLoanAdjustTransactionBusinessEvent.class, transaction.getId())// + .extractingData(data -> data.getTransactionToAdjust().getWcLoanId()).isEqualTo(loanId)// + .extractingBigDecimal(data -> data.getTransactionToAdjust().getTransactionAmount()) + .isEqualTo(transaction.getTransactionAmount())// + .extractingData(data -> data.getTransactionToAdjust().getType().getCode()).isEqualTo(transaction.getType().getCode())// + .extractingData(data -> data.getTransactionToAdjust().getReversed()).isEqualTo(true)// + .extractingData(WorkingCapitalLoanTransactionAdjustmentDataV1::getNewTransactionDetail).isEqualTo(null); + } + + public void workingCapitalLoanAdjustTransactionReprocessEventCheck(final Long loanId, final String transactionType, + final String transactionDate, final BigDecimal previousPrincipalPortion, final BigDecimal newPrincipalPortion, + final BigDecimal previousFeeChargesPortion, final BigDecimal newFeeChargesPortion) { + final GetWorkingCapitalLoanTransactionIdResponse transaction = workingCapitalLoanTransactionDetails(loanId, transactionType, + transactionDate); + eventAssertion.assertEvent(WorkingCapitalLoanAdjustTransactionBusinessEvent.class, transaction.getId())// + .extractingData(data -> data.getTransactionToAdjust().getWcLoanId()).isEqualTo(loanId)// + .extractingBigDecimal(data -> data.getTransactionToAdjust().getPrincipalPortion()).isEqualTo(previousPrincipalPortion)// + .extractingBigDecimal(data -> data.getTransactionToAdjust().getFeeChargesPortion()).isEqualTo(previousFeeChargesPortion)// + .extractingBigDecimal(data -> data.getNewTransactionDetail().getPrincipalPortion()).isEqualTo(newPrincipalPortion)// + .extractingBigDecimal(data -> data.getNewTransactionDetail().getFeeChargesPortion()).isEqualTo(newFeeChargesPortion); + } + + public void workingCapitalLoanAccrualTransactionEventCheck(final Long loanId, final BigDecimal expectedAmount) { + waitForTransactionCommit(); + final GetWorkingCapitalLoanTransactionIdResponse transaction = findLastWorkingCapitalLoanTransaction(loanId, "accrual", false, + "Accrual transaction not found"); + workingCapitalLoanTransactionEventCheck(WorkingCapitalLoanAccrualTransactionBusinessEvent.class, loanId, transaction, + expectedAmount, false); + } + + public void workingCapitalLoanAccrualAdjustmentTransactionEventCheck(final Long loanId, final BigDecimal expectedAmount) { + waitForTransactionCommit(); + final GetWorkingCapitalLoanTransactionIdResponse transaction = findLastWorkingCapitalLoanTransaction(loanId, "accrual", true, + "Reversed accrual transaction not found"); + workingCapitalLoanTransactionEventCheck(WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.class, loanId, transaction, + expectedAmount, true); + } + + public void workingCapitalLoanWriteOffTransactionEventCheck(final Long loanId, final BigDecimal expectedAmount) { + waitForTransactionCommit(); + final GetWorkingCapitalLoanTransactionIdResponse transaction = findLastWorkingCapitalLoanTransaction(loanId, "writeOff", false, + "Write-off transaction not found"); + workingCapitalLoanTransactionEventCheck(WorkingCapitalLoanWriteOffTransactionBusinessEvent.class, loanId, transaction, + expectedAmount, false); + } + + public void workingCapitalLoanUndoWriteOffTransactionEventCheck(final Long loanId, final BigDecimal expectedAmount) { + waitForTransactionCommit(); + final GetWorkingCapitalLoanTransactionIdResponse transaction = findLastWorkingCapitalLoanTransaction(loanId, "writeOff", true, + "Reversed write-off transaction not found"); + workingCapitalLoanTransactionEventCheck(WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.class, loanId, transaction, + expectedAmount, true); + } + + public void workingCapitalLoanChargeAdjustmentTransactionEventCheck(final Long loanId, final BigDecimal expectedAmount) { + waitForTransactionCommit(); + final GetWorkingCapitalLoanTransactionIdResponse transaction = findLastWorkingCapitalLoanTransaction(loanId, "chargeAdjustment", + false, "Charge adjustment transaction not found"); + workingCapitalLoanTransactionEventCheck(WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.class, loanId, transaction, + expectedAmount, false); } public void workingCapitalLoanChargeOffTransactionEventCheck(final Long loanId, final BigDecimal expectedAmount) { diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java similarity index 56% rename from fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java rename to fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java index 15fad4a781e..6433326dd8d 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java @@ -16,20 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction; +package org.apache.fineract.test.messaging.event.workingcapitalloan.transaction; -import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; - -public class WorkingCapitalLoanTransactionReversedBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { - - private static final String TYPE = "WorkingCapitalLoanTransactionReversedBusinessEvent"; - - public WorkingCapitalLoanTransactionReversedBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { - super(value, aggregateRootId); - } +public class WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent extends AbstractWorkingCapitalLoanTransactionEvent { @Override - public String getType() { - return TYPE; + public String getEventName() { + return "WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent"; } } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java new file mode 100644 index 00000000000..107bc9b6fff --- /dev/null +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java @@ -0,0 +1,27 @@ +/** + * 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.test.messaging.event.workingcapitalloan.transaction; + +public class WorkingCapitalLoanAccrualTransactionBusinessEvent extends AbstractWorkingCapitalLoanTransactionEvent { + + @Override + public String getEventName() { + return "WorkingCapitalLoanAccrualTransactionBusinessEvent"; + } +} diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java new file mode 100644 index 00000000000..9c2f10471d0 --- /dev/null +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java @@ -0,0 +1,41 @@ +/** + * 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.test.messaging.event.workingcapitalloan.transaction; + +import java.util.function.Function; +import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionAdjustmentDataV1; +import org.apache.fineract.test.messaging.event.Event; + +public class WorkingCapitalLoanAdjustTransactionBusinessEvent implements Event { + + @Override + public Class getDataClass() { + return WorkingCapitalLoanTransactionAdjustmentDataV1.class; + } + + @Override + public Function getIdExtractor() { + return data -> data.getTransactionToAdjust().getId(); + } + + @Override + public String getEventName() { + return "WorkingCapitalLoanAdjustTransactionBusinessEvent"; + } +} diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java new file mode 100644 index 00000000000..59962dd5fbe --- /dev/null +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java @@ -0,0 +1,27 @@ +/** + * 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.test.messaging.event.workingcapitalloan.transaction; + +public class WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent extends AbstractWorkingCapitalLoanTransactionEvent { + + @Override + public String getEventName() { + return "WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent"; + } +} diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java new file mode 100644 index 00000000000..77cfaf82b27 --- /dev/null +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java @@ -0,0 +1,27 @@ +/** + * 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.test.messaging.event.workingcapitalloan.transaction; + +public class WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent extends AbstractWorkingCapitalLoanTransactionEvent { + + @Override + public String getEventName() { + return "WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent"; + } +} diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanWriteOffTransactionBusinessEvent.java similarity index 88% rename from fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java rename to fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanWriteOffTransactionBusinessEvent.java index 6a57e10fa44..d905f1bc5a6 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanTransactionReversedBusinessEvent.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/messaging/event/workingcapitalloan/transaction/WorkingCapitalLoanWriteOffTransactionBusinessEvent.java @@ -18,10 +18,10 @@ */ package org.apache.fineract.test.messaging.event.workingcapitalloan.transaction; -public class WorkingCapitalLoanTransactionReversedBusinessEvent extends AbstractWorkingCapitalLoanTransactionEvent { +public class WorkingCapitalLoanWriteOffTransactionBusinessEvent extends AbstractWorkingCapitalLoanTransactionEvent { @Override public String getEventName() { - return "WorkingCapitalLoanTransactionReversedBusinessEvent"; + return "WorkingCapitalLoanWriteOffTransactionBusinessEvent"; } } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java index 5a67253bca8..ab225758a53 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java @@ -1748,9 +1748,43 @@ public void aWorkingCapitalLoanGoodwillCreditTransactionBusinessEventIsRaised(fi eventCheckHelper.workingCapitalLoanGoodwillCreditTransactionEventCheck(getCreatedLoanId(), new BigDecimal(amount)); } - @Then("a Working Capital Loan Transaction Reversed business event is raised for the {string} transaction") - public void aWorkingCapitalLoanTransactionReversedBusinessEventIsRaised(final String transactionType) { - eventCheckHelper.workingCapitalLoanTransactionReversedEventCheck(getCreatedLoanId(), transactionType); + @Then("a Working Capital Loan Adjust Transaction business event is raised for the reversed {string} transaction") + public void aWorkingCapitalLoanAdjustTransactionBusinessEventIsRaisedForReversal(final String transactionType) { + eventCheckHelper.workingCapitalLoanAdjustTransactionReversalEventCheck(getCreatedLoanId(), transactionType); + } + + @Then("a Working Capital Loan Adjust Transaction business event is raised for the {string} transaction on {string} with principal portion changed from {string} to {string} and fee portion changed from {string} to {string}") + public void aWorkingCapitalLoanAdjustTransactionBusinessEventIsRaisedForReprocess(final String transactionType, + final String transactionDate, final String previousPrincipalPortion, final String newPrincipalPortion, + final String previousFeeChargesPortion, final String newFeeChargesPortion) { + eventCheckHelper.workingCapitalLoanAdjustTransactionReprocessEventCheck(getCreatedLoanId(), transactionType, transactionDate, + new BigDecimal(previousPrincipalPortion), new BigDecimal(newPrincipalPortion), new BigDecimal(previousFeeChargesPortion), + new BigDecimal(newFeeChargesPortion)); + } + + @Then("a Working Capital Loan Accrual transaction business event is raised with {string} EUR amount") + public void aWorkingCapitalLoanAccrualTransactionBusinessEventIsRaised(final String amount) { + eventCheckHelper.workingCapitalLoanAccrualTransactionEventCheck(getCreatedLoanId(), new BigDecimal(amount)); + } + + @Then("a Working Capital Loan Accrual Adjustment transaction business event is raised with {string} EUR amount") + public void aWorkingCapitalLoanAccrualAdjustmentTransactionBusinessEventIsRaised(final String amount) { + eventCheckHelper.workingCapitalLoanAccrualAdjustmentTransactionEventCheck(getCreatedLoanId(), new BigDecimal(amount)); + } + + @Then("a Working Capital Loan Write Off transaction business event is raised with {string} EUR amount") + public void aWorkingCapitalLoanWriteOffTransactionBusinessEventIsRaised(final String amount) { + eventCheckHelper.workingCapitalLoanWriteOffTransactionEventCheck(getCreatedLoanId(), new BigDecimal(amount)); + } + + @Then("a Working Capital Loan Undo Write Off transaction business event is raised with {string} EUR amount") + public void aWorkingCapitalLoanUndoWriteOffTransactionBusinessEventIsRaised(final String amount) { + eventCheckHelper.workingCapitalLoanUndoWriteOffTransactionEventCheck(getCreatedLoanId(), new BigDecimal(amount)); + } + + @Then("a Working Capital Loan Charge Adjustment transaction business event is raised with {string} EUR amount") + public void aWorkingCapitalLoanChargeAdjustmentTransactionBusinessEventIsRaised(final String amount) { + eventCheckHelper.workingCapitalLoanChargeAdjustmentTransactionEventCheck(getCreatedLoanId(), new BigDecimal(amount)); } @Then("a Working Capital Loan Charge Off transaction business event is raised with {string} EUR amount") diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAccrual.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAccrual.feature index ef567524b15..70f3ea10cab 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAccrual.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAccrual.feature @@ -29,6 +29,7 @@ Feature: Working Capital Loan Charge Accrual | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 15 January 2026 | Accrual | 50.0 | 0.0 | 50.0 | 0.0 | false | + And a Working Capital Loan Accrual transaction business event is raised with "50.0" EUR amount And Working Capital Loan Transactions tab has a "ACCRUAL" transaction with date "15 January 2026" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | ASSET | 112603 | Interest/Fee Receivable | 50.0 | | @@ -115,6 +116,7 @@ Feature: Working Capital Loan Charge Accrual | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 April 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 10 April 2026 | Accrual | 50.0 | 0.0 | 50.0 | 0.0 | false | + And a Working Capital Loan Accrual transaction business event is raised with "50.0" EUR amount And Working Capital Loan Transactions tab has a "ACCRUAL" transaction with date "10 April 2026" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | ASSET | 112603 | Interest/Fee Receivable | 50.0 | | @@ -389,6 +391,7 @@ Feature: Working Capital Loan Charge Accrual | 01 February 2027 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 15 February 2027 | Repayment | 9100.0 | 9000.0 | 100.0 | 0.0 | false | | 15 February 2027 | Accrual | 100.0 | 0.0 | 100.0 | 0.0 | false | + And a Working Capital Loan Accrual transaction business event is raised with "100.0" EUR amount And Working Capital Loan Transactions tab has a "ACCRUAL" transaction with date "15 February 2027" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | ASSET | 112603 | Interest/Fee Receivable | 100.0 | | @@ -412,6 +415,7 @@ Feature: Working Capital Loan Charge Accrual | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 March 2027 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 10 March 2027 | Accrual | 100.0 | 0.0 | 100.0 | 0.0 | false | + And a Working Capital Loan Accrual transaction business event is raised with "100.0" EUR amount And Working Capital Loan Transactions tab has a "ACCRUAL" transaction with date "10 March 2027" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | ASSET | 112603 | Interest/Fee Receivable | 100.0 | | @@ -421,6 +425,7 @@ Feature: Working Capital Loan Charge Accrual | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 March 2027 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | true | | 10 March 2027 | Accrual | 100.0 | 0.0 | 100.0 | 0.0 | true | + And a Working Capital Loan Accrual Adjustment transaction business event is raised with "100.0" EUR amount And Working Capital Loan Transactions tab has a reversed "ACCRUAL" transaction with date "10 March 2027" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | ASSET | 112603 | Interest/Fee Receivable | 100.0 | | @@ -437,6 +442,7 @@ Feature: Working Capital Loan Charge Accrual | 10 March 2027 | Accrual | 100.0 | 0.0 | 100.0 | 0.0 | true | | 10 March 2027 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 20 March 2027 | Accrual | 100.0 | 0.0 | 100.0 | 0.0 | false | + And a Working Capital Loan Accrual transaction business event is raised with "100.0" EUR amount And Working Capital Loan Transactions tab has a "ACCRUAL" transaction with date "20 March 2027" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | ASSET | 112603 | Interest/Fee Receivable | 100.0 | | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAdjustment.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAdjustment.feature index 076388f021b..06d12cd77b2 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAdjustment.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeAdjustment.feature @@ -318,7 +318,8 @@ Feature: WorkingCapitalLoanChargeAdjustmentFeature And Admin runs inline COB job for Working Capital Loan by loanId And Admin adds "WORKING_CAPITAL_SPECIFIED_DUE_DATE_FEE" specified due date charge to working capital loan with "10 January 2026" due date and 100.0 transaction amount When Admin makes a charge adjustment for the last added charge with 100.0 amount on working capital loan - Then Working Capital Loan has transactions: + Then a Working Capital Loan Charge Adjustment transaction business event is raised with "100.0" EUR amount + And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 10 January 2026 | Charge Adjustment | 100.0 | 0.0 | 100.0 | 0.0 | false | @@ -328,7 +329,8 @@ Feature: WorkingCapitalLoanChargeAdjustmentFeature When Admin sets the business date to "15 January 2026" And Admin runs inline COB job for Working Capital Loan by loanId And Admin reverts the last charge adjustment on working capital loan - Then Working Capital Loan has transactions: + Then a Working Capital Loan Adjust Transaction business event is raised for the reversed "chargeAdjustment" transaction + And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 10 January 2026 | Charge Adjustment | 100.0 | 0.0 | 100.0 | 0.0 | true | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeOff.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeOff.feature index 20548757807..e84f3605bba 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeOff.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanChargeOff.feature @@ -414,7 +414,7 @@ Feature: Working Capital Loan Charge-off Then a Working Capital Loan Charge Off business event is raised with "15 January 2026" charge off date When Admin undoes the charge-off on the Working Capital loan Then a Working Capital Loan Undo Charge Off business event is raised - And a Working Capital Loan Transaction Reversed business event is raised for the "chargeOff" transaction + And a Working Capital Loan Adjust Transaction business event is raised for the reversed "chargeOff" transaction Then Admin closes the Working Capital loan with a full repayment on "15 January 2026" @TestRailId:C98193 @@ -434,15 +434,18 @@ Feature: Working Capital Loan Charge-off And Admin runs inline COB job for Working Capital Loan by loanId And Admin charges off the Working Capital loan on "15 January 2026" with charge-off reason "Fraud" Then a Working Capital Loan Charge Off business event is raised with "15 January 2026" charge off date - When Customer makes "REPAYMENT" transaction on "10 January 2026" with 100.0 transaction amount on Working Capital loan + When Customer makes "REPAYMENT" transaction on "10 January 2026" with 40.0 transaction amount on Working Capital loan + Then a Working Capital Loan Adjust Transaction business event is raised for the "chargeOff" transaction on "15 January 2026" with principal portion changed from "100.0" to "60.0" and fee portion changed from "0.0" to "0.0" + When Customer makes "REPAYMENT" transaction on "10 January 2026" with 60.0 transaction amount on Working Capital loan Then a Working Capital Loan Undo Charge Off business event is raised - And a Working Capital Loan Transaction Reversed business event is raised for the "chargeOff" transaction + And a Working Capital Loan Adjust Transaction business event is raised for the reversed "chargeOff" transaction And Working capital loan account has the correct data: | product.name | submittedOnDate | expectedDisbursementDate | principal | approvedPrincipal | totalPaymentVolume | periodPaymentRate | discount | chargedOff | chargedOffOnDate | chargeOffReason.name | | WCLP | 2026-01-01 | 2026-01-01 | 100.0 | 100.0 | 1000.0 | 18.0 | null | false | null | null | And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 100.0 | 100.0 | 0.0 | 0.0 | false | - | 10 January 2026 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | false | - | 15 January 2026 | Charge-off | 100.0 | 100.0 | 0.0 | 0.0 | true | + | 10 January 2026 | Repayment | 40.0 | 40.0 | 0.0 | 0.0 | false | + | 10 January 2026 | Repayment | 60.0 | 60.0 | 0.0 | 0.0 | false | + | 15 January 2026 | Charge-off | 60.0 | 60.0 | 0.0 | 0.0 | true | Then Working Capital loan status will be "CLOSED_OBLIGATIONS_MET" diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanCreditBalanceRefund.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanCreditBalanceRefund.feature index fa285db252a..b2df869b5bd 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanCreditBalanceRefund.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanCreditBalanceRefund.feature @@ -599,7 +599,11 @@ Feature: Working Capital Loan Credit Balance Refund | 05 January 2026 | Repayment | 9100.0 | 8800.0 | 0.0 | 0.0 | false | | 10 January 2026 | Credit Balance Refund | 300.0 | 0.0 | 0.0 | 0.0 | false | And Customer undo "1"th "REPAYMENT" transaction made on "02 January 2026" on Working Capital loan - Then Working Capital loan status will be "ACTIVE" + # The replay leaves only 9000 repaid, so 200 of the refund loses its overpayment backing and becomes due principal + Then a Working Capital Loan Adjust Transaction business event is raised for the "repayment" transaction on "05 January 2026" with principal portion changed from "8800.0" to "9000.0" and fee portion changed from "0.0" to "0.0" + And a Working Capital Loan Adjust Transaction business event is raised for the "creditBalanceRefund" transaction on "10 January 2026" with principal portion changed from "0.0" to "200.0" and fee portion changed from "0.0" to "0.0" + And a Working Capital Loan Adjust Transaction business event is raised for the reversed "repayment" transaction + And Working Capital loan status will be "ACTIVE" And Working capital loan account has the correct data: | principal | totalPaidPrincipal | realizedIncome | unrealizedIncome | overpaymentAmount | | 9200.0 | 9000.0 | 0.0 | 0.0 | 0.0 | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanTransactionBusinessEvents.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanTransactionBusinessEvents.feature index 08b3ddb1a36..5c900d88450 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanTransactionBusinessEvents.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanTransactionBusinessEvents.feature @@ -25,7 +25,7 @@ Feature: Working Capital Loan Transaction Business Events Then Admin closes the Working Capital loan with a full repayment on "02 January 2026" @TestRailId:C94075 - Scenario: Working Capital loan raises Transaction Reversed business event when a repayment and a payout refund are undone + Scenario: Working Capital loan raises Adjust Transaction business event when a repayment and a payout refund are undone When Admin sets the business date to "01 January 2026" And Admin creates a client with random data And Admin creates a working capital loan with the following data: @@ -42,18 +42,18 @@ Feature: Working Capital Loan Transaction Business Events Then a Working Capital Loan Repayment transaction business event is raised with "100.0" EUR amount And a Working Capital Loan Balance Changed business event is raised And Customer undo "1"th "REPAYMENT" transaction made on "02 January 2026" on Working Capital loan - Then a Working Capital Loan Transaction Reversed business event is raised for the "repayment" transaction + Then a Working Capital Loan Adjust Transaction business event is raised for the reversed "repayment" transaction And a Working Capital Loan Balance Changed business event is raised When Admin sets the business date to "03 January 2026" And Customer makes "PAYOUT_REFUND" transaction on "03 January 2026" with 150.0 transaction amount on Working Capital loan And a Working Capital Loan Balance Changed business event is raised And Customer undo "1"th "PAYOUT_REFUND" transaction made on "03 January 2026" on Working Capital loan - Then a Working Capital Loan Transaction Reversed business event is raised for the "payoutRefund" transaction + Then a Working Capital Loan Adjust Transaction business event is raised for the reversed "payoutRefund" transaction And a Working Capital Loan Balance Changed business event is raised Then Admin closes the Working Capital loan with a full repayment on "03 January 2026" @TestRailId:C98191 - Scenario: Working Capital loan raises Transaction Reversed business event when a discount fee adjustment is undone + Scenario: Working Capital loan raises Adjust Transaction business event when a discount fee adjustment is undone When Admin sets the business date to "01 January 2026" And Admin creates a client with random data And Admin creates a working capital loan with the following data: @@ -68,7 +68,7 @@ Feature: Working Capital Loan Transaction Business Events When Admin adds Discount fee with "12" amount on Working Capital loan account for last disbursement And Admin adds Discount fee adjustment with "5" amount on Working Capital loan account for last discount And Admin undo the last Discount fee adjustment on Working Capital loan account - Then a Working Capital Loan Transaction Reversed business event is raised for the "discountFeeAdjustment" transaction + Then a Working Capital Loan Adjust Transaction business event is raised for the reversed "discountFeeAdjustment" transaction And a Working Capital Loan Balance Changed business event is raised And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanUndoTransaction.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanUndoTransaction.feature index 64316cadc83..be137fba02a 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanUndoTransaction.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanUndoTransaction.feature @@ -234,7 +234,8 @@ Feature: Working Capital Loan Undo Transaction | 1 | 2026-01-01 | 2026-01-30 | 270.0 | 270.0 | 0.0 | true | 0.0 | 0 | # Undo the goodwill credit: transaction is reversed, mirror GL entries created When Customer undo "1"th "GOODWILL_CREDIT" transaction made on "10 January 2026" on Working Capital loan - Then Working Capital Loan Transactions tab has a reversed "GOODWILL_CREDIT" transaction with date "10 January 2026" which has the following Journal entries: + Then a Working Capital Loan Adjust Transaction business event is raised for the reversed "goodwillCredit" transaction + And Working Capital Loan Transactions tab has a reversed "GOODWILL_CREDIT" transaction with date "10 January 2026" which has the following Journal entries: | Type | Account code | Account name | Debit | Credit | | EXPENSE | 744003 | Goodwill Expense Account | 270.0 | | | ASSET | 112601 | Loans Receivable | | 270.0 | @@ -347,7 +348,9 @@ Feature: Working Capital Loan Undo Transaction # 9000 + overpayment 200; the 100 already refunded must stay subtracted: overpayment = 100, not 200. When Admin sets the business date to "09 January 2026" And Customer undo "1"th "REPAYMENT" transaction made on "06 January 2026" on Working Capital loan - Then Working Capital Loan has transactions: + Then a Working Capital Loan Adjust Transaction business event is raised for the "repayment" transaction on "07 January 2026" with principal portion changed from "9000.0" to "9000.0" and fee portion changed from "0.0" to "100.0" + And a Working Capital Loan Adjust Transaction business event is raised for the reversed "repayment" transaction + And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 06 January 2026 | Repayment | 100.0 | 0.0 | 100.0 | 0.0 | true | @@ -381,7 +384,9 @@ Feature: Working Capital Loan Undo Transaction # Undo the day-5 repayment: the remaining day-10 repayment alone covers 6500 principal (no overpayment) When Admin sets the business date to "12 January 2026" And Customer undo "1"th "REPAYMENT" transaction made on "05 January 2026" on Working Capital loan - Then Working Capital loan status will be "ACTIVE" + Then a Working Capital Loan Adjust Transaction business event is raised for the "repayment" transaction on "10 January 2026" with principal portion changed from "6000.0" to "6500.0" and fee portion changed from "0.0" to "0.0" + And a Working Capital Loan Adjust Transaction business event is raised for the reversed "repayment" transaction + And Working Capital loan status will be "ACTIVE" And Working Capital loan balance payload contains the following fields: | field | value | | principalOutstanding | 2500.0 | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanWriteOff.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanWriteOff.feature index 438d8e12b23..c55a9c592fa 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanWriteOff.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalLoanWriteOff.feature @@ -15,6 +15,7 @@ Feature: Working Capital Loan Write-off # --- write-off the working capital loan account--- # And Admin sets the business date to "15 January 2026" And Admin writes off the Working Capital loan on "15 January 2026" + Then a Working Capital Loan Write Off transaction business event is raised with "100.0" EUR amount Then Working Capital loan status will be "CLOSED_WRITTEN_OFF" And Working Capital loan balance principalOutstanding is "0.0" And Working Capital Loan has transactions: @@ -46,6 +47,7 @@ Feature: Working Capital Loan Write-off And Working Capital loan balance principalOutstanding is "0.0" # --- undo write-off the working capital loan account--- # When Admin undoes the write-off on the Working Capital loan + Then a Working Capital Loan Undo Write Off transaction business event is raised with "100.0" EUR amount And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 100.0 | 100.0 | 0.0 | 0.0 | false | diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalTransactionReprocessing.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalTransactionReprocessing.feature index 96b693056c2..14cdde8997f 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalTransactionReprocessing.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalTransactionReprocessing.feature @@ -253,7 +253,8 @@ Feature: Working Capital Transaction Reprocessing # Backdated Txn#2 on day 5 takes the fee; Txn#1 is reprocessed to 30 principal When Admin sets the business date to "15 January 2026" And Customer makes repayment on "05 January 2026" with 25 transaction amount on Working Capital loan - Then Working Capital Loan has transactions: + Then a Working Capital Loan Adjust Transaction business event is raised for the "repayment" transaction on "10 January 2026" with principal portion changed from "25.0" to "30.0" and fee portion changed from "5.0" to "0.0" + And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 05 January 2026 | Repayment | 25.0 | 20.0 | 5.0 | 0.0 | false | @@ -285,7 +286,9 @@ Feature: Working Capital Transaction Reprocessing And Customer makes repayment on "10 January 2026" with 25 transaction amount on Working Capital loan # Reverse Txn#1 (the day-5 repayment) -> Txn#2 must be reprocessed to take the fee When Customer undo "1"th "REPAYMENT" transaction made on "05 January 2026" on Working Capital loan - Then Working Capital Loan has transactions: + Then a Working Capital Loan Adjust Transaction business event is raised for the "repayment" transaction on "10 January 2026" with principal portion changed from "25.0" to "20.0" and fee portion changed from "0.0" to "5.0" + And a Working Capital Loan Adjust Transaction business event is raised for the reversed "repayment" transaction + And Working Capital Loan has transactions: | transactionDate | type | transactionAmount | principalPortion | feeChargesPortion | penaltyChargesPortion | reversed | | 01 January 2026 | Disbursement | 9000.0 | 9000.0 | 0.0 | 0.0 | false | | 05 January 2026 | Repayment | 30.0 | 25.0 | 5.0 | 0.0 | true | @@ -385,7 +388,8 @@ Feature: Working Capital Transaction Reprocessing And Customer makes repayment on "10 January 2026" with 7000 transaction amount on Working Capital loan When Admin sets the business date to "15 January 2026" And Customer makes repayment on "05 January 2026" with 5000 transaction amount on Working Capital loan - Then Working Capital loan balance payload contains the following fields: + Then a Working Capital Loan Adjust Transaction business event is raised for the "repayment" transaction on "10 January 2026" with principal portion changed from "7000.0" to "4000.0" and fee portion changed from "0.0" to "0.0" + And Working Capital loan balance payload contains the following fields: | field | value | | principalOutstanding | 0.0 | | totalPaidPrincipal | 9000.0 | diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/workingcapitalloan/WorkingCapitalLoanTransactionDataMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/workingcapitalloan/WorkingCapitalLoanTransactionDataMapper.java index ad4b5a2833c..a1f2ffc07f0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/workingcapitalloan/WorkingCapitalLoanTransactionDataMapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/mapper/workingcapitalloan/WorkingCapitalLoanTransactionDataMapper.java @@ -18,19 +18,33 @@ */ package org.apache.fineract.infrastructure.event.external.service.serialization.mapper.workingcapitalloan; +import java.util.Optional; +import org.apache.fineract.avro.generic.v1.StringEnumOptionDataV1; import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionDataV1; import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.support.AvroMapperConfig; +import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanTransactionData; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; @Mapper(config = AvroMapperConfig.class) public interface WorkingCapitalLoanTransactionDataMapper { @Mapping(target = "reversed", expression = "java(isReversed(source))") + @Mapping(target = "type", source = "type", qualifiedByName = "toTransactionType") WorkingCapitalLoanTransactionDataV1 map(WorkingCapitalLoanTransactionData source); default boolean isReversed(WorkingCapitalLoanTransactionData source) { return Boolean.TRUE.equals(source.getReversed()) || source.getReversedOnDate() != null; } + + @Named("toTransactionType") + default StringEnumOptionDataV1 toTransactionType(final LoanTransactionEnumData type) { + return Optional.ofNullable(type).map(LoanTransactionEnumData::getId).map(Long::intValue).map(LoanTransactionType::fromInt) + .map(transactionType -> StringEnumOptionDataV1.newBuilder().setId(transactionType.name()).setCode(transactionType.getCode()) + .setValue(transactionType.name()).build()) + .orElse(null); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/workingcapitalloan/WorkingCapitalLoanAdjustTransactionBusinessEventSerializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/workingcapitalloan/WorkingCapitalLoanAdjustTransactionBusinessEventSerializer.java new file mode 100644 index 00000000000..d76ab71e27a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/workingcapitalloan/WorkingCapitalLoanAdjustTransactionBusinessEventSerializer.java @@ -0,0 +1,57 @@ +/** + * 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.infrastructure.event.external.service.serialization.serializer.workingcapitalloan; + +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.apache.avro.generic.GenericContainer; +import org.apache.fineract.avro.generator.ByteBufferSerializable; +import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionAdjustmentDataV1; +import org.apache.fineract.avro.workingcapitalloan.v1.WorkingCapitalLoanTransactionDataV1; +import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanAdjustTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.external.service.serialization.mapper.workingcapitalloan.WorkingCapitalLoanTransactionDataMapper; +import org.apache.fineract.infrastructure.event.external.service.serialization.serializer.BusinessEventSerializer; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class WorkingCapitalLoanAdjustTransactionBusinessEventSerializer implements BusinessEventSerializer { + + private final WorkingCapitalLoanTransactionDataMapper mapper; + + @Override + public boolean canSerialize(final BusinessEvent event) { + return event instanceof WorkingCapitalLoanAdjustTransactionBusinessEvent; + } + + @Override + public ByteBufferSerializable toAvroDTO(final BusinessEvent rawEvent) { + final WorkingCapitalLoanAdjustTransactionBusinessEvent event = (WorkingCapitalLoanAdjustTransactionBusinessEvent) rawEvent; + final WorkingCapitalLoanTransactionDataV1 transactionToAdjust = mapper.map(event.get().getTransactionToAdjust()); + final WorkingCapitalLoanTransactionDataV1 newTransactionDetail = Optional.ofNullable(event.get().getNewTransactionDetail()) + .map(mapper::map).orElse(null); + return new WorkingCapitalLoanTransactionAdjustmentDataV1(transactionToAdjust, newTransactionDetail); + } + + @Override + public Class getSupportedSchema() { + return WorkingCapitalLoanTransactionAdjustmentDataV1.class; + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java index bfc19218e6a..7d95b2b7acd 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java @@ -118,21 +118,22 @@ public void givenAllConfigurationWhenValidatedThenValidationSuccessful() throws "SavingsAccountsStayedLockedBusinessEvent", "SavingsAccountForceWithdrawalBusinessEvent", "WorkingCapitalLoanDisbursalTransactionBusinessEvent", "WorkingCapitalLoanUndoDisbursalTransactionBusinessEvent", "WorkingCapitalLoanRepaymentTransactionBusinessEvent", "WorkingCapitalLoanDiscountFeeTransactionBusinessEvent", - "WorkingCapitalLoanDiscountFeeAdjustmentTransactionBusinessEvent", "WorkingCapitalLoanChargeAdjustmentPreBusinessEvent", - "WorkingCapitalLoanChargeAdjustmentPostBusinessEvent", "WorkingCapitalLoanCreditBalanceRefundTransactionBusinessEvent", - "WorkingCapitalLoanCreatedBusinessEvent", "WorkingCapitalLoanApplicationModifiedBusinessEvent", - "WorkingCapitalLoanApprovedBusinessEvent", "WorkingCapitalLoanUndoApprovalBusinessEvent", - "WorkingCapitalLoanRejectedBusinessEvent", "WorkingCapitalLoanDisbursalBusinessEvent", - "WorkingCapitalLoanUndoDisbursalBusinessEvent", "WorkingCapitalLoanStatusChangedBusinessEvent", - "WorkingCapitalLoanBalanceChangedBusinessEvent", "WorkingCapitalLoanDelinquencyRangeChangeBusinessEvent", - "WorkingCapitalLoanWrittenOffBusinessEvent", "WorkingCapitalLoanUndoWrittenOffBusinessEvent", - "WorkingCapitalLoanPeriodPaymentRateChangedBusinessEvent", "WorkingCapitalLoanDelinquencyScheduleChangedBusinessEvent", - "WorkingCapitalLoanDelinquencyDisableBusinessEvent", "WorkingCapitalLoanDelinquencyEnableBusinessEvent", - "WorkingCapitalLoanBreachScheduleChangedBusinessEvent", "WorkingCapitalLoanBreachDisableBusinessEvent", - "WorkingCapitalLoanBreachEnableBusinessEvent", "WorkingCapitalLoanChargeOffBusinessEvent", - "WorkingCapitalLoanFraudChangedBusinessEvent", "WorkingCapitalLoanPayoutRefundTransactionBusinessEvent", - "WorkingCapitalLoanGoodwillCreditTransactionBusinessEvent", "WorkingCapitalLoanTransactionReversedBusinessEvent", - "WorkingCapitalLoanChargeOffTransactionBusinessEvent", "WorkingCapitalLoanDiscountFeeAmortizationTransactionBusinessEvent", + "WorkingCapitalLoanDiscountFeeAdjustmentTransactionBusinessEvent", + "WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent", + "WorkingCapitalLoanCreditBalanceRefundTransactionBusinessEvent", "WorkingCapitalLoanCreatedBusinessEvent", + "WorkingCapitalLoanApplicationModifiedBusinessEvent", "WorkingCapitalLoanApprovedBusinessEvent", + "WorkingCapitalLoanUndoApprovalBusinessEvent", "WorkingCapitalLoanRejectedBusinessEvent", + "WorkingCapitalLoanDisbursalBusinessEvent", "WorkingCapitalLoanUndoDisbursalBusinessEvent", + "WorkingCapitalLoanStatusChangedBusinessEvent", "WorkingCapitalLoanBalanceChangedBusinessEvent", + "WorkingCapitalLoanDelinquencyRangeChangeBusinessEvent", "WorkingCapitalLoanWriteOffTransactionBusinessEvent", + "WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent", "WorkingCapitalLoanPeriodPaymentRateChangedBusinessEvent", + "WorkingCapitalLoanDelinquencyScheduleChangedBusinessEvent", "WorkingCapitalLoanDelinquencyDisableBusinessEvent", + "WorkingCapitalLoanDelinquencyEnableBusinessEvent", "WorkingCapitalLoanBreachScheduleChangedBusinessEvent", + "WorkingCapitalLoanBreachDisableBusinessEvent", "WorkingCapitalLoanBreachEnableBusinessEvent", + "WorkingCapitalLoanChargeOffBusinessEvent", "WorkingCapitalLoanFraudChangedBusinessEvent", + "WorkingCapitalLoanPayoutRefundTransactionBusinessEvent", "WorkingCapitalLoanGoodwillCreditTransactionBusinessEvent", + "WorkingCapitalLoanAdjustTransactionBusinessEvent", "WorkingCapitalLoanChargeOffTransactionBusinessEvent", + "WorkingCapitalLoanDiscountFeeAmortizationTransactionBusinessEvent", "WorkingCapitalLoanDiscountFeeAmortizationAdjustmentTransactionBusinessEvent", "WorkingCapitalLoanAddChargeBusinessEvent", "WorkingCapitalLoanJournalEntryCreatedBusinessEvent", "WorkingCapitalLoanBreachPastDueChangeBusinessEvent", "WorkingCapitalLoanUndoChargeOffBusinessEvent", "WorkingCapitalLoanBreachChangeBusinessEvent", @@ -141,7 +142,8 @@ public void givenAllConfigurationWhenValidatedThenValidationSuccessful() throws "WorkingCapitalLoanBreachResetBusinessEvent", "WorkingCapitalLoanBreachUndoResetBusinessEvent", "WorkingCapitalLoanDelinquencyPauseBusinessEvent", "WorkingCapitalLoanDelinquencyResumeBusinessEvent", "WorkingCapitalLoanDelinquencyRescheduleBusinessEvent", "WorkingCapitalLoanDelinquencyResetBusinessEvent", - "WorkingCapitalLoanDelinquencyUndoResetBusinessEvent"); + "WorkingCapitalLoanDelinquencyUndoResetBusinessEvent", "WorkingCapitalLoanAccrualTransactionBusinessEvent", + "WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent"); List tenants = List .of(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null)); diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java new file mode 100644 index 00000000000..09cb82eee22 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent.java @@ -0,0 +1,40 @@ +/** + * 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.infrastructure.event.business.domain.workingcapitalloan.transaction; + +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; + +public class WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { + + private static final String TYPE = "WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent"; + + public WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent(final WorkingCapitalLoanTransaction value) { + super(value); + } + + public WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent(final WorkingCapitalLoanTransaction value, + final Long aggregateRootId) { + super(value, aggregateRootId); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanWrittenOffBusinessEvent.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java similarity index 71% rename from fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanWrittenOffBusinessEvent.java rename to fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java index 3a7e426f8df..873dc510108 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanWrittenOffBusinessEvent.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAccrualTransactionBusinessEvent.java @@ -20,15 +20,15 @@ import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; -public class WorkingCapitalLoanWrittenOffBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { +public class WorkingCapitalLoanAccrualTransactionBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { - private static final String TYPE = "WorkingCapitalLoanWrittenOffBusinessEvent"; + private static final String TYPE = "WorkingCapitalLoanAccrualTransactionBusinessEvent"; - public WorkingCapitalLoanWrittenOffBusinessEvent(final WorkingCapitalLoanTransaction value) { + public WorkingCapitalLoanAccrualTransactionBusinessEvent(final WorkingCapitalLoanTransaction value) { super(value); } - public WorkingCapitalLoanWrittenOffBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { + public WorkingCapitalLoanAccrualTransactionBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { super(value, aggregateRootId); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java new file mode 100644 index 00000000000..c6f838bcf4f --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanAdjustTransactionBusinessEvent.java @@ -0,0 +1,69 @@ +/** + * 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.infrastructure.event.business.domain.workingcapitalloan.transaction; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.event.business.domain.AbstractBusinessEvent; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanTransactionData; + +/** + * Carries data snapshots rather than the transaction entity: external events are serialized in {@code beforeCommit}, by + * which time reprocessing has already mutated the allocation in place. + */ +public class WorkingCapitalLoanAdjustTransactionBusinessEvent + extends AbstractBusinessEvent { + + private static final String TYPE = "WorkingCapitalLoanAdjustTransactionBusinessEvent"; + private static final String CATEGORY = "WorkingCapitalLoan"; + + private final Long aggregateRootId; + + public WorkingCapitalLoanAdjustTransactionBusinessEvent(final Data value, final Long aggregateRootId) { + super(value); + this.aggregateRootId = aggregateRootId; + } + + @Override + public String getType() { + return TYPE; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Long getAggregateRootId() { + return aggregateRootId; + } + + @RequiredArgsConstructor + @Getter + public static class Data { + + private final WorkingCapitalLoanTransactionData transactionToAdjust; + private final WorkingCapitalLoanTransactionData newTransactionDetail; + + public static Data reversal(final WorkingCapitalLoanTransactionData reversedTransaction) { + return new Data(reversedTransaction, null); + } + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentPostBusinessEvent.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java similarity index 73% rename from fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentPostBusinessEvent.java rename to fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java index 92cbb5e355c..aee90959688 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentPostBusinessEvent.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent.java @@ -20,15 +20,16 @@ import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; -public class WorkingCapitalLoanChargeAdjustmentPostBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { +public class WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { - private static final String TYPE = "WorkingCapitalLoanChargeAdjustmentPostBusinessEvent"; + private static final String TYPE = "WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent"; - public WorkingCapitalLoanChargeAdjustmentPostBusinessEvent(final WorkingCapitalLoanTransaction value) { + public WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent(final WorkingCapitalLoanTransaction value) { super(value); } - public WorkingCapitalLoanChargeAdjustmentPostBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { + public WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent(final WorkingCapitalLoanTransaction value, + final Long aggregateRootId) { super(value, aggregateRootId); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanUndoWrittenOffBusinessEvent.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java similarity index 74% rename from fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanUndoWrittenOffBusinessEvent.java rename to fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java index 6e505b075c1..60132fe7883 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanUndoWrittenOffBusinessEvent.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.java @@ -20,15 +20,15 @@ import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; -public class WorkingCapitalLoanUndoWrittenOffBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { +public class WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { - private static final String TYPE = "WorkingCapitalLoanUndoWrittenOffBusinessEvent"; + private static final String TYPE = "WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent"; - public WorkingCapitalLoanUndoWrittenOffBusinessEvent(final WorkingCapitalLoanTransaction value) { + public WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent(final WorkingCapitalLoanTransaction value) { super(value); } - public WorkingCapitalLoanUndoWrittenOffBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { + public WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { super(value, aggregateRootId); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentPreBusinessEvent.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanWriteOffTransactionBusinessEvent.java similarity index 82% rename from fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentPreBusinessEvent.java rename to fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanWriteOffTransactionBusinessEvent.java index 8bbb8155446..d1151c47335 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanChargeAdjustmentPreBusinessEvent.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/workingcapitalloan/transaction/WorkingCapitalLoanWriteOffTransactionBusinessEvent.java @@ -20,15 +20,15 @@ import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; -public class WorkingCapitalLoanChargeAdjustmentPreBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { +public class WorkingCapitalLoanWriteOffTransactionBusinessEvent extends WorkingCapitalLoanTransactionBusinessEvent { - private static final String TYPE = "WorkingCapitalLoanChargeAdjustmentPreBusinessEvent"; + private static final String TYPE = "WorkingCapitalLoanWriteOffTransactionBusinessEvent"; - public WorkingCapitalLoanChargeAdjustmentPreBusinessEvent(final WorkingCapitalLoanTransaction value) { + public WorkingCapitalLoanWriteOffTransactionBusinessEvent(final WorkingCapitalLoanTransaction value) { super(value); } - public WorkingCapitalLoanChargeAdjustmentPreBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { + public WorkingCapitalLoanWriteOffTransactionBusinessEvent(final WorkingCapitalLoanTransaction value, final Long aggregateRootId) { super(value, aggregateRootId); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAdjustTransactionEventPublisher.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAdjustTransactionEventPublisher.java new file mode 100644 index 00000000000..f65304da498 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanAdjustTransactionEventPublisher.java @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.workingcapitalloan.service; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanAdjustTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanTransactionData; +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class WorkingCapitalLoanAdjustTransactionEventPublisher { + + private final WorkingCapitalLoanTransactionDataFactory transactionDataFactory; + private final BusinessEventNotifierService businessEventNotifierService; + + public Map snapshots(final List transactions) { + if (!isPostingEnabled()) { + return Map.of(); + } + return transactionDataFactory.create(transactions).stream() + .collect(Collectors.toMap(WorkingCapitalLoanTransactionData::getId, Function.identity())); + } + + public void publishReversal(final Long wcLoanId, final WorkingCapitalLoanTransaction reversedTransaction) { + if (!isPostingEnabled()) { + return; + } + final WorkingCapitalLoanTransactionData snapshot = transactionDataFactory.create(reversedTransaction); + businessEventNotifierService.notifyPostBusinessEvent(new WorkingCapitalLoanAdjustTransactionBusinessEvent( + WorkingCapitalLoanAdjustTransactionBusinessEvent.Data.reversal(snapshot), wcLoanId)); + } + + private boolean isPostingEnabled() { + return businessEventNotifierService.isExternalEventPostingEnabled(new WorkingCapitalLoanAdjustTransactionBusinessEvent(null, null)); + } + + /** The recording window makes the whole replay reach the consumer as one bulk event instead of N separate ones. */ + public void publishReprocessed(final Long wcLoanId, final List adjustments) { + if (adjustments.isEmpty()) { + return; + } + try { + businessEventNotifierService.startExternalEventRecording(); + adjustments.forEach(adjustment -> businessEventNotifierService.notifyPostBusinessEvent( + new WorkingCapitalLoanAdjustTransactionBusinessEvent(new WorkingCapitalLoanAdjustTransactionBusinessEvent.Data( + adjustment.previousState(), adjustment.currentState()), wcLoanId))); + businessEventNotifierService.stopExternalEventRecording(); + } catch (Exception e) { + businessEventNotifierService.resetEventRecording(); + throw e; + } + } + + public record WorkingCapitalLoanTransactionAdjustment(WorkingCapitalLoanTransactionData previousState, + WorkingCapitalLoanTransactionData currentState) { + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeAccrualService.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeAccrualService.java index e8609718084..a150efc248b 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeAccrualService.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeAccrualService.java @@ -25,6 +25,8 @@ import org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurationRepositoryWrapper; import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.service.MathUtil; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanAccrualTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.workingcapitalloan.accounting.WorkingCapitalLoanAccountingProcessor; @@ -55,6 +57,7 @@ public class WorkingCapitalLoanChargeAccrualService { private final WorkingCapitalLoanTransactionRelationRepository relationRepository; private final WorkingCapitalLoanAccountingProcessor accountingProcessor; private final WorkingCapitalLoanTransactionFinder transactionFinder; + private final BusinessEventNotifierService businessEventNotifierService; public void processOnChargeAdded(final WorkingCapitalLoan loan, final WorkingCapitalLoanCharge charge) { if (isAccrualPostingDisabled(loan)) { @@ -134,6 +137,9 @@ private void createChargeAccrualIfMissing(final WorkingCapitalLoan loan, final W allocationRepository.saveAndFlush(allocation); accountingProcessor.postJournalEntries(loan, accrualTransaction, allocation, transactionFinder.isAfterActiveChargeOffForAccountingRouting(loan, accrualTransaction)); + + businessEventNotifierService + .notifyPostBusinessEvent(new WorkingCapitalLoanAccrualTransactionBusinessEvent(accrualTransaction, loan.getId())); } private boolean isAlreadyAccrued(final WorkingCapitalLoanCharge charge) { diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeOffWriteServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeOffWriteServiceImpl.java index 85ebada71f1..c487f47c589 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeOffWriteServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeOffWriteServiceImpl.java @@ -37,7 +37,6 @@ import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanChargeOffBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanUndoChargeOffBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanChargeOffTransactionBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanTransactionReversedBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.workingcapitalloan.WorkingCapitalLoanConstants; @@ -76,6 +75,7 @@ public class WorkingCapitalLoanChargeOffWriteServiceImpl implements WorkingCapit private final ExternalIdFactory externalIdFactory; private final WorkingCapitalLoanAccountingProcessor accountingProcessor; private final BusinessEventNotifierService businessEventNotifierService; + private final WorkingCapitalLoanAdjustTransactionEventPublisher adjustTransactionEventPublisher; @Transactional @Override @@ -172,8 +172,7 @@ public CommandProcessingResult undoChargeOff(final Long loanId, final JsonComman this.loanRepository.saveAndFlush(loan); this.businessEventNotifierService.notifyPostBusinessEvent(new WorkingCapitalLoanUndoChargeOffBusinessEvent(loan)); - this.businessEventNotifierService - .notifyPostBusinessEvent(new WorkingCapitalLoanTransactionReversedBusinessEvent(chargeOffTransaction, loan.getId())); + this.adjustTransactionEventPublisher.publishReversal(loan.getId(), chargeOffTransaction); // Reverse the charge-off journal entries. No schedule reprocessing -- pure tag. if (loan.getLoanProduct().getAccountingRule().isAccrualWithDeferredRevenueAmortization()) { diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeWritePlatformServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeWritePlatformServiceImpl.java index 69c81d4236c..9143b0a3499 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeWritePlatformServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanChargeWritePlatformServiceImpl.java @@ -42,8 +42,7 @@ import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.charge.WorkingCapitalLoanAddChargeBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanBalanceChangedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanStatusChangedBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanChargeAdjustmentPostBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanChargeAdjustmentPreBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.charge.domain.Charge; @@ -356,9 +355,6 @@ public CommandProcessingResult adjustmentForLoanCharge(final Long loanId, final final WorkingCapitalLoanTransaction adjustmentTx = WorkingCapitalLoanTransaction.chargeAdjustment(loan, externalId, amount, transactionDate, paymentDetail); - businessEventNotifierService - .notifyPreBusinessEvent(new WorkingCapitalLoanChargeAdjustmentPreBusinessEvent(adjustmentTx, loan.getId())); - final WorkingCapitalLoanTransactionRelation relation = WorkingCapitalLoanTransactionRelation.linkToCharge(adjustmentTx, wcCharge, LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT); adjustmentTx.getLoanTransactionRelations().add(relation); @@ -376,7 +372,7 @@ public CommandProcessingResult adjustmentForLoanCharge(final Long loanId, final } businessEventNotifierService - .notifyPostBusinessEvent(new WorkingCapitalLoanChargeAdjustmentPostBusinessEvent(adjustmentTx, loan.getId())); + .notifyPostBusinessEvent(new WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent(adjustmentTx, loan.getId())); workingCapitalLoanRepository.saveAndFlush(loan); notifyBalanceChanged(loan); diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionDataFactory.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionDataFactory.java new file mode 100644 index 00000000000..ce9c96c58c7 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionDataFactory.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.workingcapitalloan.service; + +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanChargePaidByData; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanTransactionData; +import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanTransaction; +import org.apache.fineract.portfolio.workingcapitalloan.mapper.WorkingCapitalLoanTransactionMapper; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class WorkingCapitalLoanTransactionDataFactory { + + private final WorkingCapitalLoanTransactionMapper transactionMapper; + private final WorkingCapitalLoanChargePaidByReadService chargePaidByReadService; + + public WorkingCapitalLoanTransactionData create(final WorkingCapitalLoanTransaction transaction) { + return create(List.of(transaction)).getFirst(); + } + + public List create(final List transactions) { + final Map> chargePaidByByTransactionId = chargePaidByReadService + .fetchByTransactionIdsGrouped(transactions.stream().map(WorkingCapitalLoanTransaction::getId).toList()); + return transactions.stream().map(transaction -> { + final WorkingCapitalLoanTransactionData data = transactionMapper.toData(transaction); + data.setChargePaidByList(chargePaidByByTransactionId.getOrDefault(transaction.getId(), List.of())); + return data; + }).toList(); + } +} diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java index 4a89ed79ad3..f6f67f4529b 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanTransactionReprocessingServiceImpl.java @@ -31,10 +31,10 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanUndoChargeOffBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanTransactionReversedBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.workingcapitalloan.accounting.WorkingCapitalLoanAccountingProcessor; +import org.apache.fineract.portfolio.workingcapitalloan.data.WorkingCapitalLoanTransactionData; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoan; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanBalance; import org.apache.fineract.portfolio.workingcapitalloan.domain.WorkingCapitalLoanCharge; @@ -48,6 +48,7 @@ import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanRepository; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanTransactionAllocationRepository; import org.apache.fineract.portfolio.workingcapitalloan.repository.WorkingCapitalLoanTransactionRepository; +import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanAdjustTransactionEventPublisher.WorkingCapitalLoanTransactionAdjustment; import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanAmortizationScheduleWriteService.PrincipalAdjustment; import org.apache.fineract.portfolio.workingcapitalloan.service.WorkingCapitalLoanAmortizationScheduleWriteService.PrincipalPayment; import org.springframework.stereotype.Service; @@ -71,6 +72,7 @@ public class WorkingCapitalLoanTransactionReprocessingServiceImpl implements Wor private final WorkingCapitalLoanAmortizationScheduleWriteService amortizationScheduleWriteService; private final WorkingCapitalLoanAccountingProcessor accountingProcessor; private final BusinessEventNotifierService businessEventNotifierService; + private final WorkingCapitalLoanAdjustTransactionEventPublisher adjustTransactionEventPublisher; @Override public void reprocessTransactions(final WorkingCapitalLoan loan) { @@ -153,9 +155,13 @@ public void reprocessChargeFreeSuffix(final WorkingCapitalLoan loan, final Local WorkingCapitalLoanTransaction liftedChargeOffTransaction = null; final Map chargesById = Map.of(); final List updatedAllocations = new ArrayList<>(); + // The replay mutates the allocations in place, so the "before" side of the adjustment pair must be read now. + final Map preReplaySnapshots = captureSnapshots(replaySet); + final List adjustedTransactions = new ArrayList<>(); for (final WorkingCapitalLoanTransaction txn : replaySet) { if (isChargeOff(txn)) { - final boolean stillChargedOff = replayChargeOff(loan, balance, txn, accountingEnabled, updatedAllocations); + final boolean stillChargedOff = replayChargeOff(loan, balance, txn, accountingEnabled, updatedAllocations, + adjustedTransactions); afterChargeOff = stillChargedOff; afterLiftedChargeOff = !stillChargedOff; if (afterLiftedChargeOff) { @@ -180,6 +186,10 @@ public void reprocessChargeFreeSuffix(final WorkingCapitalLoan loan, final Local amortizationScheduleWriteService.applyRepayment(loan, txn.getTransactionDate(), allocated.plan().principalPortion()); } + if (allocated.allocationChanged() && allocated.hadStoredAllocation()) { + adjustedTransactions.add(txn); + } + if (accountingEnabled) { final boolean chargeOffRoutingMayHaveChanged = afterChargeOff || afterLiftedChargeOff; postOrRestateJournalEntries(loan, txn, allocated.allocation(), afterChargeOff, allocated.hadStoredAllocation(), @@ -190,6 +200,7 @@ public void reprocessChargeFreeSuffix(final WorkingCapitalLoan loan, final Local allocationRepository.saveAll(updatedAllocations); balanceRepository.saveAndFlush(balance); + adjustTransactionEventPublisher.publishReprocessed(loan.getId(), buildAdjustments(preReplaySnapshots, adjustedTransactions)); notifyChargeOffLifted(loan, liftedChargeOffTransaction); } @@ -224,6 +235,16 @@ private void reprocessFully(final WorkingCapitalLoan loan, final List replayable = allTransactions.stream().filter( + txn -> !txn.isReversed() && (txn.getTransactionType().isRepaymentType() || isCreditBalanceRefund(txn) || isChargeOff(txn))) + .sorted(TRANSACTION_ORDER).toList(); + + // Last point where the "before" side of the adjustment pair is readable: the charge-paid-by rows are deleted + // below and the allocations are mutated in place. + final Map preReplaySnapshots = captureSnapshots(replayable); + // Reset the paid distribution; the principal/fee/penalty totals stay, only how much of each is paid is // recomputed. The charge-paid-by rows are output for the old distribution, so drop them here and let the // reset+replay below rebuild them (mirrors the core module's clear-and-rewrite of LoanChargePaidBy). @@ -243,22 +264,18 @@ private void reprocessFully(final WorkingCapitalLoan loan, final List chargesById = charges.stream() .collect(Collectors.toMap(WorkingCapitalLoanCharge::getId, Function.identity())); - // Re-allocate every non-reversed repayment-like transaction (and replay each credit balance refund and - // charge-off) in chronological order. - final List replayable = allTransactions.stream().filter( - txn -> !txn.isReversed() && (txn.getTransactionType().isRepaymentType() || isCreditBalanceRefund(txn) || isChargeOff(txn))) - .sorted(TRANSACTION_ORDER).toList(); - final boolean accountingEnabled = loan.getLoanProduct().getAccountingRule().isAccrualWithDeferredRevenueAmortization(); final List principalPayments = new ArrayList<>(); final List principalAdjustments = new ArrayList<>(); final List updatedAllocations = new ArrayList<>(); + final List adjustedTransactions = new ArrayList<>(); boolean afterChargeOff = false; boolean afterLiftedChargeOff = false; WorkingCapitalLoanTransaction liftedChargeOffTransaction = null; for (final WorkingCapitalLoanTransaction txn : replayable) { if (isChargeOff(txn)) { - final boolean stillChargedOff = replayChargeOff(loan, balance, txn, accountingEnabled, updatedAllocations); + final boolean stillChargedOff = replayChargeOff(loan, balance, txn, accountingEnabled, updatedAllocations, + adjustedTransactions); afterChargeOff = stillChargedOff; afterLiftedChargeOff = !stillChargedOff; if (afterLiftedChargeOff) { @@ -300,6 +317,10 @@ private void reprocessFully(final WorkingCapitalLoan loan, final List captureSnapshots(final List transactions) { + return adjustTransactionEventPublisher + .snapshots(transactions.stream().filter(txn -> txn.getId() != null && txn.getAllocation() != null).toList()); + } + + private List buildAdjustments( + final Map preReplaySnapshots, + final List adjustedTransactions) { + final List paired = adjustedTransactions.stream() + .filter(txn -> preReplaySnapshots.containsKey(txn.getId())).toList(); + final Map postReplaySnapshots = adjustTransactionEventPublisher.snapshots(paired); + return paired.stream().map(txn -> new WorkingCapitalLoanTransactionAdjustment(preReplaySnapshots.get(txn.getId()), + postReplaySnapshots.get(txn.getId()))).toList(); } private boolean isCreditBalanceRefund(final WorkingCapitalLoanTransaction txn) { @@ -352,7 +392,8 @@ private void postOrRestateJournalEntries(final WorkingCapitalLoan loan, final Wo /** @return {@code false} when outstanding is zero and the charge-off is lifted. */ private boolean replayChargeOff(final WorkingCapitalLoan loan, final WorkingCapitalLoanBalance balance, final WorkingCapitalLoanTransaction chargeOffTransaction, final boolean accountingEnabled, - final List updatedAllocations) { + final List updatedAllocations, + final List adjustedTransactions) { final BigDecimal chargeOffAmount = balance.getTotalOutstanding(); final BigDecimal principalPortion = balance.getPrincipalOutstanding(); final BigDecimal feePortion = balance.getFeeOutstanding(); @@ -388,6 +429,12 @@ private boolean replayChargeOff(final WorkingCapitalLoan loan, final WorkingCapi updatedAllocations.add(storedAllocation); transactionRepository.save(chargeOffTransaction); + // Charge-off is the only branch that restates the transaction amount itself and not just its split, hence the + // amountChanged half of the condition. + if (amountChanged || allocationChanged) { + adjustedTransactions.add(chargeOffTransaction); + } + if (accountingEnabled && (amountChanged || allocationChanged)) { accountingProcessor.restateJournalEntries(loan, chargeOffTransaction, storedAllocation, true); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceImpl.java index 08c1b4d5d2c..cac2f157457 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceImpl.java @@ -38,8 +38,8 @@ import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanBalanceChangedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanStatusChangedBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanUndoWrittenOffBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanWrittenOffBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanWriteOffTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; @@ -140,7 +140,7 @@ public CommandProcessingResult writeOff(final Long loanId, final JsonCommand com this.accountingProcessor.postJournalEntries(loan, writeOffTransaction, allocation, loan.isChargedOff()); } this.businessEventNotifierService - .notifyPostBusinessEvent(new WorkingCapitalLoanWrittenOffBusinessEvent(writeOffTransaction, loan.getId())); + .notifyPostBusinessEvent(new WorkingCapitalLoanWriteOffTransactionBusinessEvent(writeOffTransaction, loan.getId())); notifyBalanceChanged(loan); notifyStatusChanged(loan, oldStatus); @@ -203,7 +203,7 @@ public CommandProcessingResult undoWriteOff(final Long loanId, final JsonCommand this.accountingProcessor.postReversalJournalEntries(loan, writeOffTransaction); } this.businessEventNotifierService - .notifyPostBusinessEvent(new WorkingCapitalLoanUndoWrittenOffBusinessEvent(writeOffTransaction, loan.getId())); + .notifyPostBusinessEvent(new WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent(writeOffTransaction, loan.getId())); notifyBalanceChanged(loan); notifyStatusChanged(loan, oldStatus); diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java index 156fce46652..77fc80fadeb 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWritePlatformServiceImpl.java @@ -53,6 +53,7 @@ import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanStatusChangedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanUndoApprovalBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanUndoDisbursalBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanCreditBalanceRefundTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanDisbursalTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanDiscountFeeAdjustmentTransactionBusinessEvent; @@ -60,7 +61,6 @@ import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanGoodwillCreditTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanPayoutRefundTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanRepaymentTransactionBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanTransactionReversedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanUndoDisbursalTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; @@ -122,6 +122,7 @@ public class WorkingCapitalLoanWritePlatformServiceImpl implements WorkingCapita private final WorkingCapitalLoanPeriodPaymentRateChangeRepository rateChangeRepository; private final WorkingCapitalLoanDiscountFeeAmortizationService discountFeeAmortizationService; private final WorkingCapitalLoanTransactionReprocessingService transactionReprocessingService; + private final WorkingCapitalLoanAdjustTransactionEventPublisher adjustTransactionEventPublisher; private final WorkingCapitalLoanChargeRepository chargeRepository; private final WorkingCapitalLoanDelinquencyRangeScheduleService delinquencyRangeScheduleService; private final WorkingCapitalLoanBreachScheduleService breachScheduleService; @@ -704,8 +705,7 @@ private CommandProcessingResult undoDiscountFeeAdjustment(final WorkingCapitalLo final String noteText = command.stringValueOfParameterNamed(WorkingCapitalLoanConstants.noteParamName); createNote(noteText, loan); - businessEventNotifierService - .notifyPostBusinessEvent(new WorkingCapitalLoanTransactionReversedBusinessEvent(adjustmentTransaction, loan.getId())); + adjustTransactionEventPublisher.publishReversal(loan.getId(), adjustmentTransaction); notifyBalanceChanged(loan); notifyStatusChanged(loan, oldStatus); @@ -1054,8 +1054,7 @@ public CommandProcessingResult undoTransaction(final WorkingCapitalLoan loan, fi handleNote(loan, command, changes); this.loanRepository.saveAndFlush(loan); - businessEventNotifierService - .notifyPostBusinessEvent(new WorkingCapitalLoanTransactionReversedBusinessEvent(transaction, loan.getId())); + adjustTransactionEventPublisher.publishReversal(loan.getId(), transaction); notifyBalanceChanged(loan); notifyStatusChanged(loan, oldStatus); @@ -1222,6 +1221,8 @@ private WorkingCapitalLoanTransaction reverseDisbursementTransactionAndResetBala // Reverse the journal entries of any charge accrual so the recognized income/receivable is backed out with the // disbursement; marking the transaction reversed alone would leave the GL postings in place. accrualsToReverse.forEach(accrual -> accountingProcessor.postReversalJournalEntries(loan, accrual)); + accrualsToReverse.forEach(accrual -> businessEventNotifierService + .notifyPostBusinessEvent(new WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent(accrual, loan.getId()))); // Operate on loan.getBalance() directly: it is the single managed balance instance that // recalculateRealizedIncome writes to, so all updates here apply to the same object that gets persisted. diff --git a/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml index e9ff8b41152..8fa0bb61f85 100644 --- a/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml +++ b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/module-changelog-master.xml @@ -91,4 +91,5 @@ + diff --git a/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0071_wc_transaction_business_events.xml b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0071_wc_transaction_business_events.xml new file mode 100644 index 00000000000..f44cc67f714 --- /dev/null +++ b/fineract-working-capital-loan/src/main/resources/db/changelog/tenant/module/workingcapitalloan/parts/0071_wc_transaction_business_events.xml @@ -0,0 +1,119 @@ + + + + + + + SELECT COUNT(*) FROM m_external_event_configuration WHERE type = 'WorkingCapitalLoanAccrualTransactionBusinessEvent' + + + + + + + + + + SELECT COUNT(*) FROM m_external_event_configuration WHERE type = 'WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent' + + + + + + + + + + SELECT COUNT(*) FROM m_external_event_configuration WHERE type = 'WorkingCapitalLoanAdjustTransactionBusinessEvent' + + + + + + + + + + type = 'WorkingCapitalLoanTransactionReversedBusinessEvent' + + + + + + + type = 'WorkingCapitalLoanWrittenOffBusinessEvent' + + + + + + SELECT COUNT(*) FROM m_external_event_configuration WHERE type = 'WorkingCapitalLoanWriteOffTransactionBusinessEvent' + + + + + + + + + + + type = 'WorkingCapitalLoanUndoWrittenOffBusinessEvent' + + + + + + SELECT COUNT(*) FROM m_external_event_configuration WHERE type = 'WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent' + + + + + + + + + + + type = 'WorkingCapitalLoanChargeAdjustmentPostBusinessEvent' + + + + + + SELECT COUNT(*) FROM m_external_event_configuration WHERE type = 'WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent' + + + + + + + + + + type = 'WorkingCapitalLoanChargeAdjustmentPreBusinessEvent' + + + + diff --git a/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceTest.java b/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceTest.java index fdf2b68b6fa..c5bad4bc3bb 100644 --- a/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceTest.java +++ b/fineract-working-capital-loan/src/test/java/org/apache/fineract/portfolio/workingcapitalloan/service/WorkingCapitalLoanWriteOffWriteServiceTest.java @@ -46,8 +46,8 @@ import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanBalanceChangedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.loan.WorkingCapitalLoanStatusChangedBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanUndoWrittenOffBusinessEvent; -import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanWrittenOffBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.workingcapitalloan.transaction.WorkingCapitalLoanWriteOffTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; @@ -166,7 +166,7 @@ void writeOffReprocessesDelinquencyAndPublishesBalanceAndStatusEvents() { verify(delinquencyRangeScheduleService).reprocessDelinquencySchedule(loan); final List> events = publishedEvents(3); - assertThat(events.get(0)).isInstanceOf(WorkingCapitalLoanWrittenOffBusinessEvent.class); + assertThat(events.get(0)).isInstanceOf(WorkingCapitalLoanWriteOffTransactionBusinessEvent.class); assertThat(events.get(1)).isInstanceOf(WorkingCapitalLoanBalanceChangedBusinessEvent.class); assertThat(events.get(2)).isInstanceOf(WorkingCapitalLoanStatusChangedBusinessEvent.class); } @@ -181,7 +181,7 @@ void undoWriteOffReprocessesDelinquencyAndPublishesBalanceAndStatusEvents() { verify(delinquencyRangeScheduleService).reprocessDelinquencySchedule(loan); final List> events = publishedEvents(3); - assertThat(events.get(0)).isInstanceOf(WorkingCapitalLoanUndoWrittenOffBusinessEvent.class); + assertThat(events.get(0)).isInstanceOf(WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent.class); assertThat(events.get(1)).isInstanceOf(WorkingCapitalLoanBalanceChangedBusinessEvent.class); assertThat(events.get(2)).isInstanceOf(WorkingCapitalLoanStatusChangedBusinessEvent.class); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/ExternalEventConfigurationTestData.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/ExternalEventConfigurationTestData.java index 9b64b52696a..d24e76d0218 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/ExternalEventConfigurationTestData.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/modules/ExternalEventConfigurationTestData.java @@ -164,8 +164,7 @@ public final class ExternalEventConfigurationTestData { "SavingsAccountForceWithdrawalBusinessEvent", // "WorkingCapitalLoanDiscountFeeTransactionBusinessEvent", // "WorkingCapitalLoanDiscountFeeAdjustmentTransactionBusinessEvent", // - "WorkingCapitalLoanChargeAdjustmentPreBusinessEvent", // - "WorkingCapitalLoanChargeAdjustmentPostBusinessEvent", // + "WorkingCapitalLoanChargeAdjustmentTransactionBusinessEvent", // "WorkingCapitalLoanCreatedBusinessEvent", // "WorkingCapitalLoanApplicationModifiedBusinessEvent", // "WorkingCapitalLoanApprovedBusinessEvent", // @@ -176,8 +175,8 @@ public final class ExternalEventConfigurationTestData { "WorkingCapitalLoanStatusChangedBusinessEvent", // "WorkingCapitalLoanBalanceChangedBusinessEvent", // "WorkingCapitalLoanDelinquencyRangeChangeBusinessEvent", // - "WorkingCapitalLoanWrittenOffBusinessEvent", // - "WorkingCapitalLoanUndoWrittenOffBusinessEvent", // + "WorkingCapitalLoanWriteOffTransactionBusinessEvent", // + "WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent", // "WorkingCapitalLoanPeriodPaymentRateChangedBusinessEvent", // "WorkingCapitalLoanDelinquencyScheduleChangedBusinessEvent", // "WorkingCapitalLoanDelinquencyDisableBusinessEvent", // @@ -189,7 +188,7 @@ public final class ExternalEventConfigurationTestData { "WorkingCapitalLoanFraudChangedBusinessEvent", // "WorkingCapitalLoanPayoutRefundTransactionBusinessEvent", // "WorkingCapitalLoanGoodwillCreditTransactionBusinessEvent", // - "WorkingCapitalLoanTransactionReversedBusinessEvent", // + "WorkingCapitalLoanAdjustTransactionBusinessEvent", // "WorkingCapitalLoanChargeOffTransactionBusinessEvent", // "WorkingCapitalLoanDiscountFeeAmortizationTransactionBusinessEvent", // "WorkingCapitalLoanDiscountFeeAmortizationAdjustmentTransactionBusinessEvent", // @@ -208,7 +207,9 @@ public final class ExternalEventConfigurationTestData { "WorkingCapitalLoanDelinquencyResumeBusinessEvent", // "WorkingCapitalLoanDelinquencyRescheduleBusinessEvent", // "WorkingCapitalLoanDelinquencyResetBusinessEvent", // - "WorkingCapitalLoanDelinquencyUndoResetBusinessEvent"// + "WorkingCapitalLoanDelinquencyUndoResetBusinessEvent", // + "WorkingCapitalLoanAccrualTransactionBusinessEvent", // + "WorkingCapitalLoanAccrualAdjustmentTransactionBusinessEvent"// ); private ExternalEventConfigurationTestData() {}