From a25acf0abdd47c8e134710cbee258cd666784ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Alberto=20Hern=C3=A1ndez?= <44206706+josehernandezfintecheandomx@users.noreply.github.com> Date: Fri, 6 Jan 2023 14:17:35 -0600 Subject: [PATCH] Fix to use notes in Savings Accounts (#2857) Co-authored-by: Jose Alberto Hernandez --- .../portfolio/note/api/NotesApiResource.java | 4 +- .../portfolio/note/domain/NoteRepository.java | 2 +- .../service/NoteReadPlatformServiceImpl.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 139 ++++++++---------- .../note/starter/NoteAutoConfiguration.java | 5 +- .../fineract/integrationtests/NotesTest.java | 35 +++++ .../integrationtests/common/NotesHelper.java | 22 +++ 7 files changed, 129 insertions(+), 80 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/api/NotesApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/api/NotesApiResource.java index c66c8ee1e14..10d7aa8fc7d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/api/NotesApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/api/NotesApiResource.java @@ -71,8 +71,8 @@ public class NotesApiResource { public static final String GROUPNOTE = "GROUPNOTE"; public static final String INVALIDNOTE = "INVALIDNOTE"; private static final Set NOTE_DATA_PARAMETERS = new HashSet<>( - Arrays.asList("id", "clientId", "groupId", "loanId", "loanTransactionId", "depositAccountId", "savingAccountId", "noteType", - "note", "createdById", "createdByUsername", "createdOn", "updatedById", "updatedByUsername", "updatedOn")); + Arrays.asList("id", "resourceId", "clientId", "groupId", "loanId", "loanTransactionId", "depositAccountId", "savingAccountId", + "noteType", "note", "createdById", "createdByUsername", "createdOn", "updatedById", "updatedByUsername", "updatedOn")); private final PlatformSecurityContext context; private final NoteReadPlatformService readPlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/domain/NoteRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/domain/NoteRepository.java index 94f89770e91..f609eeae0e7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/domain/NoteRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/domain/NoteRepository.java @@ -45,5 +45,5 @@ public interface NoteRepository extends JpaRepository, JpaSpecificat List findBySavingsAccount(SavingsAccount savingAccount); - // Note findBySavingsAccountIdAndId(Long savingAccountId, Long id); + Note findBySavingsAccountAndId(SavingsAccount savingAccount, Long id); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteReadPlatformServiceImpl.java index 2722cb5cc1c..29bc1422675 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteReadPlatformServiceImpl.java @@ -132,7 +132,7 @@ public static String getResourceCondition(final NoteType noteType, List case SAVING_ACCOUNT: paramList.add(NoteType.SAVING_ACCOUNT.getValue()); paramList.add(NoteType.SAVINGS_TRANSACTION.getValue()); - conditionSql = " n.saving_account_id = ? and ( n.note_type_enum = ? or n.note_type_enum = ? ) "; + conditionSql = " n.savings_account_id = ? and ( n.note_type_enum = ? or n.note_type_enum = ? ) "; break; case SAVINGS_TRANSACTION: conditionSql = " n.savings_account_transaction_id = ? "; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceJpaRepositoryImpl.java index 9803356114e..c3513d60ce8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/service/NoteWritePlatformServiceJpaRepositoryImpl.java @@ -19,6 +19,7 @@ package org.apache.fineract.portfolio.note.service; import java.util.Map; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; @@ -40,28 +41,32 @@ import org.apache.fineract.portfolio.note.exception.NoteNotFoundException; import org.apache.fineract.portfolio.note.exception.NoteResourceNotSupportedException; import org.apache.fineract.portfolio.note.serialization.NoteCommandFromApiJsonDeserializer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; +import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException; +@Slf4j public class NoteWritePlatformServiceJpaRepositoryImpl implements NoteWritePlatformService { - private static final Logger LOG = LoggerFactory.getLogger(NoteWritePlatformServiceJpaRepositoryImpl.class); private final NoteRepository noteRepository; private final ClientRepositoryWrapper clientRepository; private final GroupRepository groupRepository; private final LoanRepositoryWrapper loanRepository; private final LoanTransactionRepository loanTransactionRepository; private final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer; + private final SavingsAccountRepository savingsAccountRepository; public NoteWritePlatformServiceJpaRepositoryImpl(final NoteRepository noteRepository, final ClientRepositoryWrapper clientRepository, final GroupRepository groupRepository, final LoanRepositoryWrapper loanRepository, - final LoanTransactionRepository loanTransactionRepository, final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer) { + final LoanTransactionRepository loanTransactionRepository, final NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer, + final SavingsAccountRepository savingsAccountRepository) { this.noteRepository = noteRepository; this.clientRepository = clientRepository; this.groupRepository = groupRepository; this.loanRepository = loanRepository; this.loanTransactionRepository = loanTransactionRepository; this.fromApiJsonDeserializer = fromApiJsonDeserializer; + this.savingsAccountRepository = savingsAccountRepository; } private CommandProcessingResult createClientNote(final JsonCommand command) { @@ -151,26 +156,23 @@ private CommandProcessingResult createLoanTransactionNote(final JsonCommand comm .build(); } - // private CommandProcessingResult createSavingAccountNote(final JsonCommand - // command) { - // - // final Long resourceId = command.getSupportedEntityId(); - // - // final SavingAccount savingAccount = - // this.savingAccountRepository.findOne(resourceId); - // if (savingAccount == null) { throw new - // SavingAccountNotFoundException(resourceId); } - // - // final String note = command.stringValueOfParameterNamed("note"); - // final Note newNote = Note.savingNote(savingAccount, note); - // - // this.noteRepository.save(newNote); - // - // return new CommandProcessingResultBuilder() // - // .withCommandId(command.commandId()) // - // .withEntityId(newNote.getId()) // - // .withClientId(savingAccount.getClient().getId()).withOfficeId(savingAccount.getClient().getOffice().getId()).build(); - // } + private CommandProcessingResult createSavingAccountNote(final JsonCommand command) { + final Long resourceId = command.getSavingsId(); + final SavingsAccount savingAccount = this.savingsAccountRepository.findById(resourceId) + .orElseThrow(() -> new SavingsAccountNotFoundException(resourceId)); + + final String note = command.stringValueOfParameterNamed("note"); + final Note newNote = Note.savingNote(savingAccount, note); + + this.noteRepository.saveAndFlush(newNote); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(newNote.getId()) // + .withOfficeId(savingAccount.getClient().getOffice().getId()) // + .withSavingsId(savingAccount.getId()) // + .build(); + } @Override public CommandProcessingResult createNote(final JsonCommand command) { @@ -192,9 +194,9 @@ public CommandProcessingResult createNote(final JsonCommand command) { case LOAN_TRANSACTION: { return createLoanTransactionNote(command); } - // case SAVING_ACCOUNT: { - // return createSavingAccountNote(command); - // } + case SAVING_ACCOUNT: { + return createSavingAccountNote(command); + } default: throw new NoteResourceNotSupportedException(resourceUrl); } @@ -333,41 +335,30 @@ private CommandProcessingResult updateLoanTransactionNote(final JsonCommand comm .withLoanId(loan.getId()).withOfficeId(loan.getOfficeId()).with(changes).build(); } - // private CommandProcessingResult updateSavingAccountNote(final JsonCommand - // command) { - // - // final Long resourceId = command.getSupportedEntityId(); - // final Long noteId = command.entityId(); - // final String resourceUrl = command.getSupportedEntityType(); - // - // final NoteType type = NoteType.fromApiUrl(resourceUrl); - // - // final SavingAccount savingAccount = - // this.savingAccountRepository.findOne(resourceId); - // if (savingAccount == null) { throw new - // SavingAccountNotFoundException(resourceId); } - // - // final Note noteForUpdate = - // this.noteRepository.findBySavingAccountIdAndId(resourceId, noteId); - // - // if (noteForUpdate == null) { throw new NoteNotFoundException(noteId, - // resourceId, type.name().toLowerCase()); } - // - // final Map changes = noteForUpdate.update(command); - // - // if (!changes.isEmpty()) { - // this.noteRepository.saveAndFlush(noteForUpdate); - // } - // - // return new CommandProcessingResultBuilder() - // // - // .withCommandId(command.commandId()) - // // - // .withEntityId(noteForUpdate.getId()) - // // - // .withClientId(savingAccount.getClient().getId()).withOfficeId(savingAccount.getClient().getOffice().getId()).with(changes) - // .build(); - // } + private CommandProcessingResult updateSavingAccountNote(final JsonCommand command) { + final Long resourceId = command.getSavingsId(); + final Long noteId = command.entityId(); + final NoteType type = NoteType.SAVING_ACCOUNT; + final SavingsAccount savingAccount = this.savingsAccountRepository.findById(resourceId) + .orElseThrow(() -> new SavingsAccountNotFoundException(resourceId)); + + final Note noteForUpdate = this.noteRepository.findBySavingsAccountAndId(savingAccount, noteId); + if (noteForUpdate == null) { + throw new NoteNotFoundException(noteId, resourceId, type.name().toLowerCase()); + } + final Map changes = noteForUpdate.update(command); + if (!changes.isEmpty()) { + this.noteRepository.saveAndFlush(noteForUpdate); + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(noteForUpdate.getId()) // + .withOfficeId(savingAccount.getClient().getOffice().getId()) // + .withSavingsId(savingAccount.getId()) // + .with(changes) // + .build(); + } @Override public CommandProcessingResult updateNote(final JsonCommand command) { @@ -390,9 +381,9 @@ public CommandProcessingResult updateNote(final JsonCommand command) { case LOAN_TRANSACTION: { return updateLoanTransactionNote(command); } - // case SAVING_ACCOUNT: { - // return updateSavingAccountNote(command); - // } + case SAVING_ACCOUNT: { + return updateSavingAccountNote(command); + } default: throw new NoteResourceNotSupportedException(resourceUrl); } @@ -444,19 +435,19 @@ private Note getNoteForDelete(final JsonCommand command) { noteForUpdate = this.noteRepository.findByLoanTransactionAndId(loanTransaction, noteId); } break; - // case SAVING_ACCOUNT: { - // noteForUpdate = - // this.noteRepository.findBySavingAccountIdAndId(resourceId, - // noteId); - // } - // break; - case SAVING_ACCOUNT: + case SAVING_ACCOUNT: { + final Long savinsAccountId = command.getSavingsId(); + final SavingsAccount savingAccount = this.savingsAccountRepository.findById(savinsAccountId) + .orElseThrow(() -> new SavingsAccountNotFoundException(savinsAccountId)); + + noteForUpdate = this.noteRepository.findBySavingsAccountAndId(savingAccount, noteId); + } break; case SHARE_ACCOUNT: - LOG.error("TODO Implement getNoteForDelete for SHARE_ACCOUNT"); + log.error("TODO Implement getNoteForDelete for SHARE_ACCOUNT"); break; case SAVINGS_TRANSACTION: - LOG.error("TODO Implement getNoteForDelete for SAVINGS_TRANSACTION"); + log.error("TODO Implement getNoteForDelete for SAVINGS_TRANSACTION"); break; } if (noteForUpdate == null) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/starter/NoteAutoConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/starter/NoteAutoConfiguration.java index 2da1c9d24b4..8fab6f156e9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/starter/NoteAutoConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/note/starter/NoteAutoConfiguration.java @@ -28,6 +28,7 @@ import org.apache.fineract.portfolio.note.service.NoteReadPlatformServiceImpl; import org.apache.fineract.portfolio.note.service.NoteWritePlatformService; import org.apache.fineract.portfolio.note.service.NoteWritePlatformServiceJpaRepositoryImpl; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,8 +47,8 @@ public NoteReadPlatformService noteReadPlatformService(JdbcTemplate jdbcTemplate @ConditionalOnMissingBean public NoteWritePlatformService noteWritePlatformService(NoteRepository noteRepository, ClientRepositoryWrapper clientRepository, GroupRepository groupRepository, LoanRepositoryWrapper loanRepository, LoanTransactionRepository loanTransactionRepository, - NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer) { + NoteCommandFromApiJsonDeserializer fromApiJsonDeserializer, SavingsAccountRepository savingsAccountRepository) { return new NoteWritePlatformServiceJpaRepositoryImpl(noteRepository, clientRepository, groupRepository, loanRepository, - loanTransactionRepository, fromApiJsonDeserializer); + loanTransactionRepository, fromApiJsonDeserializer, savingsAccountRepository); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/NotesTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/NotesTest.java index 3b4d4f1af8d..99f0fa077d7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/NotesTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/NotesTest.java @@ -28,6 +28,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import org.apache.fineract.client.models.GetResourceTypeResourceIdNotesNoteIdResponse; +import org.apache.fineract.client.models.PostResourceTypeResourceIdNotesResponse; import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.CollateralManagementHelper; import org.apache.fineract.integrationtests.common.GroupHelper; @@ -36,6 +38,8 @@ import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper; +import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -47,6 +51,8 @@ public class NotesTest { private RequestSpecification requestSpec; private ResponseSpecification responseSpec404; private LoanTransactionHelper loanTransactionHelper; + private SavingsProductHelper savingsProductHelper; + private SavingsAccountHelper savingsAccountHelper; @BeforeEach public void setup() { @@ -56,6 +62,8 @@ public void setup() { this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); this.responseSpec404 = new ResponseSpecBuilder().expectStatusCode(404).build(); this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + this.savingsProductHelper = new SavingsProductHelper(); + this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); } @Test @@ -190,6 +198,33 @@ public void testCreateLoanNote() { } + @Test + public void testCreateSavingsNote() { + final String noteText = "this is a test Savings note"; + final String testDate = "01 January 2012"; + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, testDate); + // Savings Account + final String savingsProductJSON = this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily() + .withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build(); + final Integer savingsProductId = SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec); + final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, savingsProductId, "INDIVIDUAL", + testDate); + Assertions.assertNotNull(savingsId); + + // Notes + final String payload = "{\"note\": \"" + noteText + "\"}"; + final PostResourceTypeResourceIdNotesResponse postNoteResponse = NotesHelper.createSavingsNote(requestSpec, responseSpec, savingsId, + payload); + Assertions.assertNotNull(postNoteResponse); + Assertions.assertNotNull(postNoteResponse.getResourceId()); + + final GetResourceTypeResourceIdNotesNoteIdResponse getNoteResponse = NotesHelper.getSavingsNote(requestSpec, responseSpec, + savingsId, postNoteResponse.getResourceId()); + Assertions.assertNotNull(getNoteResponse); + Assertions.assertEquals(noteText, getNoteResponse.getNote()); + } + private Integer applyForLoanApplication(final Integer clientID, final Integer loanProductID) { List collaterals = new ArrayList<>(); final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/NotesHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/NotesHelper.java index 25c0fb9d371..e333f3fa180 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/NotesHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/NotesHelper.java @@ -18,12 +18,18 @@ */ package org.apache.fineract.integrationtests.common; +import com.google.gson.Gson; import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; +import org.apache.fineract.client.models.GetResourceTypeResourceIdNotesNoteIdResponse; +import org.apache.fineract.client.models.PostResourceTypeResourceIdNotesResponse; +import org.apache.fineract.client.util.JSON; @SuppressWarnings({ "rawtypes", "unchecked" }) public final class NotesHelper { + private static final Gson GSON = new JSON().getGson(); + private NotesHelper() { } @@ -134,4 +140,20 @@ public static void deleteLoanTransactionNote(RequestSpecification requestSpec, R Utils.performServerDelete(requestSpec, responseSpec, deleteLoanTransactionNoteURL, ""); } + private static final String SAVINGS_URL = "/fineract-provider/api/v1/savings"; + + public static PostResourceTypeResourceIdNotesResponse createSavingsNote(RequestSpecification requestSpec, + ResponseSpecification responseSpec, Integer savingsId, String request) { + final String noteURL = SAVINGS_URL + "/" + savingsId + "/notes?" + Utils.TENANT_IDENTIFIER; + final String response = Utils.performServerPost(requestSpec, responseSpec, noteURL, request); + return GSON.fromJson(response, PostResourceTypeResourceIdNotesResponse.class); + } + + public static GetResourceTypeResourceIdNotesNoteIdResponse getSavingsNote(RequestSpecification requestSpec, + ResponseSpecification responseSpec, Integer savingsId, Integer noteId) { + final String noteURL = SAVINGS_URL + "/" + savingsId + "/notes/" + noteId + "?" + Utils.TENANT_IDENTIFIER; + final String response = Utils.performServerGet(requestSpec, responseSpec, noteURL); + return GSON.fromJson(response, GetResourceTypeResourceIdNotesNoteIdResponse.class); + } + }