From ae20c489a99dc9338f81ea72cdf9aee9b96a235a Mon Sep 17 00:00:00 2001 From: mansimaurya Date: Thu, 9 Jul 2026 17:18:39 +0530 Subject: [PATCH 1/2] FINERACT-2647: client module depends only on fineract-core/command via Spring Modulith boundary test --- .../service/ClientChargeReadServiceImpl.java | 40 +++++ .../AccountNumberFormatRepositoryWrapper.java | 0 .../AccountNumberFormatNotFoundException.java | 0 ...tyDatatableChecksWritePlatformService.java | 0 .../client/ClientActivateBusinessEvent.java | 0 .../domain/client/ClientBusinessEvent.java | 0 .../client/ClientCreateBusinessEvent.java | 0 .../client/ClientRejectBusinessEvent.java | 0 .../holiday/domain/HolidayRepository.java | 0 .../domain/HolidayRepositoryWrapper.java | 0 .../exception/HolidayNotFoundException.java | 0 .../staff/domain/StaffRepository.java | 0 .../staff/domain/StaffRepositoryWrapper.java | 0 .../staff/service/StaffReadService.java | 0 .../domain/WorkingDaysRepository.java | 0 .../domain/WorkingDaysRepositoryWrapper.java | 0 .../WorkingDaysNotFoundException.java | 0 .../address/data/ClientAddressData.java | 0 .../portfolio/address/domain/Address.java | 0 .../filter/ClientAddressSearchParam.java | 0 .../service/AddressReadPlatformService.java | 0 .../service/AddressWritePlatformService.java | 0 .../charge/domain/ChargeAppliesTo.java | 0 .../charge/domain/ChargeCalculationType.java | 0 .../charge/domain/ChargePaymentMode.java | 0 .../ChargeCannotBeAppliedToException.java | 0 .../exception/ChargeNotFoundException.java | 0 .../charge/service/ChargeEnumerations.java | 0 .../ClientAccountSummaryReadService.java | 31 ++++ .../ClientApplicableChargeReadService.java | 32 ++++ .../contract/ClientChargeDefinitionData.java | 29 ++++ .../contract/ClientChargeReadService.java | 28 ++++ .../ClientDatatableChecksReadService.java | 33 ++++ .../ClientJournalEntryWriteService.java | 31 ++++ .../contract/ClientLoanReadService.java | 31 ++++ .../contract/ClientNoteWriteService.java | 30 ++++ .../contract/ClientObligeeReadService.java | 32 ++++ ...ClientSavingsAccountLookupReadService.java | 32 ++++ .../ClientSavingsActivationService.java | 32 ++++ ...ClientSavingsProductLookupReadService.java | 32 ++++ .../contract/ClientSavingsReadService.java | 44 +++++ .../client/data/ClientLoanStatusData.java | 28 ++++ .../client/domain/ClientAddress.java | 0 .../service/ClientCollateralReadService.java | 31 ++++ .../group/domain/GroupRepository.java | 0 ...erCountNotInPermissibleRangeException.java | 0 .../exception/GroupNotFoundException.java | 0 .../guarantor/data/ObligeeData.java | 0 .../service/ClientLoanReadServiceImpl.java | 45 ++++++ fineract-provider/build.gradle | 7 +- fineract-provider/dependencies.gradle | 4 +- .../JournalEntryWritePlatformService.java | 5 +- .../EntityDatatableChecksReadService.java | 7 +- ...sReadPlatformServiceJpaRepositoryImpl.java | 15 +- .../ChargeReadPlatformServiceImpl.java | 3 +- .../client/api/ClientAddressApiResource.java | 4 +- .../client/api/ClientChargesApiResource.java | 4 +- .../client/api/ClientsApiResource.java | 22 ++- .../portfolio/client/domain/ClientCharge.java | 27 ++-- .../domain/ClientChargeRepositoryWrapper.java | 9 +- .../client/domain/ClientTransaction.java | 16 +- .../portfolio/client/package-info.java | 27 ++++ .../ClientChargeWritePlatformServiceImpl.java | 39 +++-- .../ClientReadPlatformServiceImpl.java | 24 +-- ...ClientTemplateReadPlatformServiceImpl.java | 11 +- ...WritePlatformServiceJpaRepositoryImpl.java | 26 ++- ...WritePlatformServiceJpaRepositoryImpl.java | 68 +++----- .../ClientCollateralReadServiceImpl.java | 52 ++++++ .../GuarantorReadPlatformServiceImpl.java | 3 +- .../service/NoteWritePlatformServiceImpl.java | 11 +- ...SavingsAccountReadPlatformServiceImpl.java | 8 +- ...SavingsProductReadPlatformServiceImpl.java | 8 +- .../ClientCrossFeatureBoundaryTest.java | 152 ++++++++++++++++++ .../ClientReadPlatformServiceImplTest.java | 6 +- .../ClientSavingsActivationServiceImpl.java | 48 ++++++ .../service/ClientSavingsReadServiceImpl.java | 58 +++++++ 76 files changed, 1081 insertions(+), 144 deletions(-) create mode 100644 fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/service/ClientChargeReadServiceImpl.java rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/AccountNumberFormatRepositoryWrapper.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/exception/AccountNumberFormatNotFoundException.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformService.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientActivateBusinessEvent.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientBusinessEvent.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCreateBusinessEvent.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientRejectBusinessEvent.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepository.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepositoryWrapper.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/holiday/exception/HolidayNotFoundException.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepository.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepositoryWrapper.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadService.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepository.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/organisation/workingdays/exception/WorkingDaysNotFoundException.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/address/data/ClientAddressData.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/address/domain/Address.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/address/filter/ClientAddressSearchParam.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/address/service/AddressReadPlatformService.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/address/service/AddressWritePlatformService.java (100%) rename {fineract-charge => fineract-core}/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java (100%) rename {fineract-charge => fineract-core}/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java (100%) rename {fineract-charge => fineract-core}/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargePaymentMode.java (100%) rename {fineract-charge => fineract-core}/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeCannotBeAppliedToException.java (100%) rename {fineract-charge => fineract-core}/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeNotFoundException.java (100%) rename {fineract-charge => fineract-core}/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java (100%) create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientAccountSummaryReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientApplicableChargeReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeDefinitionData.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientDatatableChecksReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientJournalEntryWriteService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientLoanReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientNoteWriteService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientObligeeReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsAccountLookupReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsActivationService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsProductLookupReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientLoanStatusData.java rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/client/domain/ClientAddress.java (100%) create mode 100644 fineract-core/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadService.java rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/group/domain/GroupRepository.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/group/exception/GroupMemberCountNotInPermissibleRangeException.java (100%) rename {fineract-provider => fineract-core}/src/main/java/org/apache/fineract/portfolio/group/exception/GroupNotFoundException.java (100%) rename {fineract-loan => fineract-core}/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/ObligeeData.java (100%) create mode 100644 fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ClientLoanReadServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/client/package-info.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadServiceImpl.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java create mode 100644 fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsActivationServiceImpl.java create mode 100644 fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsReadServiceImpl.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/service/ClientChargeReadServiceImpl.java b/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/service/ClientChargeReadServiceImpl.java new file mode 100644 index 00000000000..0e57218e739 --- /dev/null +++ b/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/service/ClientChargeReadServiceImpl.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.portfolio.charge.service; + +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.charge.domain.Charge; +import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; +import org.apache.fineract.portfolio.client.contract.ClientChargeDefinitionData; +import org.apache.fineract.portfolio.client.contract.ClientChargeReadService; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ClientChargeReadServiceImpl implements ClientChargeReadService { + + private final ChargeRepositoryWrapper chargeRepository; + + @Override + public ClientChargeDefinitionData retrieveClientChargeDefinition(final Long chargeId) { + final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(chargeId); + return new ClientChargeDefinitionData(charge.getId(), charge.isClientCharge(), charge.isPenalty(), charge.getChargeTimeType(), + charge.getChargeCalculation(), charge.getAmount(), charge.getCurrencyCode(), charge.getIncomeAccountId()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/AccountNumberFormatRepositoryWrapper.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/AccountNumberFormatRepositoryWrapper.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/AccountNumberFormatRepositoryWrapper.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/domain/AccountNumberFormatRepositoryWrapper.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/exception/AccountNumberFormatNotFoundException.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/exception/AccountNumberFormatNotFoundException.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/exception/AccountNumberFormatNotFoundException.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/accountnumberformat/exception/AccountNumberFormatNotFoundException.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformService.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformService.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformService.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksWritePlatformService.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientActivateBusinessEvent.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientActivateBusinessEvent.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientActivateBusinessEvent.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientActivateBusinessEvent.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientBusinessEvent.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientBusinessEvent.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientBusinessEvent.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientBusinessEvent.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCreateBusinessEvent.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCreateBusinessEvent.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCreateBusinessEvent.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCreateBusinessEvent.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientRejectBusinessEvent.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientRejectBusinessEvent.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientRejectBusinessEvent.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientRejectBusinessEvent.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepository.java b/fineract-core/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepository.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepository.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepository.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepositoryWrapper.java b/fineract-core/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepositoryWrapper.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepositoryWrapper.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/holiday/domain/HolidayRepositoryWrapper.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/exception/HolidayNotFoundException.java b/fineract-core/src/main/java/org/apache/fineract/organisation/holiday/exception/HolidayNotFoundException.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/holiday/exception/HolidayNotFoundException.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/holiday/exception/HolidayNotFoundException.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepository.java b/fineract-core/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepository.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepository.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepository.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepositoryWrapper.java b/fineract-core/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepositoryWrapper.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepositoryWrapper.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/staff/domain/StaffRepositoryWrapper.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadService.java b/fineract-core/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadService.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadService.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/staff/service/StaffReadService.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepository.java b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepository.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepository.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepository.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/domain/WorkingDaysRepositoryWrapper.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/exception/WorkingDaysNotFoundException.java b/fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/exception/WorkingDaysNotFoundException.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/organisation/workingdays/exception/WorkingDaysNotFoundException.java rename to fineract-core/src/main/java/org/apache/fineract/organisation/workingdays/exception/WorkingDaysNotFoundException.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/data/ClientAddressData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/address/data/ClientAddressData.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/address/data/ClientAddressData.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/address/data/ClientAddressData.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/domain/Address.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/address/domain/Address.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/address/domain/Address.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/address/domain/Address.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/filter/ClientAddressSearchParam.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/address/filter/ClientAddressSearchParam.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/address/filter/ClientAddressSearchParam.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/address/filter/ClientAddressSearchParam.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/service/AddressReadPlatformService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/address/service/AddressReadPlatformService.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/address/service/AddressReadPlatformService.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/address/service/AddressReadPlatformService.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/service/AddressWritePlatformService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/address/service/AddressWritePlatformService.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/address/service/AddressWritePlatformService.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/address/service/AddressWritePlatformService.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java similarity index 100% rename from fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeAppliesTo.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java similarity index 100% rename from fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargeCalculationType.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargePaymentMode.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargePaymentMode.java similarity index 100% rename from fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargePaymentMode.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/charge/domain/ChargePaymentMode.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeCannotBeAppliedToException.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeCannotBeAppliedToException.java similarity index 100% rename from fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeCannotBeAppliedToException.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeCannotBeAppliedToException.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeNotFoundException.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeNotFoundException.java similarity index 100% rename from fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeNotFoundException.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/charge/exception/ChargeNotFoundException.java diff --git a/fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java similarity index 100% rename from fineract-charge/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeEnumerations.java diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientAccountSummaryReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientAccountSummaryReadService.java new file mode 100644 index 00000000000..7c94063828f --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientAccountSummaryReadService.java @@ -0,0 +1,31 @@ +/** + * 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.client.contract; + +import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; + +/** + * Core read-contract returning a client's account summary already serialized to JSON, so that the client feature does + * not need a compile-time dependency on {@code AccountSummaryCollectionData} (which transitively references the loan + * and shares domain types). Implemented by the account-details module. + */ +public interface ClientAccountSummaryReadService { + + String retrieveClientAccountSummaryAsJson(Long clientId, ApiRequestJsonSerializationSettings settings); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientApplicableChargeReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientApplicableChargeReadService.java new file mode 100644 index 00000000000..648ccbab478 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientApplicableChargeReadService.java @@ -0,0 +1,32 @@ +/** + * 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.client.contract; + +import java.util.List; +import org.apache.fineract.portfolio.charge.data.ChargeData; + +/** + * Core read-contract exposing only the "charges applicable to clients" lookup required by the client feature, so that + * the client module does not need a compile-time dependency on the full {@code ChargeReadPlatformService}. Implemented + * by the charge read service. + */ +public interface ClientApplicableChargeReadService { + + List retrieveAllChargesApplicableToClients(); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeDefinitionData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeDefinitionData.java new file mode 100644 index 00000000000..e5386dfe546 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeDefinitionData.java @@ -0,0 +1,29 @@ +/** + * 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.client.contract; + +import java.math.BigDecimal; + +/** + * Minimal projection of a charge definition needed by the client feature to create/inspect a {@code ClientCharge} + * without a compile-time dependency on the charge domain types. Populated by the charge module. + */ +public record ClientChargeDefinitionData(Long chargeId, boolean applicableToClients, boolean penalty, Integer chargeTimeType, + Integer chargeCalculation, BigDecimal amount, String currencyCode, Long incomeAccountId) { +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeReadService.java new file mode 100644 index 00000000000..334a970b0c8 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientChargeReadService.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.contract; + +/** + * Core read-contract exposing the charge-definition data required by the client feature, so that the client module does + * not need a compile-time dependency on the charge domain/repository types. Implemented by the charge module. + */ +public interface ClientChargeReadService { + + ClientChargeDefinitionData retrieveClientChargeDefinition(Long chargeId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientDatatableChecksReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientDatatableChecksReadService.java new file mode 100644 index 00000000000..f01dbc65a6a --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientDatatableChecksReadService.java @@ -0,0 +1,33 @@ +/** + * 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.client.contract; + +import java.util.List; +import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; + +/** + * Core read-contract exposing only the datatable-template lookup required by the client feature, so that the client + * module does not need a compile-time dependency on the full {@code EntityDatatableChecksReadService} (whose other + * methods reference {@code EntityDataTableChecksTemplateData} -> loan product data). Implemented by the data-queries + * module. + */ +public interface ClientDatatableChecksReadService { + + List retrieveTemplates(Integer status, String entity, Long productId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientJournalEntryWriteService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientJournalEntryWriteService.java new file mode 100644 index 00000000000..a9fd2c991d3 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientJournalEntryWriteService.java @@ -0,0 +1,31 @@ +/** + * 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.client.contract; + +import java.util.Map; + +/** + * Core write-contract exposing only the journal-entry posting behaviour required by the client feature, so that the + * client module does not need a compile-time dependency on the full {@code JournalEntryWritePlatformService} (which + * references loan/investor/provisioning types). Implemented by the accounting module. + */ +public interface ClientJournalEntryWriteService { + + void createJournalEntriesForClientTransactions(Map accountingBridgeData); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientLoanReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientLoanReadService.java new file mode 100644 index 00000000000..fc63a548898 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientLoanReadService.java @@ -0,0 +1,31 @@ +/** + * 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.client.contract; + +import java.util.List; +import org.apache.fineract.portfolio.client.data.ClientLoanStatusData; + +/** + * Core read-contract returning the loan-status projection of a client's loans, so that the client feature does not need + * a compile-time dependency on the loan domain/repository types. Implemented by the loan module. + */ +public interface ClientLoanReadService { + + List retrieveClientLoanStatuses(Long clientId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientNoteWriteService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientNoteWriteService.java new file mode 100644 index 00000000000..75fd279b79c --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientNoteWriteService.java @@ -0,0 +1,30 @@ +/** + * 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.client.contract; + +import org.apache.fineract.portfolio.client.domain.Client; + +/** + * Core write-contract exposing only the note cleanup behaviour required by the client feature, so that the client + * module does not need a compile-time dependency on the note domain/repository types. Implemented by the note module. + */ +public interface ClientNoteWriteService { + + void deleteNotesByClient(Client client); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientObligeeReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientObligeeReadService.java new file mode 100644 index 00000000000..c1949446c96 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientObligeeReadService.java @@ -0,0 +1,32 @@ +/** + * 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.client.contract; + +import java.util.List; +import org.apache.fineract.portfolio.loanaccount.guarantor.data.ObligeeData; + +/** + * Core read-contract exposing only the obligee lookup required by the client feature, so that the client feature does + * not need a compile-time dependency on the full {@code GuarantorReadPlatformService}. Implemented by the guarantor + * module. + */ +public interface ClientObligeeReadService { + + List retrieveObligeeDetails(Long clientId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsAccountLookupReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsAccountLookupReadService.java new file mode 100644 index 00000000000..d328d92a925 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsAccountLookupReadService.java @@ -0,0 +1,32 @@ +/** + * 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.client.contract; + +import java.util.Collection; +import org.apache.fineract.portfolio.savings.data.SavingsAccountData; + +/** + * Core read-contract exposing only the savings-account lookup required by the client feature, so that the client module + * does not need a compile-time dependency on the full {@code SavingsAccountReadPlatformService}. Implemented by the + * savings account read service. + */ +public interface ClientSavingsAccountLookupReadService { + + Collection retrieveSavingsAccountsForClientLookup(Long clientId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsActivationService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsActivationService.java new file mode 100644 index 00000000000..56aaa21aa8a --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsActivationService.java @@ -0,0 +1,32 @@ +/** + * 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.client.contract; + +import java.time.format.DateTimeFormatter; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.client.domain.Client; + +/** + * Core write-contract that creates the default savings account for a client being activated, so that the client module + * does not need a compile-time dependency on the savings application-process types. Implemented by the savings module. + */ +public interface ClientSavingsActivationService { + + CommandProcessingResult createSavingsForClientActivation(Client client, DateTimeFormatter fmt); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsProductLookupReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsProductLookupReadService.java new file mode 100644 index 00000000000..56a47103e38 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsProductLookupReadService.java @@ -0,0 +1,32 @@ +/** + * 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.client.contract; + +import java.util.Collection; +import org.apache.fineract.portfolio.savings.data.SavingsProductData; + +/** + * Core read-contract exposing only the savings-product lookup required by the client feature, so that the client module + * does not need a compile-time dependency on the full {@code SavingsProductReadPlatformService}. Implemented by the + * savings product read service. + */ +public interface ClientSavingsProductLookupReadService { + + Collection retrieveAllSavingsProductsForClientLookup(); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsReadService.java new file mode 100644 index 00000000000..a707a0356df --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/contract/ClientSavingsReadService.java @@ -0,0 +1,44 @@ +/** + * 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.client.contract; + +/** + * Core read-contract exposing only the savings checks required by the client feature, so that the client module does + * not need a compile-time dependency on the savings domain/repository types. Implemented by the savings module. + */ +public interface ClientSavingsReadService { + + /** + * Validates that a savings product with the given id exists, throwing {@code SavingsProductNotFoundException} if + * not. + */ + void validateSavingsProductExists(Long savingsProductId); + + /** + * Returns {@code true} if the client has any savings account that is active, approved or submitted-and-pending, + * i.e. a non-closed savings account that prevents the client from being closed. + */ + boolean hasNonClosedSavingsAccountsForClient(Long clientId); + + /** + * Returns {@code true} if the savings account with the given id belongs to the given client. Throws + * {@code SavingsAccountNotFoundException} if the savings account does not exist. + */ + boolean isSavingsAccountForClient(Long savingsId, Long clientId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientLoanStatusData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientLoanStatusData.java new file mode 100644 index 00000000000..7132837ebbd --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientLoanStatusData.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.client.data; + +import java.time.LocalDate; + +/** + * Minimal projection of a client's loan needed to decide whether the client can be closed, so that the client feature + * does not need a compile-time dependency on the loan domain types. Populated by the loan module. + */ +public record ClientLoanStatusData(Integer statusId, LocalDate closedOnDate) { +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientAddress.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/domain/ClientAddress.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientAddress.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/client/domain/ClientAddress.java diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadService.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadService.java new file mode 100644 index 00000000000..9962598ca80 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadService.java @@ -0,0 +1,31 @@ +/** + * 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.collateralmanagement.service; + +import java.util.Set; +import org.apache.fineract.portfolio.client.data.ClientCollateralManagementData; + +/** + * Core read-contract returning a client's collateral as DTOs, so that the client feature does not need a compile-time + * dependency on the collateral-management domain/repository types. Implemented by the collateral-management module. + */ +public interface ClientCollateralReadService { + + Set retrieveCollateralDataForClient(Long clientId); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/domain/GroupRepository.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/group/domain/GroupRepository.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/group/domain/GroupRepository.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/group/domain/GroupRepository.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/exception/GroupMemberCountNotInPermissibleRangeException.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/group/exception/GroupMemberCountNotInPermissibleRangeException.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/group/exception/GroupMemberCountNotInPermissibleRangeException.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/group/exception/GroupMemberCountNotInPermissibleRangeException.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/exception/GroupNotFoundException.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/group/exception/GroupNotFoundException.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/portfolio/group/exception/GroupNotFoundException.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/group/exception/GroupNotFoundException.java diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/ObligeeData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/ObligeeData.java similarity index 100% rename from fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/ObligeeData.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/data/ObligeeData.java diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ClientLoanReadServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ClientLoanReadServiceImpl.java new file mode 100644 index 00000000000..51f59e216f9 --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ClientLoanReadServiceImpl.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanaccount.service; + +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.client.contract.ClientLoanReadService; +import org.apache.fineract.portfolio.client.data.ClientLoanStatusData; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ClientLoanReadServiceImpl implements ClientLoanReadService { + + private final LoanRepositoryWrapper loanRepositoryWrapper; + + @Override + public List retrieveClientLoanStatuses(final Long clientId) { + final List clientLoans = this.loanRepositoryWrapper.findLoanByClientId(clientId); + final List result = new ArrayList<>(); + for (final Loan loan : clientLoans) { + result.add(new ClientLoanStatusData(loan.getStatus().getValue(), loan.getClosedOnDate())); + } + return result; + } +} diff --git a/fineract-provider/build.gradle b/fineract-provider/build.gradle index 9043db3e9fe..9c9102e91d9 100644 --- a/fineract-provider/build.gradle +++ b/fineract-provider/build.gradle @@ -145,6 +145,11 @@ dependencies { apply from: 'dependencies.gradle' +test { + maxHeapSize = '2g' + jvmArgs += ['-XX:MaxMetaspaceSize=1g'] +} + // Configuration for the modernizer plugin // https://github.com/andygoossens/gradle-modernizer-plugin modernizer { @@ -450,4 +455,4 @@ tasks.register('devRun', org.springframework.boot.gradle.tasks.run.BootRun) { doFirst { println "Running in development mode - quality checks are disabled" } -} +} \ No newline at end of file diff --git a/fineract-provider/dependencies.gradle b/fineract-provider/dependencies.gradle index 669f7b94e19..477f1bd1958 100644 --- a/fineract-provider/dependencies.gradle +++ b/fineract-provider/dependencies.gradle @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +ext['spring-modulith.version'] = '1.4.11' dependencies { modules { module("io.swagger.core.v3:swagger-annotations") { @@ -25,6 +25,8 @@ dependencies { } implementation(project(path: ':fineract-core')) + implementation 'org.springframework.modulith:spring-modulith-api' + testImplementation 'org.springframework.modulith:spring-modulith-core' implementation(project(path: ':fineract-cob')) implementation(project(path: ':fineract-command')) implementation(project(path: ':fineract-command-audit')) diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java index c01e543e9e9..0f9086d43b1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java @@ -26,11 +26,12 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.investor.domain.ExternalAssetOwner; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer; +import org.apache.fineract.portfolio.client.contract.ClientJournalEntryWriteService; import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -public interface JournalEntryWritePlatformService { +public interface JournalEntryWritePlatformService extends ClientJournalEntryWriteService { CommandProcessingResult createJournalEntry(JsonCommand command); @@ -40,8 +41,6 @@ public interface JournalEntryWritePlatformService { void createJournalEntriesForSavings(Map accountingBridgeData); - void createJournalEntriesForClientTransactions(Map accountingBridgeData); - CommandProcessingResult defineOpeningBalance(JsonCommand command); void createJournalEntryForReversedLoanTransaction(LocalDate transactionDate, String loanTransactionId, Long officeId); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadService.java index 89b604e75f8..e22e6997805 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/EntityDatatableChecksReadService.java @@ -18,18 +18,15 @@ */ package org.apache.fineract.infrastructure.dataqueries.service; -import java.util.List; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; -import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; import org.apache.fineract.infrastructure.dataqueries.data.EntityDataTableChecksData; import org.apache.fineract.infrastructure.dataqueries.data.EntityDataTableChecksTemplateData; +import org.apache.fineract.portfolio.client.contract.ClientDatatableChecksReadService; -public interface EntityDatatableChecksReadService { +public interface EntityDatatableChecksReadService extends ClientDatatableChecksReadService { EntityDataTableChecksTemplateData retrieveTemplate(); - List retrieveTemplates(Integer status, String entity, Long productId); - Page retrieveAll(SearchParameters searchParameters, Integer status, String entity, Long productId); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java index a93756c80df..1c08552e4a0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java @@ -27,6 +27,8 @@ import lombok.AllArgsConstructor; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.accountdetails.data.AccountSummaryCollectionData; @@ -35,6 +37,8 @@ import org.apache.fineract.portfolio.accountdetails.data.SavingsAccountSummaryData; import org.apache.fineract.portfolio.accountdetails.data.ShareAccountSummaryData; import org.apache.fineract.portfolio.accountdetails.data.WorkingCapitalLoanAccountSummaryData; +import org.apache.fineract.portfolio.client.api.ClientApiConstants; +import org.apache.fineract.portfolio.client.contract.ClientAccountSummaryReadService; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.group.service.GroupReadPlatformService; import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData; @@ -54,13 +58,22 @@ @Service @AllArgsConstructor -public class AccountDetailsReadPlatformServiceJpaRepositoryImpl implements AccountDetailsReadPlatformService { +public class AccountDetailsReadPlatformServiceJpaRepositoryImpl + implements AccountDetailsReadPlatformService, ClientAccountSummaryReadService { private final JdbcTemplate jdbcTemplate; private final ClientReadPlatformService clientReadPlatformService; private final GroupReadPlatformService groupReadPlatformService; private final ColumnValidator columnValidator; private final WorkingCapitalLoanApplicationReadPlatformService workingCapitalLoanApplicationReadPlatformService; + private final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer; + + @Override + public String retrieveClientAccountSummaryAsJson(final Long clientId, final ApiRequestJsonSerializationSettings settings) { + final AccountSummaryCollectionData clientAccount = retrieveClientAccountDetails(clientId); + return this.clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount, + ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS); + } @Override public AccountSummaryCollectionData retrieveClientAccountDetails(final Long clientId) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java index 78051122e9d..8ace0c5bc09 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/service/ChargeReadPlatformServiceImpl.java @@ -41,6 +41,7 @@ import org.apache.fineract.portfolio.charge.domain.ChargeAppliesTo; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; import org.apache.fineract.portfolio.charge.exception.ChargeNotFoundException; +import org.apache.fineract.portfolio.client.contract.ClientApplicableChargeReadService; import org.apache.fineract.portfolio.common.service.CommonEnumerations; import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; @@ -59,7 +60,7 @@ */ @RequiredArgsConstructor @Transactional(readOnly = true) -public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService { +public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService, ClientApplicableChargeReadService { private final CurrencyReadPlatformService currencyReadPlatformService; private final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientAddressApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientAddressApiResource.java index 1288debbe62..66d1176c051 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientAddressApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientAddressApiResource.java @@ -45,7 +45,7 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.address.data.AddressData; import org.apache.fineract.portfolio.address.filter.ClientAddressSearchParam; -import org.apache.fineract.portfolio.address.service.AddressReadPlatformServiceImpl; +import org.apache.fineract.portfolio.address.service.AddressReadPlatformService; import org.apache.fineract.portfolio.client.data.ClientAddressRequest; import org.springframework.stereotype.Component; @@ -57,7 +57,7 @@ public class ClientAddressApiResource { private static final String RESOURCE_NAME_FOR_PERMISSIONS = "Address"; private final PlatformSecurityContext context; - private final AddressReadPlatformServiceImpl readPlatformService; + private final AddressReadPlatformService readPlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResource.java index 01bf74f1a43..cf5e6373cae 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResource.java @@ -56,7 +56,7 @@ import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.charge.data.ChargeData; -import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; +import org.apache.fineract.portfolio.client.contract.ClientApplicableChargeReadService; import org.apache.fineract.portfolio.client.data.ClientChargeData; import org.apache.fineract.portfolio.client.data.ClientTransactionData; import org.apache.fineract.portfolio.client.service.ClientChargeReadPlatformService; @@ -73,7 +73,7 @@ public class ClientChargesApiResource { private final PlatformSecurityContext context; - private final ChargeReadPlatformService chargeReadPlatformService; + private final ClientApplicableChargeReadService chargeReadPlatformService; private final ClientChargeReadPlatformService clientChargeReadPlatformService; private final ClientTransactionReadPlatformService clientTransactionReadPlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java index 5a31c0bab6b..db917cb6811 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java @@ -63,16 +63,15 @@ import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.SqlValidator; -import org.apache.fineract.portfolio.accountdetails.data.AccountSummaryCollectionData; -import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPlatformService; +import org.apache.fineract.portfolio.client.contract.ClientAccountSummaryReadService; +import org.apache.fineract.portfolio.client.contract.ClientObligeeReadService; +import org.apache.fineract.portfolio.client.contract.ClientSavingsAccountLookupReadService; import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.client.service.ClientTemplateReadPlatformService; import org.apache.fineract.portfolio.loanaccount.guarantor.data.ObligeeData; -import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorReadPlatformService; import org.apache.fineract.portfolio.savings.data.SavingsAccountData; -import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.springframework.stereotype.Component; @@ -88,14 +87,13 @@ public class ClientsApiResource { private final ClientReadPlatformService clientReadPlatformService; private final ClientTemplateReadPlatformService clientTemplateReadPlatformService; private final ToApiJsonSerializer toApiJsonSerializer; - private final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; - private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; + private final ClientAccountSummaryReadService clientAccountSummaryReadService; + private final ClientSavingsAccountLookupReadService savingsAccountReadPlatformService; private final BulkImportWorkbookService bulkImportWorkbookService; private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService; - private final GuarantorReadPlatformService guarantorReadPlatformService; + private final ClientObligeeReadService guarantorReadPlatformService; private final SqlValidator sqlValidator; @GET @@ -462,7 +460,8 @@ private ClientData retrieveClientData(final Long clientId, final boolean staffIn final ClientData templateData = clientTemplateReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly); clientData = ClientData.templateOnTop(clientData, templateData); - Collection savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null); + Collection savingAccountOptions = savingsAccountReadPlatformService + .retrieveSavingsAccountsForClientLookup(clientId); if (savingAccountOptions != null && savingAccountOptions.size() > 0) { clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions); } @@ -566,11 +565,8 @@ private String retrieveClientAccounts(Long clientId, final String externalId, fi ExternalId clientExternalId = ExternalIdFactory.produce(externalId); clientId = getResolvedClientId(clientId, clientExternalId); - final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId); - final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount, - ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS); + return clientAccountSummaryReadService.retrieveClientAccountSummaryAsJson(clientId, settings); } private String applyCommandOverClient(Long clientId, final String externalId, final String command, final String jsonPayload) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientCharge.java index 6e179d27fdf..5c2789035fc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientCharge.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientCharge.java @@ -30,9 +30,9 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.OrganisationCurrency; -import org.apache.fineract.portfolio.charge.domain.Charge; import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType; import org.apache.fineract.portfolio.charge.domain.ChargeTimeType; +import org.apache.fineract.portfolio.client.contract.ClientChargeDefinitionData; @Entity @Table(name = "m_client_charge") @@ -42,9 +42,8 @@ public class ClientCharge extends AbstractPersistableCustom { @JoinColumn(name = "client_id", referencedColumnName = "id", nullable = false) private Client client; - @ManyToOne(optional = false) - @JoinColumn(name = "charge_id", referencedColumnName = "id", nullable = false) - private Charge charge; + @Column(name = "charge_id", nullable = false) + private Long chargeId; @Column(name = "charge_time_enum", nullable = false) private Integer chargeTime; @@ -92,21 +91,23 @@ protected ClientCharge() { // } - public static ClientCharge createNew(final Client client, final Charge charge, final BigDecimal amount, final LocalDate dueDate) { + public static ClientCharge createNew(final Client client, final ClientChargeDefinitionData charge, final BigDecimal amount, + final LocalDate dueDate) { final boolean status = true; return new ClientCharge(client, charge, amount, dueDate, status); } - private ClientCharge(final Client client, final Charge charge, final BigDecimal amount, final LocalDate dueDate, final boolean status) { + private ClientCharge(final Client client, final ClientChargeDefinitionData charge, final BigDecimal amount, final LocalDate dueDate, + final boolean status) { this.client = client; - this.charge = charge; - this.penaltyCharge = charge.isPenalty(); - this.chargeTime = charge.getChargeTimeType(); + this.chargeId = charge.chargeId(); + this.penaltyCharge = charge.penalty(); + this.chargeTime = charge.chargeTimeType(); this.dueDate = dueDate; - this.chargeCalculation = charge.getChargeCalculation(); + this.chargeCalculation = charge.chargeCalculation(); - BigDecimal chargeAmount = charge.getAmount(); + BigDecimal chargeAmount = charge.amount(); if (amount != null) { chargeAmount = amount; } @@ -214,8 +215,8 @@ public Client getClient() { return this.client; } - public Charge getCharge() { - return this.charge; + public Long getChargeId() { + return this.chargeId; } public Integer getChargeTime() { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientChargeRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientChargeRepositoryWrapper.java index 92e7a0486af..07cb26da53a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientChargeRepositoryWrapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientChargeRepositoryWrapper.java @@ -20,6 +20,7 @@ import org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepositoryWrapper; import org.apache.fineract.portfolio.charge.exception.ChargeNotFoundException; +import org.apache.fineract.portfolio.client.contract.ClientChargeReadService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -28,18 +29,22 @@ public class ClientChargeRepositoryWrapper { private final ClientChargeRepository repository; private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository; + private final ClientChargeReadService clientChargeReadService; @Autowired public ClientChargeRepositoryWrapper(final ClientChargeRepository repository, - final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper) { + final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepositoryWrapper, + final ClientChargeReadService clientChargeReadService) { this.repository = repository; this.organisationCurrencyRepository = organisationCurrencyRepositoryWrapper; + this.clientChargeReadService = clientChargeReadService; } public ClientCharge findOneWithNotFoundDetection(final Long id) { final ClientCharge clientCharge = this.repository.findById(id).orElseThrow(() -> new ChargeNotFoundException(id)); // enrich Client charge with details of Organizational currency - clientCharge.setCurrency(organisationCurrencyRepository.findOneWithNotFoundDetection(clientCharge.getCharge().getCurrencyCode())); + final String currencyCode = this.clientChargeReadService.retrieveClientChargeDefinition(clientCharge.getChargeId()).currencyCode(); + clientCharge.setCurrency(organisationCurrencyRepository.findOneWithNotFoundDetection(currencyCode)); return clientCharge; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java index d8828238d01..eb5698fc1c2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java @@ -36,7 +36,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; import org.apache.fineract.infrastructure.core.domain.ExternalId; @@ -139,7 +138,7 @@ public void reverse() { * * */ - public Map toMapData() { + public Map toMapData(final Map incomeAccountIdByChargeId) { final Map thisTransactionData = new LinkedHashMap<>(); final EnumOptionData transactionType = ClientEnumerations.clientTransactionType(this.typeOf); @@ -162,14 +161,15 @@ public Map toMapData() { final List> clientChargesPaidData = new ArrayList<>(); for (final ClientChargePaidBy clientChargePaidBy : this.clientChargePaidByCollection) { final Map clientChargePaidData = new LinkedHashMap<>(); - clientChargePaidData.put("chargeId", clientChargePaidBy.getClientCharge().getCharge().getId()); - clientChargePaidData.put("isPenalty", clientChargePaidBy.getClientCharge().getCharge().isPenalty()); - clientChargePaidData.put("clientChargeId", clientChargePaidBy.getClientCharge().getId()); + final ClientCharge clientCharge = clientChargePaidBy.getClientCharge(); + clientChargePaidData.put("chargeId", clientCharge.getChargeId()); + clientChargePaidData.put("isPenalty", clientCharge.isPenaltyCharge()); + clientChargePaidData.put("clientChargeId", clientCharge.getId()); clientChargePaidData.put("amount", clientChargePaidBy.getAmount()); - GLAccount glAccount = clientChargePaidBy.getClientCharge().getCharge().getAccount(); - if (glAccount != null) { + final Long incomeAccountId = incomeAccountIdByChargeId.get(clientCharge.getChargeId()); + if (incomeAccountId != null) { accountingEnabledForAtleastOneCharge = true; - clientChargePaidData.put("incomeAccountId", glAccount.getId()); + clientChargePaidData.put("incomeAccountId", incomeAccountId); } clientChargesPaidData.add(clientChargePaidData); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/package-info.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/package-info.java new file mode 100644 index 00000000000..c8eee5b2923 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/package-info.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. + */ + +/** + * Declares the client feature as a Spring Modulith application module so its outbound dependencies can be verified + * against the modularization outline. No {@code allowedDependencies} are declared: the boundary is checked by detecting + * references to other feature modules' types, and any violation is fixed at the source (fineract-core DTOs / + * read-services per the outline) rather than by widening an allow-list here. + */ +@org.springframework.modulith.ApplicationModule(displayName = "Client") +package org.apache.fineract.portfolio.client; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java index f9574f56f5f..cbcc0e1b755 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java @@ -22,13 +22,13 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.ApiParameterError; @@ -47,10 +47,11 @@ import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; -import org.apache.fineract.portfolio.charge.domain.Charge; -import org.apache.fineract.portfolio.charge.domain.ChargeRepositoryWrapper; import org.apache.fineract.portfolio.charge.exception.ChargeCannotBeAppliedToException; import org.apache.fineract.portfolio.client.api.ClientApiConstants; +import org.apache.fineract.portfolio.client.contract.ClientChargeDefinitionData; +import org.apache.fineract.portfolio.client.contract.ClientChargeReadService; +import org.apache.fineract.portfolio.client.contract.ClientJournalEntryWriteService; import org.apache.fineract.portfolio.client.data.ClientChargeDataValidator; import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientCharge; @@ -71,7 +72,7 @@ @Slf4j public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePlatformService { - private final ChargeRepositoryWrapper chargeRepository; + private final ClientChargeReadService clientChargeReadService; private final ClientRepositoryWrapper clientRepository; private final ClientChargeDataValidator clientChargeDataValidator; private final ConfigurationDomainService configurationDomainService; @@ -80,7 +81,7 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl private final ClientChargeRepositoryWrapper clientChargeRepository; private final ClientTransactionRepository clientTransactionRepository; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final ClientJournalEntryWriteService journalEntryWritePlatformService; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; @Override @@ -91,12 +92,12 @@ public CommandProcessingResult addCharge(Long clientId, JsonCommand command) { final Client client = clientRepository.getActiveClientInUserScope(clientId); final Long chargeDefinitionId = command.longValueOfParameterNamed(ClientApiConstants.chargeIdParamName); - final Charge charge = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); + final ClientChargeDefinitionData charge = this.clientChargeReadService.retrieveClientChargeDefinition(chargeDefinitionId); // validate for client charge - if (!charge.isClientCharge()) { - final String errorMessage = "Charge with identifier " + charge.getId() + " cannot be applied to a Client"; - throw new ChargeCannotBeAppliedToException("client", errorMessage, charge.getId()); + if (!charge.applicableToClients()) { + final String errorMessage = "Charge with identifier " + charge.chargeId() + " cannot be applied to a Client"; + throw new ChargeCannotBeAppliedToException("client", errorMessage, charge.chargeId()); } Money roundedAmount = calculateRoundedChargeAmount(charge, command); @@ -188,10 +189,22 @@ public CommandProcessingResult payCharge(Long clientId, Long clientChargeId, Jso } private void generateAccountingEntries(ClientTransaction clientTransaction) { - Map accountingBridgeData = clientTransaction.toMapData(); + Map accountingBridgeData = clientTransaction.toMapData(incomeAccountIdByChargeId(clientTransaction)); journalEntryWritePlatformService.createJournalEntriesForClientTransactions(accountingBridgeData); } + private Map incomeAccountIdByChargeId(final ClientTransaction clientTransaction) { + final Map incomeAccountIdByChargeId = new HashMap<>(); + for (final ClientChargePaidBy clientChargePaidBy : clientTransaction.getClientChargePaidByCollection()) { + final Long chargeId = clientChargePaidBy.getClientCharge().getChargeId(); + if (!incomeAccountIdByChargeId.containsKey(chargeId)) { + incomeAccountIdByChargeId.put(chargeId, + this.clientChargeReadService.retrieveClientChargeDefinition(chargeId).incomeAccountId()); + } + } + return incomeAccountIdByChargeId; + } + @Override public CommandProcessingResult waiveCharge(Long clientId, Long clientChargeId) { try { @@ -435,11 +448,11 @@ private void handleDataIntegrityIssues(@SuppressWarnings("unused") final Long cl "Unknown data integrity issue with resource: " + realCause.getMessage()); } - private Money calculateRoundedChargeAmount(final Charge charge, final JsonCommand command) { + private Money calculateRoundedChargeAmount(final ClientChargeDefinitionData charge, final JsonCommand command) { BigDecimal amount = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName); - amount = (amount == null) ? charge.getAmount() : amount; + amount = (amount == null) ? charge.amount() : amount; - ApplicationCurrency currency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(charge.getCurrencyCode()); + ApplicationCurrency currency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(charge.currencyCode()); CurrencyData currencyData = currency.toData(); return Money.of(currencyData, amount); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java index c790c6f5e5a..c591b06c11c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -18,14 +18,12 @@ */ package org.apache.fineract.portfolio.client.service; -import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; @@ -56,8 +54,7 @@ import org.apache.fineract.portfolio.client.domain.ClientStatus; import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; import org.apache.fineract.portfolio.client.mapper.ClientMapper; -import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement; -import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepositoryWrapper; +import org.apache.fineract.portfolio.collateralmanagement.service.ClientCollateralReadService; import org.apache.fineract.portfolio.group.data.GroupGeneralData; import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.dao.EmptyResultDataAccessException; @@ -83,7 +80,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private final ParentGroupsMapper clientGroupsMapper = new ParentGroupsMapper(); private final ColumnValidator columnValidator; - private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper; + private final ClientCollateralReadService clientCollateralReadService; private final ClientRepositoryWrapper clientRepositoryWrapper; private final ClientMapper clientMapper; private final InputValidator inputValidator; @@ -223,20 +220,9 @@ public ClientData retrieveOne(final Long clientId) { final Client client = clientRepositoryWrapper.getClientByClientIdAndHierarchy(clientId, hierarchySearchString); final ClientData clientData = clientMapper.map(client); - // Get client collaterals - final Collection clientCollateralManagements = this.clientCollateralManagementRepositoryWrapper - .getCollateralsPerClient(clientId); - final Set clientCollateralManagementDataSet = new HashSet<>(); - - // Map to client collateral data class - for (ClientCollateralManagement clientCollateralManagement : clientCollateralManagements) { - BigDecimal total = clientCollateralManagement.getTotal(); - BigDecimal totalCollateral = clientCollateralManagement.getTotalCollateral(total); - clientCollateralManagementDataSet.add(new ClientCollateralManagementData(clientCollateralManagement.getId(), - clientCollateralManagement.getCollaterals().getName(), clientCollateralManagement.getQuantity(), - clientCollateralManagement.getCollaterals().getPctToBase(), - clientCollateralManagement.getCollaterals().getBasePrice(), total, totalCollateral)); - } + // Get client collaterals (mapped to DTOs by the collateral-management module) + final Set clientCollateralManagementDataSet = this.clientCollateralReadService + .retrieveCollateralDataForClient(clientId); final String clientGroupsSql = "select " + this.clientGroupsMapper.parentGroupsSchema(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java index af7d8c7ea93..25ccc2bfe7f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTemplateReadPlatformServiceImpl.java @@ -32,7 +32,6 @@ import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; import org.apache.fineract.infrastructure.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum; -import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.office.data.OfficeData; import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; @@ -41,12 +40,13 @@ import org.apache.fineract.portfolio.address.data.AddressData; import org.apache.fineract.portfolio.address.service.AddressReadPlatformService; import org.apache.fineract.portfolio.client.api.ClientApiConstants; +import org.apache.fineract.portfolio.client.contract.ClientDatatableChecksReadService; +import org.apache.fineract.portfolio.client.contract.ClientSavingsProductLookupReadService; import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientFamilyMembersData; import org.apache.fineract.portfolio.client.domain.ClientEnumerations; import org.apache.fineract.portfolio.client.domain.LegalForm; import org.apache.fineract.portfolio.savings.data.SavingsProductData; -import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -58,9 +58,9 @@ public class ClientTemplateReadPlatformServiceImpl implements ClientTemplateRead private final OfficeReadPlatformService officeReadPlatformService; private final StaffReadService staffReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService; - private final SavingsProductReadPlatformService savingsProductReadPlatformService; + private final ClientSavingsProductLookupReadService savingsProductReadPlatformService; // data mappers - private final EntityDatatableChecksReadService entityDatatableChecksReadService; + private final ClientDatatableChecksReadService entityDatatableChecksReadService; private final AddressReadPlatformService addressReadPlatformService; private final ClientFamilyMembersReadPlatformService clientFamilyMembersReadPlatformService; @@ -75,7 +75,8 @@ public ClientData retrieveTemplate(final Long officeId, final boolean staffInSel final Collection offices = this.officeReadPlatformService.retrieveAllOfficesForDropdown(); - final Collection savingsProductDatas = this.savingsProductReadPlatformService.retrieveAllForLookupByType(null); + final Collection savingsProductDatas = this.savingsProductReadPlatformService + .retrieveAllSavingsProductsForClientLookup(); final Boolean isAddressEnabled = configurationDomainService.isAddressEnabled(); if (isAddressEnabled) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionWritePlatformServiceJpaRepositoryImpl.java index 97841807ea3..a1f06fbb00c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionWritePlatformServiceJpaRepositoryImpl.java @@ -18,13 +18,15 @@ */ package org.apache.fineract.portfolio.client.service; +import java.util.HashMap; import java.util.Map; import java.util.Set; import lombok.RequiredArgsConstructor; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepositoryWrapper; +import org.apache.fineract.portfolio.client.contract.ClientChargeReadService; +import org.apache.fineract.portfolio.client.contract.ClientJournalEntryWriteService; import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientCharge; import org.apache.fineract.portfolio.client.domain.ClientChargePaidBy; @@ -42,7 +44,8 @@ public class ClientTransactionWritePlatformServiceJpaRepositoryImpl implements C private final ClientRepositoryWrapper clientRepository; private final OrganisationCurrencyRepositoryWrapper organisationCurrencyRepository; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; + private final ClientJournalEntryWriteService journalEntryWritePlatformService; + private final ClientChargeReadService clientChargeReadService; @Override public CommandProcessingResult undo(Long clientId, Long transactionId) { @@ -65,8 +68,9 @@ public CommandProcessingResult undo(Long clientId, Long transactionId) { final Set chargesPaidBy = clientTransaction.getClientChargePaidByCollection(); for (final ClientChargePaidBy clientChargePaidBy : chargesPaidBy) { final ClientCharge clientCharge = clientChargePaidBy.getClientCharge(); - clientCharge.setCurrency( - organisationCurrencyRepository.findOneWithNotFoundDetection(clientCharge.getCharge().getCurrencyCode())); + final String currencyCode = this.clientChargeReadService.retrieveClientChargeDefinition(clientCharge.getChargeId()) + .currencyCode(); + clientCharge.setCurrency(organisationCurrencyRepository.findOneWithNotFoundDetection(currencyCode)); if (clientTransaction.isPayChargeTransaction()) { clientCharge.undoPayment(clientTransaction.getAmount()); } else if (clientTransaction.isWaiveChargeTransaction()) { @@ -87,8 +91,20 @@ public CommandProcessingResult undo(Long clientId, Long transactionId) { } private void generateAccountingEntries(ClientTransaction clientTransaction) { - Map accountingBridgeData = clientTransaction.toMapData(); + Map accountingBridgeData = clientTransaction.toMapData(incomeAccountIdByChargeId(clientTransaction)); journalEntryWritePlatformService.createJournalEntriesForClientTransactions(accountingBridgeData); } + private Map incomeAccountIdByChargeId(final ClientTransaction clientTransaction) { + final Map incomeAccountIdByChargeId = new HashMap<>(); + for (final ClientChargePaidBy clientChargePaidBy : clientTransaction.getClientChargePaidByCollection()) { + final Long chargeId = clientChargePaidBy.getClientCharge().getChargeId(); + if (!incomeAccountIdByChargeId.containsKey(chargeId)) { + incomeAccountIdByChargeId.put(chargeId, + this.clientChargeReadService.retrieveClientChargeDefinition(chargeId).incomeAccountId()); + } + } + return incomeAccountIdByChargeId; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 59ea3118e64..e417a6ce726 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -36,6 +36,7 @@ import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormat; import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper; import org.apache.fineract.infrastructure.accountnumberformat.domain.EntityAccountType; +import org.apache.fineract.infrastructure.accountnumberformat.service.AccountNumberGeneratorService; import org.apache.fineract.infrastructure.codes.domain.CodeValue; import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; @@ -60,10 +61,14 @@ import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; import org.apache.fineract.organisation.staff.domain.Staff; import org.apache.fineract.organisation.staff.domain.StaffRepositoryWrapper; -import org.apache.fineract.portfolio.account.service.AccountNumberGenerator; import org.apache.fineract.portfolio.address.service.AddressWritePlatformService; import org.apache.fineract.portfolio.client.api.ClientApiConstants; +import org.apache.fineract.portfolio.client.contract.ClientLoanReadService; +import org.apache.fineract.portfolio.client.contract.ClientNoteWriteService; +import org.apache.fineract.portfolio.client.contract.ClientSavingsActivationService; +import org.apache.fineract.portfolio.client.contract.ClientSavingsReadService; import org.apache.fineract.portfolio.client.data.ClientDataValidator; +import org.apache.fineract.portfolio.client.data.ClientLoanStatusData; import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientEnumerations; import org.apache.fineract.portfolio.client.domain.ClientNonPerson; @@ -80,16 +85,6 @@ import org.apache.fineract.portfolio.group.domain.GroupRepository; import org.apache.fineract.portfolio.group.exception.GroupMemberCountNotInPermissibleRangeException; import org.apache.fineract.portfolio.group.exception.GroupNotFoundException; -import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; -import org.apache.fineract.portfolio.note.domain.Note; -import org.apache.fineract.portfolio.note.domain.NoteRepository; -import org.apache.fineract.portfolio.savings.data.SavingsAccountDataDTO; -import org.apache.fineract.portfolio.savings.domain.SavingsAccount; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper; -import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; -import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; -import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService; import org.apache.fineract.useradministration.domain.AppUser; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.orm.jpa.JpaSystemException; @@ -105,16 +100,15 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final ClientRepositoryWrapper clientRepository; private final ClientNonPersonRepositoryWrapper clientNonPersonRepository; private final OfficeRepositoryWrapper officeRepositoryWrapper; - private final NoteRepository noteRepository; + private final ClientNoteWriteService clientNoteWriteService; private final GroupRepository groupRepository; private final ClientDataValidator fromApiJsonDeserializer; - private final AccountNumberGenerator accountNumberGenerator; + private final AccountNumberGeneratorService accountNumberGenerator; private final StaffRepositoryWrapper staffRepository; private final CodeValueRepositoryWrapper codeValueRepository; - private final LoanRepositoryWrapper loanRepositoryWrapper; - private final SavingsAccountRepositoryWrapper savingsRepositoryWrapper; - private final SavingsProductRepository savingsProductRepository; - private final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService; + private final ClientLoanReadService clientLoanReadService; + private final ClientSavingsReadService clientSavingsReadService; + private final ClientSavingsActivationService clientSavingsActivationService; private final CommandProcessingService commandProcessingService; private final ConfigurationDomainService configurationDomainService; private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; @@ -134,8 +128,7 @@ public CommandProcessingResult deleteClient(final Long clientId) { if (client.isNotPending()) { throw new ClientMustBePendingToBeDeletedException(clientId); } - final List relatedNotes = this.noteRepository.findByClient(client); - this.noteRepository.deleteAllInBatch(relatedNotes); + this.clientNoteWriteService.deleteNotesByClient(client); final ClientNonPerson clientNonPerson = this.clientNonPersonRepository.findOneByClientId(clientId); if (clientNonPerson != null) { @@ -231,8 +224,7 @@ public CommandProcessingResult createClient(final JsonCommand command) { final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName); if (savingsProductId != null) { - this.savingsProductRepository.findById(savingsProductId) - .orElseThrow(() -> new SavingsProductNotFoundException(savingsProductId)); + this.clientSavingsReadService.validateSavingsProductExists(savingsProductId); } boolean isEntity = false; @@ -292,7 +284,7 @@ public CommandProcessingResult createClient(final JsonCommand command) { this.clientRepository.saveAndFlush(newClient); if (StringUtils.isBlank(accountNo)) { AccountNumberFormat accountNumberFormat = this.accountNumberFormatRepository.findByAccountType(EntityAccountType.CLIENT); - newClient.updateAccountNo(accountNumberGenerator.generate(newClient, accountNumberFormat)); + newClient.updateAccountNo(accountNumberGenerator.generate(EntityAccountType.CLIENT, newClient, accountNumberFormat)); this.clientRepository.saveAndFlush(newClient); } @@ -578,8 +570,7 @@ public CommandProcessingResult updateClient(final Long clientId, final JsonComma } final Long savingsProductId = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName); if (savingsProductId != null) { - this.savingsProductRepository.findById(savingsProductId) - .orElseThrow(() -> new SavingsProductNotFoundException(savingsProductId)); + this.clientSavingsReadService.validateSavingsProductExists(savingsProductId); } clientForUpdate.updateSavingsProduct(savingsProductId); } @@ -731,11 +722,8 @@ public CommandProcessingResult activateClient(final Long clientId, final JsonCom private CommandProcessingResult openSavingsAccount(final Client client, final DateTimeFormatter fmt) { CommandProcessingResult commandProcessingResult = CommandProcessingResult.empty(); if (client.isActive() && client.savingsProductId() != null) { - SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.savingsProductId(), - client.getActivationDate(), client.activatedBy(), fmt); - commandProcessingResult = this.savingsApplicationProcessWritePlatformService.createActiveApplication(savingsAccountDataDTO); + commandProcessingResult = this.clientSavingsActivationService.createSavingsForClientActivation(client, fmt); if (commandProcessingResult.getSavingsId() != null) { - this.savingsRepositoryWrapper.findOneWithNotFoundDetection(commandProcessingResult.getSavingsId()); client.updateSavingsAccount(commandProcessingResult.getSavingsId()); client.updateSavingsProduct(null); } @@ -849,28 +837,24 @@ public CommandProcessingResult closeClient(final Long clientId, final JsonComman entityDatatableChecksWritePlatformService.runTheCheck(clientId, EntityTables.CLIENT.getName(), StatusEnum.CLOSE.getValue(), EntityTables.CLIENT.getForeignKeyColumnNameOnDatatable(), legalForm.getLabel()); - final List clientLoans = this.loanRepositoryWrapper.findLoanByClientId(clientId); - for (final Loan loan : clientLoans) { - final LoanStatusMapper loanStatus = new LoanStatusMapper(loan.getStatus().getValue()); + final List clientLoans = this.clientLoanReadService.retrieveClientLoanStatuses(clientId); + for (final ClientLoanStatusData loan : clientLoans) { + final LoanStatusMapper loanStatus = new LoanStatusMapper(loan.statusId()); if (loanStatus.isOpen() || loanStatus.isPendingApproval() || loanStatus.isAwaitingDisbursal()) { final String errorMessage = "Client cannot be closed because of non-closed loans."; throw new InvalidClientStateTransitionException("close", "loan.non-closed", errorMessage); - } else if (loanStatus.isClosed() && DateUtils.isAfter(loan.getClosedOnDate(), closureDate)) { + } else if (loanStatus.isClosed() && DateUtils.isAfter(loan.closedOnDate(), closureDate)) { final String errorMessage = "The client closureDate cannot be before the loan closedOnDate."; throw new InvalidClientStateTransitionException("close", "date.cannot.before.loan.closed.date", errorMessage, - closureDate, loan.getClosedOnDate()); + closureDate, loan.closedOnDate()); } else if (loanStatus.isOverpaid()) { final String errorMessage = "Client cannot be closed because of overpaid loans."; throw new InvalidClientStateTransitionException("close", "loan.overpaid", errorMessage); } } - final List clientSavingAccounts = this.savingsRepositoryWrapper.findSavingAccountByClientId(clientId); - - for (final SavingsAccount saving : clientSavingAccounts) { - if (saving.isActive() || saving.isSubmittedAndPendingApproval() || saving.isApproved()) { - final String errorMessage = "Client cannot be closed because of non-closed savings account."; - throw new InvalidClientStateTransitionException("close", "non-closed.savings.account", errorMessage); - } + if (this.clientSavingsReadService.hasNonClosedSavingsAccountsForClient(clientId)) { + final String errorMessage = "Client cannot be closed because of non-closed savings account."; + throw new InvalidClientStateTransitionException("close", "non-closed.savings.account", errorMessage); } client.close(currentUser, closureReason, closureDate); @@ -898,11 +882,9 @@ public CommandProcessingResult updateDefaultSavingsAccount(final Long clientId, final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId); - SavingsAccount savingsAccount = null; final Long savingsId = command.longValueOfParameterNamed(ClientApiConstants.savingsAccountIdParamName); if (savingsId != null) { - savingsAccount = this.savingsRepositoryWrapper.findOneWithNotFoundDetection(savingsId); - if (!savingsAccount.getClient().identifiedBy(clientId)) { + if (!this.clientSavingsReadService.isSavingsAccountForClient(savingsId, clientId)) { String defaultUserMessage = "saving account must belongs to client"; throw new InvalidClientSavingProductException("saving.account", "must.belongs.to.client", defaultUserMessage, savingsId, clientForUpdate.getId()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadServiceImpl.java new file mode 100644 index 00000000000..86ef4444388 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collateralmanagement/service/ClientCollateralReadServiceImpl.java @@ -0,0 +1,52 @@ +/** + * 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.collateralmanagement.service; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.client.data.ClientCollateralManagementData; +import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement; +import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepositoryWrapper; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ClientCollateralReadServiceImpl implements ClientCollateralReadService { + + private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper; + + @Override + public Set retrieveCollateralDataForClient(final Long clientId) { + final Collection clientCollateralManagements = this.clientCollateralManagementRepositoryWrapper + .getCollateralsPerClient(clientId); + final Set clientCollateralManagementDataSet = new HashSet<>(); + for (final ClientCollateralManagement clientCollateralManagement : clientCollateralManagements) { + final BigDecimal total = clientCollateralManagement.getTotal(); + final BigDecimal totalCollateral = clientCollateralManagement.getTotalCollateral(total); + clientCollateralManagementDataSet.add(new ClientCollateralManagementData(clientCollateralManagement.getId(), + clientCollateralManagement.getCollaterals().getName(), clientCollateralManagement.getQuantity(), + clientCollateralManagement.getCollaterals().getPctToBase(), clientCollateralManagement.getCollaterals().getBasePrice(), + total, totalCollateral)); + } + return clientCollateralManagementDataSet; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java index 19f88a56c84..a532bed23cb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/guarantor/service/GuarantorReadPlatformServiceImpl.java @@ -33,6 +33,7 @@ import org.apache.fineract.organisation.staff.service.StaffReadService; import org.apache.fineract.portfolio.account.data.PortfolioAccountData; import org.apache.fineract.portfolio.account.domain.AccountAssociationType; +import org.apache.fineract.portfolio.client.contract.ClientObligeeReadService; import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; @@ -51,7 +52,7 @@ @Service @Transactional(readOnly = true) -public class GuarantorReadPlatformServiceImpl implements GuarantorReadPlatformService { +public class GuarantorReadPlatformServiceImpl implements GuarantorReadPlatformService, ClientObligeeReadService { private final JdbcTemplate jdbcTemplate; private final ClientReadPlatformService clientReadPlatformService; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceImpl.java index c87f75d3a76..2cc99b8a948 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceImpl.java @@ -18,10 +18,13 @@ */ package org.apache.fineract.portfolio.note.service; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.Strings; import org.apache.commons.lang3.tuple.Pair; +import org.apache.fineract.portfolio.client.contract.ClientNoteWriteService; +import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.group.domain.GroupRepository; import org.apache.fineract.portfolio.group.exception.GroupNotFoundException; @@ -51,7 +54,7 @@ @RequiredArgsConstructor @Service @ConditionalOnMissingBean(value = NoteWritePlatformService.class, ignored = NoteWritePlatformServiceImpl.class) -public class NoteWritePlatformServiceImpl implements NoteWritePlatformService { +public class NoteWritePlatformServiceImpl implements NoteWritePlatformService, ClientNoteWriteService { private final NoteRepository noteRepository; private final ClientRepositoryWrapper clientRepository; @@ -206,4 +209,10 @@ private Pair getNote(NoteType type, Long resourceId, Long noteId) { return Pair.of(note, officeId); } + + @Override + public void deleteNotesByClient(final Client client) { + final List relatedNotes = this.noteRepository.findByClient(client); + this.noteRepository.deleteAllInBatch(relatedNotes); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java index 674e8442d1c..087950e8f27 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java @@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.account.data.AccountTransferData; +import org.apache.fineract.portfolio.client.contract.ClientSavingsAccountLookupReadService; import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.group.data.GroupGeneralData; import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData; @@ -86,7 +87,12 @@ import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; -public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService { +public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService, ClientSavingsAccountLookupReadService { + + @Override + public Collection retrieveSavingsAccountsForClientLookup(final Long clientId) { + return retrieveForLookup(clientId, null); + } private final PlatformSecurityContext context; private final JdbcTemplate jdbcTemplate; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java index 9c3e18d7812..e56832e7b91 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsProductReadPlatformServiceImpl.java @@ -30,6 +30,7 @@ import org.apache.fineract.infrastructure.entityaccess.service.FineractEntityAccessUtil; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.portfolio.client.contract.ClientSavingsProductLookupReadService; import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.data.SavingsProductData; import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; @@ -39,7 +40,12 @@ import org.springframework.jdbc.core.RowMapper; @RequiredArgsConstructor -public class SavingsProductReadPlatformServiceImpl implements SavingsProductReadPlatformService { +public class SavingsProductReadPlatformServiceImpl implements SavingsProductReadPlatformService, ClientSavingsProductLookupReadService { + + @Override + public Collection retrieveAllSavingsProductsForClientLookup() { + return retrieveAllForLookupByType(null); + } private final PlatformSecurityContext context; private final JdbcTemplate jdbcTemplate; diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java new file mode 100644 index 00000000000..9deaa7505da --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java @@ -0,0 +1,152 @@ +/** + * 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.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.tngtech.archunit.core.domain.JavaClass; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.modulith.core.ApplicationModule; +import org.springframework.modulith.core.ApplicationModuleDependency; +import org.springframework.modulith.core.ApplicationModules; + +class ClientCrossFeatureBoundaryTest { + + private static final Logger LOG = LoggerFactory.getLogger(ClientCrossFeatureBoundaryTest.class); + + private static final String BASE = "org.apache.fineract"; + private static final String CLIENT_PACKAGE = "org.apache.fineract.portfolio.client"; + + // fineract-validation is a foundation module (fineract-core itself depends on it), so shared validation + // constraints/utilities are allowed alongside fineract-core and fineract-command. + private static final Set FOUNDATION_ARTIFACTS = Set.of("fineract-core", "fineract-command", "fineract-validation"); + + private static final Pattern FINERACT_ARTIFACT = Pattern.compile("fineract-[a-z0-9-]+"); + + private static ApplicationModules modules; + + private static ApplicationModules modules() { + if (modules == null) { + modules = ApplicationModules.of(BASE); + } + return modules; + } + + private static ApplicationModule clientModule() { + return modules().stream() // + .filter(module -> module.getBasePackage().getName().equals(CLIENT_PACKAGE)) // + .findFirst() // + .orElseThrow(() -> new IllegalStateException("Client module not found in the model")); + } + + private static String featureKey(String typeName) { + String prefix = BASE + "."; + if (!typeName.startsWith(prefix)) { + return typeName; + } + String remainder = typeName.substring(prefix.length()); + int firstDot = remainder.indexOf('.'); + if (firstDot < 0) { + return remainder; + } + int secondDot = remainder.indexOf('.', firstDot + 1); + return secondDot < 0 ? remainder : remainder.substring(0, secondDot); + } + + private static String owningArtifact(JavaClass type) { + return type.getSource() // + .map(source -> source.getUri().toString()) // + .map(uri -> { + Matcher matcher = FINERACT_ARTIFACT.matcher(uri); + return matcher.find() ? matcher.group() : uri; + }) // + .orElse("(unknown-source)"); + } + + private static boolean isFoundation(JavaClass type) { + return type.getSource() // + .map(source -> source.getUri().toString()) // + .map(uri -> FOUNDATION_ARTIFACTS.stream().anyMatch(uri::contains)) // + .orElse(false); + } + + @org.junit.jupiter.api.Disabled("Diagnostic report; enable locally when investigating Client module boundary dependencies") + @Test + void printClientCrossFeatureDependencyReport() { + Map> sourceTypeToTargets = new TreeMap<>(); + Set violationFeatures = new TreeSet<>(); + Set allowedFromCore = new TreeSet<>(); + + clientModule().getDirectDependencies(modules()).stream().forEach((ApplicationModuleDependency dependency) -> { + JavaClass targetType = dependency.getTargetType(); + String sourceType = dependency.getSourceType().getName(); + String featureKey = featureKey(targetType.getName()); + String artifact = owningArtifact(targetType); + boolean foundation = isFoundation(targetType); + + String label = featureKey + " [" + artifact + (foundation ? " : foundation]" : " : VIOLATION]"); + sourceTypeToTargets.computeIfAbsent(sourceType, key -> new TreeSet<>()).add(label); + + if (foundation) { + allowedFromCore.add(featureKey + " (" + artifact + ")"); + } else { + violationFeatures.add(featureKey + " (" + artifact + ")"); + } + }); + + LOG.info("==== Client cross-feature dependency report ===="); + LOG.info("base package : {}", BASE); + LOG.info("-- source type -> referenced feature packages [owning artifact : status] --"); + sourceTypeToTargets.forEach((source, targets) -> { + LOG.info("source type : {}", source); + LOG.info("referenced targets : {}", targets); + }); + LOG.info("-- allowed dependencies --"); + LOG.info("allowed from core : {}", allowedFromCore); + LOG.info("-- dependency violations --"); + LOG.info("violation features : {}", violationFeatures); + } + + @Test + void clientMustNotImportOtherFeatureModules() { + Set featureDependencies = new TreeSet<>(); + + clientModule().getDirectDependencies(modules()).stream().forEach((ApplicationModuleDependency dependency) -> { + JavaClass targetType = dependency.getTargetType(); + if (!isFoundation(targetType)) { + featureDependencies.add(featureKey(targetType.getName()) + " (" + owningArtifact(targetType) + ")"); + } + }); + + assertThat(featureDependencies) // + .as("Client may depend only on fineract-core and fineract-command. Types referenced from other " + + "feature modules must be reached via core read-contracts / DTOs, command/event " + + "boundaries, or by-id references (or the shared type moved into core). Offending " + + "feature packages (with owning artifact): %s", featureDependencies) // + .isEmpty(); + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImplTest.java index d05c78216c9..5a69246f3af 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImplTest.java @@ -44,7 +44,7 @@ import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; import org.apache.fineract.portfolio.client.mapper.ClientMapper; -import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepositoryWrapper; +import org.apache.fineract.portfolio.collateralmanagement.service.ClientCollateralReadService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -71,7 +71,7 @@ class ClientReadPlatformServiceImplTest { @Mock private ColumnValidator columnValidator; @Mock - private ClientCollateralManagementRepositoryWrapper collateralRepoWrapper; + private ClientCollateralReadService clientCollateralReadService; @Mock private ClientRepositoryWrapper clientRepositoryWrapper; @Mock @@ -94,7 +94,7 @@ void testRetrieveOne_Success() { when(context.officeHierarchy()).thenReturn(mockHierarchy); when(clientRepositoryWrapper.getClientByClientIdAndHierarchy(clientId, mockHierarchy + "%")).thenReturn(mockClientEntity); when(clientMapper.map(mockClientEntity)).thenReturn(mockClientData); - when(collateralRepoWrapper.getCollateralsPerClient(clientId)).thenReturn(Collections.emptyList()); + when(clientCollateralReadService.retrieveCollateralDataForClient(clientId)).thenReturn(Collections.emptySet()); // Mock the groups query to return an empty list when(jdbcTemplate.query(anyString(), any(RowMapper.class), anyLong())).thenReturn(Collections.emptyList()); diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsActivationServiceImpl.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsActivationServiceImpl.java new file mode 100644 index 00000000000..a1a36777954 --- /dev/null +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsActivationServiceImpl.java @@ -0,0 +1,48 @@ +/** + * 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.savings.service; + +import java.time.format.DateTimeFormatter; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.portfolio.client.contract.ClientSavingsActivationService; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.savings.data.SavingsAccountDataDTO; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ClientSavingsActivationServiceImpl implements ClientSavingsActivationService { + + private final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService; + private final SavingsAccountRepositoryWrapper savingsRepositoryWrapper; + + @Override + public CommandProcessingResult createSavingsForClientActivation(final Client client, final DateTimeFormatter fmt) { + final SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.savingsProductId(), + client.getActivationDate(), client.activatedBy(), fmt); + final CommandProcessingResult result = this.savingsApplicationProcessWritePlatformService + .createActiveApplication(savingsAccountDataDTO); + if (result.getSavingsId() != null) { + this.savingsRepositoryWrapper.findOneWithNotFoundDetection(result.getSavingsId()); + } + return result; + } +} diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsReadServiceImpl.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsReadServiceImpl.java new file mode 100644 index 00000000000..0c4ba410f78 --- /dev/null +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/service/ClientSavingsReadServiceImpl.java @@ -0,0 +1,58 @@ +/** + * 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.savings.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.client.contract.ClientSavingsReadService; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper; +import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository; +import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ClientSavingsReadServiceImpl implements ClientSavingsReadService { + + private final SavingsProductRepository savingsProductRepository; + private final SavingsAccountRepositoryWrapper savingsRepositoryWrapper; + + @Override + public void validateSavingsProductExists(final Long savingsProductId) { + this.savingsProductRepository.findById(savingsProductId).orElseThrow(() -> new SavingsProductNotFoundException(savingsProductId)); + } + + @Override + public boolean hasNonClosedSavingsAccountsForClient(final Long clientId) { + final List clientSavingAccounts = this.savingsRepositoryWrapper.findSavingAccountByClientId(clientId); + for (final SavingsAccount saving : clientSavingAccounts) { + if (saving.isActive() || saving.isSubmittedAndPendingApproval() || saving.isApproved()) { + return true; + } + } + return false; + } + + @Override + public boolean isSavingsAccountForClient(final Long savingsId, final Long clientId) { + final SavingsAccount savingsAccount = this.savingsRepositoryWrapper.findOneWithNotFoundDetection(savingsId); + return savingsAccount.getClient().identifiedBy(clientId); + } +} From 00bb00bf34b8a004c73caa4fe0b9142d71a2cdd0 Mon Sep 17 00:00:00 2001 From: mansimaurya Date: Thu, 9 Jul 2026 20:45:06 +0530 Subject: [PATCH 2/2] FINERACT-2647: Fixed the checkstyle violation --- .../portfolio/client/ClientCrossFeatureBoundaryTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java index 9deaa7505da..cdbcce80aa2 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/ClientCrossFeatureBoundaryTest.java @@ -28,6 +28,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.modulith.core.ApplicationModule; @@ -41,8 +42,6 @@ class ClientCrossFeatureBoundaryTest { private static final String BASE = "org.apache.fineract"; private static final String CLIENT_PACKAGE = "org.apache.fineract.portfolio.client"; - // fineract-validation is a foundation module (fineract-core itself depends on it), so shared validation - // constraints/utilities are allowed alongside fineract-core and fineract-command. private static final Set FOUNDATION_ARTIFACTS = Set.of("fineract-core", "fineract-command", "fineract-validation"); private static final Pattern FINERACT_ARTIFACT = Pattern.compile("fineract-[a-z0-9-]+"); @@ -94,7 +93,7 @@ private static boolean isFoundation(JavaClass type) { .orElse(false); } - @org.junit.jupiter.api.Disabled("Diagnostic report; enable locally when investigating Client module boundary dependencies") + @EnabledIfSystemProperty(named = "client.boundary.report", matches = "true") @Test void printClientCrossFeatureDependencyReport() { Map> sourceTypeToTargets = new TreeMap<>();