From 5109f9d9f2f09dc9bc4e41a33f43e2573f9208a1 Mon Sep 17 00:00:00 2001 From: "Rym.Ghosn" Date: Tue, 18 Aug 2026 17:05:19 +0300 Subject: [PATCH 1/2] FINERACT-2763: Add pending maker-checker approval status to client, loan, and savings account GET responses Currently, when a client, loan, or savings account has one or more maker-checker actions awaiting checker approval, there is no way to discover this from the corresponding GET API response. A caller has to separately query the checker inbox/pending-audit endpoints and correlate entries by entity, which is easy to miss and leaves API consumers unaware that the record they just fetched has a pending change awaiting approval. This adds a pendingMakerCheckerApprovals field to the ClientData, LoanAccountData, SavingsAccountData, and DepositAccountData (fixed/recurring deposit) response objects. When a GET request for one of these entities is served, the field is populated with any CommandSource entries still AWAITING_APPROVAL for that entity (id, action name, entity name, permission code, maker username, and submission date), or omitted (null) when there is nothing pending. Changes: - New PendingMakerCheckerData DTO and MakerCheckerReadService/ MakerCheckerReadServiceImpl to look up pending CommandSource rows by loan/client/savings id. - New CommandSourceRepository.findPendingBy{Loan,Client,Savings}Id queries (join fetch c.maker to avoid N+1 lookups for the maker username). - Wired into ClientReadPlatformServiceImpl#retrieveOne, LoanReadPlatformServiceImpl#retrieveOne, SavingsAccountReadPlatformServiceImpl#retrieveOne, and DepositAccountReadPlatformServiceImpl#retrieveOne. This is a read-only, additive change - it does not alter maker-checker approval/permission logic itself, only exposes existing pending-approval state that was previously only visible via the checker inbox. OffsetDateTime.now() without an explicit zone in the test fails the JavaTimeDefaultTimeZone errorprone check; uses OffsetDateTime.now(ZoneOffset.UTC) instead. --- .../data/PendingMakerCheckerData.java | 49 +++++ .../domain/CommandSourceRepository.java | 10 + .../service/MakerCheckerReadService.java | 31 +++ .../service/MakerCheckerReadServiceImpl.java | 75 +++++++ .../portfolio/client/data/ClientData.java | 3 + .../savings/data/SavingsAccountData.java | 3 + .../MakerCheckerReadServiceImplTest.java | 192 ++++++++++++++++++ .../loanaccount/data/LoanAccountData.java | 3 + .../portfolio/client/mapper/ClientMapper.java | 1 + .../ClientReadPlatformServiceImpl.java | 10 +- .../service/LoanReadPlatformServiceImpl.java | 13 +- .../starter/LoanAccountConfiguration.java | 5 +- ...DepositAccountReadPlatformServiceImpl.java | 14 +- ...SavingsAccountReadPlatformServiceImpl.java | 15 +- .../savings/starter/SavingsConfiguration.java | 10 +- .../ClientReadPlatformServiceImplTest.java | 4 + .../savings/data/DepositAccountData.java | 12 ++ 17 files changed, 439 insertions(+), 11 deletions(-) create mode 100644 fineract-core/src/main/java/org/apache/fineract/commands/data/PendingMakerCheckerData.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadService.java create mode 100644 fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java create mode 100644 fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/data/PendingMakerCheckerData.java b/fineract-core/src/main/java/org/apache/fineract/commands/data/PendingMakerCheckerData.java new file mode 100644 index 00000000000..588c2b70282 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/commands/data/PendingMakerCheckerData.java @@ -0,0 +1,49 @@ +/** + * 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.commands.data; + +import java.time.OffsetDateTime; +import lombok.Builder; +import lombok.Getter; + +/** + * Lightweight DTO representing a single pending maker-checker entry for a resource. + */ +@Getter +@Builder +public class PendingMakerCheckerData { + + /** The maker-checker command source id (m_portfolio_command_source.id) */ + private final Long id; + + /** e.g. "APPROVE", "DISBURSE", "CREATE", "ACTIVATE" */ + private final String actionName; + + /** e.g. "LOAN", "CLIENT", "SAVINGSACCOUNT" */ + private final String entityName; + + /** Human-readable label, e.g. "APPROVE_LOAN" */ + private final String permissionCode; + + /** Username of the maker who submitted this command */ + private final String makerUsername; + + /** When the maker submitted this command */ + private final OffsetDateTime madeOnDate; +} diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSourceRepository.java b/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSourceRepository.java index ca0de426e7b..cdf0171de56 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSourceRepository.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/domain/CommandSourceRepository.java @@ -19,6 +19,7 @@ package org.apache.fineract.commands.domain; import java.time.OffsetDateTime; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; @@ -29,6 +30,15 @@ public interface CommandSourceRepository extends JpaRepository findPendingByLoanId(@Param("loanId") Long loanId, @Param("status") Integer status); + + @Query("select distinct c from CommandSource c join fetch c.maker where c.clientId = :clientId and c.status = :status order by c.madeOnDate desc") + List findPendingByClientId(@Param("clientId") Long clientId, @Param("status") Integer status); + + @Query("select distinct c from CommandSource c join fetch c.maker where c.savingsId = :savingsId and c.status = :status order by c.madeOnDate desc") + List findPendingBySavingsId(@Param("savingsId") Long savingsId, @Param("status") Integer status); + @Modifying(flushAutomatically = true) @Query("delete from CommandSource c where c.status = :status and c.madeOnDate is not null and c.madeOnDate <= :dateForPurgeCriteria") void deleteOlderEventsWithStatus(@Param("status") Integer status, @Param("dateForPurgeCriteria") OffsetDateTime dateForPurgeCriteria); diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadService.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadService.java new file mode 100644 index 00000000000..3829c75543c --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadService.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.commands.service; + +import java.util.List; +import org.apache.fineract.commands.data.PendingMakerCheckerData; + +public interface MakerCheckerReadService { + + List retrievePendingByLoanId(Long loanId); + + List retrievePendingByClientId(Long clientId); + + List retrievePendingBySavingsId(Long savingsId); +} diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java new file mode 100644 index 00000000000..b1760c8df60 --- /dev/null +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java @@ -0,0 +1,75 @@ +/** + * 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.commands.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.commands.data.PendingMakerCheckerData; +import org.apache.fineract.commands.domain.CommandProcessingResultType; +import org.apache.fineract.commands.domain.CommandSource; +import org.apache.fineract.commands.domain.CommandSourceRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MakerCheckerReadServiceImpl implements MakerCheckerReadService { + + private final CommandSourceRepository commandSourceRepository; + + @Override + public List retrievePendingByLoanId(final Long loanId) { + return commandSourceRepository + .findPendingByLoanId(loanId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()) + .stream() + .map(this::toData) + .toList(); + } + + @Override + public List retrievePendingByClientId(final Long clientId) { + return commandSourceRepository + .findPendingByClientId(clientId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()) + .stream() + .map(this::toData) + .toList(); + } + + @Override + public List retrievePendingBySavingsId(final Long savingsId) { + return commandSourceRepository + .findPendingBySavingsId(savingsId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()) + .stream() + .map(this::toData) + .toList(); + } + + private PendingMakerCheckerData toData(final CommandSource cs) { + final String makerUsername = cs.getMaker() != null ? cs.getMaker().getUsername() : null; + return PendingMakerCheckerData.builder() + .id(cs.getId()) + .actionName(cs.getActionName()) + .entityName(cs.getEntityName()) + .permissionCode(cs.getPermissionCode()) + .makerUsername(makerUsername) + .madeOnDate(cs.getMadeOnDate()) + .build(); + } +} diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java index e0f0dc3be46..696a3d97c84 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java @@ -30,6 +30,7 @@ import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.fineract.commands.data.PendingMakerCheckerData; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.ExternalId; @@ -115,6 +116,8 @@ public final class ClientData implements Comparable, Serializable { private List datatables; + private List pendingMakerCheckerApprovals; + // import fields private transient Integer rowIndex; private String dateFormat; diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java index 6733c2325f8..908c0d32bd7 100644 --- a/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java +++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/savings/data/SavingsAccountData.java @@ -32,6 +32,7 @@ import lombok.Setter; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.fineract.commands.data.PendingMakerCheckerData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.jersey.serializer.legacy.JsonLocalDateArrayFormat; import org.apache.fineract.infrastructure.dataqueries.data.DatatableData; @@ -95,6 +96,8 @@ public final class SavingsAccountData implements Serializable { private final Integer daysToDormancy; private final Integer daysToEscheat; private final BigDecimal savingsAmountOnHold; + + private List pendingMakerCheckerApprovals; // associations private final SavingsAccountSummaryData summary; @SuppressWarnings("unused") diff --git a/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java b/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java new file mode 100644 index 00000000000..6067c7adb6d --- /dev/null +++ b/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java @@ -0,0 +1,192 @@ +/** + * 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.commands.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Collections; +import java.util.List; +import org.apache.fineract.commands.data.PendingMakerCheckerData; +import org.apache.fineract.commands.domain.CommandProcessingResultType; +import org.apache.fineract.commands.domain.CommandSource; +import org.apache.fineract.commands.domain.CommandSourceRepository; +import org.apache.fineract.useradministration.domain.AppUser; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class MakerCheckerReadServiceImplTest { + + @Mock + private CommandSourceRepository commandSourceRepository; + + @InjectMocks + private MakerCheckerReadServiceImpl service; + + private static final Integer AWAITING_STATUS = CommandProcessingResultType.AWAITING_APPROVAL.getValue(); + private static final OffsetDateTime NOW = OffsetDateTime.now(ZoneOffset.UTC); + + private CommandSource buildCommandSource(String action, String entity, String username) { + final AppUser maker = mock(AppUser.class); + when(maker.getUsername()).thenReturn(username); + + return CommandSource.builder() + .actionName(action) + .entityName(entity) + .maker(maker) + .madeOnDate(NOW) + .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) + .sanitized(false) + .build(); + } + + @Test + void retrievePendingByLoanId_withPendingCommand_returnsMappedData() { + final Long loanId = 101L; + final CommandSource cs = buildCommandSource("APPROVE", "LOAN", "maker01"); + when(commandSourceRepository.findPendingByLoanId(loanId, AWAITING_STATUS)).thenReturn(List.of(cs)); + + final List result = service.retrievePendingByLoanId(loanId); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getActionName()).isEqualTo("APPROVE"); + assertThat(result.get(0).getEntityName()).isEqualTo("LOAN"); + assertThat(result.get(0).getPermissionCode()).isEqualTo("APPROVE_LOAN"); // computed: action + "_" + entity + assertThat(result.get(0).getMakerUsername()).isEqualTo("maker01"); + assertThat(result.get(0).getMadeOnDate()).isEqualTo(NOW); + verify(commandSourceRepository).findPendingByLoanId(loanId, AWAITING_STATUS); + } + + @Test + void retrievePendingByLoanId_withNoPendingCommands_returnsEmptyList() { + final Long loanId = 102L; + when(commandSourceRepository.findPendingByLoanId(loanId, AWAITING_STATUS)).thenReturn(Collections.emptyList()); + + final List result = service.retrievePendingByLoanId(loanId); + + assertThat(result).isEmpty(); + } + + @Test + void retrievePendingByLoanId_withMultiplePendingCommands_returnsAllMapped() { + final Long loanId = 103L; + final CommandSource cs1 = buildCommandSource("APPROVE", "LOAN", "maker01"); + final CommandSource cs2 = buildCommandSource("DISBURSE", "LOAN", "maker02"); + when(commandSourceRepository.findPendingByLoanId(loanId, AWAITING_STATUS)).thenReturn(List.of(cs1, cs2)); + + final List result = service.retrievePendingByLoanId(loanId); + + assertThat(result).hasSize(2); + assertThat(result.get(0).getActionName()).isEqualTo("APPROVE"); + assertThat(result.get(1).getActionName()).isEqualTo("DISBURSE"); + } + + @Test + void retrievePendingByLoanId_withNullMaker_returnsMakerUsernameNull() { + final Long loanId = 104L; + final CommandSource cs = CommandSource.builder() + .actionName("APPROVE") + .entityName("LOAN") + .maker(null) + .madeOnDate(NOW) + .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) + .sanitized(false) + .build(); + when(commandSourceRepository.findPendingByLoanId(loanId, AWAITING_STATUS)).thenReturn(List.of(cs)); + + final List result = service.retrievePendingByLoanId(loanId); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getMakerUsername()).isNull(); + } + + @Test + void retrievePendingByClientId_withPendingCommand_returnsMappedData() { + final Long clientId = 201L; + final CommandSource cs = buildCommandSource("ACTIVATE", "CLIENT", "maker03"); + when(commandSourceRepository.findPendingByClientId(clientId, AWAITING_STATUS)).thenReturn(List.of(cs)); + + final List result = service.retrievePendingByClientId(clientId); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getActionName()).isEqualTo("ACTIVATE"); + assertThat(result.get(0).getEntityName()).isEqualTo("CLIENT"); + assertThat(result.get(0).getPermissionCode()).isEqualTo("ACTIVATE_CLIENT"); + assertThat(result.get(0).getMakerUsername()).isEqualTo("maker03"); + verify(commandSourceRepository).findPendingByClientId(clientId, AWAITING_STATUS); + } + + @Test + void retrievePendingByClientId_withNoPendingCommands_returnsEmptyList() { + final Long clientId = 202L; + when(commandSourceRepository.findPendingByClientId(clientId, AWAITING_STATUS)).thenReturn(Collections.emptyList()); + + final List result = service.retrievePendingByClientId(clientId); + + assertThat(result).isEmpty(); + } + + @Test + void retrievePendingBySavingsId_withPendingCommand_returnsMappedData() { + final Long savingsId = 301L; + final CommandSource cs = buildCommandSource("APPROVE", "SAVINGSACCOUNT", "maker04"); + when(commandSourceRepository.findPendingBySavingsId(savingsId, AWAITING_STATUS)).thenReturn(List.of(cs)); + + final List result = service.retrievePendingBySavingsId(savingsId); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getActionName()).isEqualTo("APPROVE"); + assertThat(result.get(0).getEntityName()).isEqualTo("SAVINGSACCOUNT"); + assertThat(result.get(0).getPermissionCode()).isEqualTo("APPROVE_SAVINGSACCOUNT"); + assertThat(result.get(0).getMakerUsername()).isEqualTo("maker04"); + verify(commandSourceRepository).findPendingBySavingsId(savingsId, AWAITING_STATUS); + } + + @Test + void retrievePendingBySavingsId_withNoPendingCommands_returnsEmptyList() { + final Long savingsId = 302L; + when(commandSourceRepository.findPendingBySavingsId(savingsId, AWAITING_STATUS)).thenReturn(Collections.emptyList()); + + final List result = service.retrievePendingBySavingsId(savingsId); + + assertThat(result).isEmpty(); + } + + @Test + void retrievePendingBySavingsId_coversFixedDeposit_returnsMappedData() { + final Long fdId = 401L; + final CommandSource cs = buildCommandSource("ACTIVATE", "FIXEDDEPOSITACCOUNT", "maker05"); + when(commandSourceRepository.findPendingBySavingsId(fdId, AWAITING_STATUS)).thenReturn(List.of(cs)); + + final List result = service.retrievePendingBySavingsId(fdId); + + assertThat(result).hasSize(1); + assertThat(result.get(0).getEntityName()).isEqualTo("FIXEDDEPOSITACCOUNT"); + assertThat(result.get(0).getPermissionCode()).isEqualTo("ACTIVATE_FIXEDDEPOSITACCOUNT"); + assertThat(result.get(0).getMakerUsername()).isEqualTo("maker05"); + } +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java index 25a2e916d55..aa294adb444 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java @@ -31,6 +31,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import org.apache.fineract.commands.data.PendingMakerCheckerData; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.StringEnumOptionData; @@ -295,6 +296,8 @@ public class LoanAccountData { private StringEnumOptionData buyDownFeeIncomeType; private Boolean merchantBuyDownFee; + private List pendingMakerCheckerApprovals; + public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId, Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments, Integer repaymentEvery, EnumOptionData repaidEveryFrequencyEnums, Integer loanTermFrequency, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java index f8849cfaf32..cd8ac561be7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java @@ -80,6 +80,7 @@ public interface ClientMapper { @Mapping(target = "legalFormId", ignore = true) @Mapping(target = "clientCollateralManagements", ignore = true) @Mapping(target = "groups", ignore = true) + @Mapping(target = "pendingMakerCheckerApprovals", ignore = true) ClientData map(Client source); @Named("clientTypeCode") 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..9251028388e 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 @@ -30,6 +30,8 @@ import java.util.Set; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.commands.data.PendingMakerCheckerData; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; import org.apache.fineract.infrastructure.core.data.ApiParameterError; @@ -87,6 +89,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private final ClientRepositoryWrapper clientRepositoryWrapper; private final ClientMapper clientMapper; private final InputValidator inputValidator; + private final MakerCheckerReadService makerCheckerReadService; @Override public Page retrieveAll(final SearchParameters searchParameters) { @@ -243,7 +246,12 @@ public ClientData retrieveOne(final Long clientId) { final Collection parentGroups = this.jdbcTemplate.query(clientGroupsSql, this.clientGroupsMapper, // NOSONAR clientId); - return ClientData.setParentGroups(clientData, parentGroups, clientCollateralManagementDataSet); + final ClientData result = ClientData.setParentGroups(clientData, parentGroups, clientCollateralManagementDataSet); + + final List pending = makerCheckerReadService.retrievePendingByClientId(clientId); + result.setPendingMakerCheckerApprovals(pending.isEmpty() ? null : pending); + + return result; } catch (final EmptyResultDataAccessException e) { throw new ClientNotFoundException(clientId, e); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 5a4bda1f74f..dec4b53a5b8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -41,6 +41,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.commands.data.PendingMakerCheckerData; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; @@ -200,6 +202,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa private final InterestRefundServiceDelegate interestRefundServiceDelegate; private final LoanMaximumAmountCalculator loanMaximumAmountCalculator; private final LoanRepaymentScheduleService loanRepaymentScheduleService; + private final MakerCheckerReadService makerCheckerReadService; @Override public LoanAccountData retrieveOne(final Long loanId) { @@ -217,7 +220,15 @@ public LoanAccountData retrieveOne(final Long loanId) { sqlBuilder.append(" left join m_office transferToOffice on transferToOffice.id = c.transfer_to_office_id "); sqlBuilder.append(" where l.id=? and ( o.hierarchy like ? or transferToOffice.hierarchy like ?)"); - return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), rm, loanId, hierarchySearchString, hierarchySearchString); + final LoanAccountData loanAccountData = this.jdbcTemplate.queryForObject(sqlBuilder.toString(), rm, loanId, + hierarchySearchString, hierarchySearchString); + + if (loanAccountData != null) { + final List pending = makerCheckerReadService.retrievePendingByLoanId(loanId); + loanAccountData.setPendingMakerCheckerApprovals(pending.isEmpty() ? null : pending); + } + + return loanAccountData; } catch (final EmptyResultDataAccessException e) { throw new LoanNotFoundException(loanId, e); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java index 292daf50bf7..d5c6cca0bff 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java @@ -19,6 +19,7 @@ package org.apache.fineract.portfolio.loanaccount.starter; import org.apache.fineract.cob.service.LoanAccountLockService; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper; import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository; import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper; @@ -353,7 +354,7 @@ public LoanReadPlatformServiceImpl loanReadPlatformService(JdbcTemplate jdbcTemp LoanBalanceService loanBalanceService, LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository, LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository, @Lazy InterestRefundServiceDelegate interestRefundServiceDelegate, LoanMaximumAmountCalculator loanMaximumAmountCalculator, - LoanRepaymentScheduleService loanRepaymentScheduleService) { + LoanRepaymentScheduleService loanRepaymentScheduleService, MakerCheckerReadService makerCheckerReadService) { return new LoanReadPlatformServiceImpl(jdbcTemplate, context, loanRepositoryWrapper, applicationCurrencyRepository, loanProductReadPlatformService, clientReadPlatformService, groupReadPlatformService, loanDropdownReadPlatformService, fundReadPlatformService, chargeReadPlatformService, codeValueReadPlatformService, calendarReadPlatformService, @@ -362,7 +363,7 @@ public LoanReadPlatformServiceImpl loanReadPlatformService(JdbcTemplate jdbcTemp delinquencyReadPlatformService, loanTransactionRepository, loanChargePaidByReadService, loanTransactionRelationReadService, loanForeclosureValidator, loanTransactionMapper, loanTransactionProcessingService, loanBalanceService, loanCapitalizedIncomeBalanceRepository, loanBuyDownFeeBalanceRepository, interestRefundServiceDelegate, - loanMaximumAmountCalculator, loanRepaymentScheduleService); + loanMaximumAmountCalculator, loanRepaymentScheduleService, makerCheckerReadService); } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java index 5f9aceb03b4..5ba7b20341b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountReadPlatformServiceImpl.java @@ -29,9 +29,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import lombok.RequiredArgsConstructor; +import org.apache.fineract.commands.data.PendingMakerCheckerData; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.PaginationParameters; import org.apache.fineract.infrastructure.core.data.PaginationParametersDataValidator; @@ -128,6 +131,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead private final DropdownReadPlatformService dropdownReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService; private final PaymentTypeReadService paymentTypeReadPlatformService; + private final MakerCheckerReadService makerCheckerReadService; @Override public Collection retrieveAll(final DepositAccountType depositAccountType, @@ -213,7 +217,15 @@ public DepositAccountData retrieveOne(final DepositAccountType depositAccountTyp sqlBuilder.append(depositAccountMapper.schema()); sqlBuilder.append(" where sa.id = ? and sa.deposit_type_enum = ? "); - return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), depositAccountMapper, accountId, depositAccountType.getValue()); + final DepositAccountData account = this.jdbcTemplate.queryForObject(sqlBuilder.toString(), depositAccountMapper, accountId, + depositAccountType.getValue()); + + if (account != null) { + final List pending = makerCheckerReadService.retrievePendingBySavingsId(accountId); + account.setPendingMakerCheckerApprovals(pending.isEmpty() ? null : pending); + } + + return account; } catch (final EmptyResultDataAccessException e) { throw new DepositAccountNotFoundException(depositAccountType, accountId, e); 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..fd1160ad67e 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 @@ -34,6 +34,8 @@ import org.apache.commons.lang3.StringUtils; import org.apache.fineract.accounting.common.AccountingRuleType; import org.apache.fineract.accounting.glaccount.data.GLAccountData; +import org.apache.fineract.commands.data.PendingMakerCheckerData; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; @@ -108,16 +110,18 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper; private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final MakerCheckerReadService makerCheckerReadService; public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate, final SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper, ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper, - SavingsAccountTransactionRepository savingsAccountTransactionRepository) { + SavingsAccountTransactionRepository savingsAccountTransactionRepository, MakerCheckerReadService makerCheckerReadService) { this.context = context; this.jdbcTemplate = jdbcTemplate; this.sqlGenerator = sqlGenerator; this.savingsAccountRepositoryWrapper = savingsAccountRepositoryWrapper; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; + this.makerCheckerReadService = makerCheckerReadService; this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper(); this.transactionsMapper = new SavingsAccountTransactionsMapper(); this.savingsAccountTransactionsForBatchMapper = new SavingsAccountTransactionsForBatchMapper(); @@ -221,7 +225,14 @@ public SavingsAccountData retrieveOne(final Long accountId) { try { final String sql = "select " + this.savingAccountMapper.schema() + " where sa.id = ?"; - return this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper, new Object[] { accountId }); // NOSONAR + final SavingsAccountData data = this.jdbcTemplate.queryForObject(sql, this.savingAccountMapper, new Object[] { accountId }); // NOSONAR + + if (data != null) { + final List pending = makerCheckerReadService.retrievePendingBySavingsId(accountId); + data.setPendingMakerCheckerApprovals(pending.isEmpty() ? null : pending); + } + + return data; } catch (final EmptyResultDataAccessException e) { throw new SavingsAccountNotFoundException(accountId, e); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java index f4960a5a9bb..ccc8448cff9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/starter/SavingsConfiguration.java @@ -21,6 +21,7 @@ import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService; import org.apache.fineract.commands.service.CommandProcessingService; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; @@ -213,12 +214,13 @@ public DepositAccountReadPlatformService depositAccountReadPlatformService(Platf SavingsDropdownReadPlatformService savingsDropdownReadPlatformService, ChargeReadPlatformService chargeReadPlatformService, StaffReadService staffReadPlatformService, DepositsDropdownReadPlatformService depositsDropdownReadPlatformService, SavingsAccountReadPlatformService savingsAccountReadPlatformService, DropdownReadPlatformService dropdownReadPlatformService, - CalendarReadPlatformService calendarReadPlatformService, PaymentTypeReadService paymentTypeReadPlatformService) { + CalendarReadPlatformService calendarReadPlatformService, PaymentTypeReadService paymentTypeReadPlatformService, + MakerCheckerReadService makerCheckerReadService) { return new DepositAccountReadPlatformServiceImpl(context, jdbcTemplate, chartReadPlatformService, productChartReadPlatformService, paginationParametersDataValidator, sqlGenerator, paginationHelper, clientReadPlatformService, groupReadPlatformService, depositProductReadPlatformService, savingsDropdownReadPlatformService, chargeReadPlatformService, staffReadPlatformService, depositsDropdownReadPlatformService, savingsAccountReadPlatformService, dropdownReadPlatformService, - calendarReadPlatformService, paymentTypeReadPlatformService); + calendarReadPlatformService, paymentTypeReadPlatformService, makerCheckerReadService); } @Bean @@ -361,9 +363,9 @@ public SavingsAccountActivationService savingsAccountActivationService( public SavingsAccountReadPlatformService savingsAccountReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate, SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper, ColumnValidator columnValidator, - SavingsAccountTransactionRepository savingsAccountTransactionRepository) { + SavingsAccountTransactionRepository savingsAccountTransactionRepository, MakerCheckerReadService makerCheckerReadService) { return new SavingsAccountReadPlatformServiceImpl(context, jdbcTemplate, savingAccountAssembler, paginationHelper, columnValidator, - sqlGenerator, savingsAccountRepositoryWrapper, savingsAccountTransactionRepository); + sqlGenerator, savingsAccountRepositoryWrapper, savingsAccountTransactionRepository, makerCheckerReadService); } @Bean 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..3f4634bfcec 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 @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Collections; +import org.apache.fineract.commands.service.MakerCheckerReadService; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; @@ -76,6 +77,8 @@ class ClientReadPlatformServiceImplTest { private ClientRepositoryWrapper clientRepositoryWrapper; @Mock private ClientMapper clientMapper; + @Mock + private MakerCheckerReadService makerCheckerReadService; @InjectMocks private ClientReadPlatformServiceImpl clientReadPlatformService; @@ -98,6 +101,7 @@ void testRetrieveOne_Success() { // Mock the groups query to return an empty list when(jdbcTemplate.query(anyString(), any(RowMapper.class), anyLong())).thenReturn(Collections.emptyList()); + when(makerCheckerReadService.retrievePendingByClientId(clientId)).thenReturn(Collections.emptyList()); // Act ClientData result = clientReadPlatformService.retrieveOne(clientId); diff --git a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountData.java b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountData.java index 540aabf2dd3..48f983df499 100644 --- a/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountData.java +++ b/fineract-savings/src/main/java/org/apache/fineract/portfolio/savings/data/DepositAccountData.java @@ -21,9 +21,11 @@ import java.math.BigDecimal; import java.util.Collection; import java.util.HashSet; +import java.util.List; import lombok.Getter; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.fineract.commands.data.PendingMakerCheckerData; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.staff.data.StaffData; @@ -64,6 +66,8 @@ public class DepositAccountData { protected final boolean withHoldTax; protected final TaxGroupData taxGroup; + protected List pendingMakerCheckerApprovals; + // associations protected final SavingsAccountSummaryData summary; protected final Collection transactions; @@ -335,4 +339,12 @@ public int hashCode() { public Collection charges() { return (this.charges == null) ? new HashSet() : this.charges; } + + public List getPendingMakerCheckerApprovals() { + return pendingMakerCheckerApprovals; + } + + public void setPendingMakerCheckerApprovals(final List pendingMakerCheckerApprovals) { + this.pendingMakerCheckerApprovals = pendingMakerCheckerApprovals; + } } From 164bfbbdbafdc83619030c73ce4a0ba5503e5a4b Mon Sep 17 00:00:00 2001 From: "Rym.Ghosn" Date: Thu, 20 Aug 2026 09:40:55 +0300 Subject: [PATCH 2/2] FINERACT-2763: Fix spotless formatting in MakerCheckerReadServiceImpl CI's spotlessJavaCheck flagged wrapping/line-length violations in the new MakerCheckerReadServiceImpl and its test; ran spotlessApply to bring them in line with the project's palantir-java-format config. --- .../service/MakerCheckerReadServiceImpl.java | 31 +++++-------------- .../MakerCheckerReadServiceImplTest.java | 20 +++--------- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java index b1760c8df60..c1db985d94c 100644 --- a/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java +++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImpl.java @@ -36,40 +36,25 @@ public class MakerCheckerReadServiceImpl implements MakerCheckerReadService { @Override public List retrievePendingByLoanId(final Long loanId) { - return commandSourceRepository - .findPendingByLoanId(loanId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()) - .stream() - .map(this::toData) - .toList(); + return commandSourceRepository.findPendingByLoanId(loanId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()).stream() + .map(this::toData).toList(); } @Override public List retrievePendingByClientId(final Long clientId) { - return commandSourceRepository - .findPendingByClientId(clientId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()) - .stream() - .map(this::toData) - .toList(); + return commandSourceRepository.findPendingByClientId(clientId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()).stream() + .map(this::toData).toList(); } @Override public List retrievePendingBySavingsId(final Long savingsId) { - return commandSourceRepository - .findPendingBySavingsId(savingsId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()) - .stream() - .map(this::toData) - .toList(); + return commandSourceRepository.findPendingBySavingsId(savingsId, CommandProcessingResultType.AWAITING_APPROVAL.getValue()).stream() + .map(this::toData).toList(); } private PendingMakerCheckerData toData(final CommandSource cs) { final String makerUsername = cs.getMaker() != null ? cs.getMaker().getUsername() : null; - return PendingMakerCheckerData.builder() - .id(cs.getId()) - .actionName(cs.getActionName()) - .entityName(cs.getEntityName()) - .permissionCode(cs.getPermissionCode()) - .makerUsername(makerUsername) - .madeOnDate(cs.getMadeOnDate()) - .build(); + return PendingMakerCheckerData.builder().id(cs.getId()).actionName(cs.getActionName()).entityName(cs.getEntityName()) + .permissionCode(cs.getPermissionCode()).makerUsername(makerUsername).madeOnDate(cs.getMadeOnDate()).build(); } } diff --git a/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java b/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java index 6067c7adb6d..f80a157c3ef 100644 --- a/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java +++ b/fineract-core/src/test/java/org/apache/fineract/commands/service/MakerCheckerReadServiceImplTest.java @@ -54,14 +54,8 @@ private CommandSource buildCommandSource(String action, String entity, String us final AppUser maker = mock(AppUser.class); when(maker.getUsername()).thenReturn(username); - return CommandSource.builder() - .actionName(action) - .entityName(entity) - .maker(maker) - .madeOnDate(NOW) - .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) - .sanitized(false) - .build(); + return CommandSource.builder().actionName(action).entityName(entity).maker(maker).madeOnDate(NOW) + .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()).sanitized(false).build(); } @Test @@ -108,14 +102,8 @@ void retrievePendingByLoanId_withMultiplePendingCommands_returnsAllMapped() { @Test void retrievePendingByLoanId_withNullMaker_returnsMakerUsernameNull() { final Long loanId = 104L; - final CommandSource cs = CommandSource.builder() - .actionName("APPROVE") - .entityName("LOAN") - .maker(null) - .madeOnDate(NOW) - .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()) - .sanitized(false) - .build(); + final CommandSource cs = CommandSource.builder().actionName("APPROVE").entityName("LOAN").maker(null).madeOnDate(NOW) + .status(CommandProcessingResultType.AWAITING_APPROVAL.getValue()).sanitized(false).build(); when(commandSourceRepository.findPendingByLoanId(loanId, AWAITING_STATUS)).thenReturn(List.of(cs)); final List result = service.retrievePendingByLoanId(loanId);